@bongos/core 1.20.51 → 1.20.53
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 +144 -24
- package/docs/copy-inventory.md +19 -18
- package/docs/copy-registry.json +28 -19
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-readings.json +6 -6
- package/migrations/core_267_page_tweak_author_of_record.sql +41 -0
- package/modules/hall-ui/public/builders.js +3 -1
- package/modules/lifecycle/db-analytics.js +6 -1
- package/modules/lifecycle/db-ship.js +10 -3
- package/modules/lifecycle/page-tweak-artist.js +86 -0
- package/modules/lifecycle/page-tweak-reads.js +7 -5
- package/modules/lifecycle/routes/tasks.js +3 -0
- package/modules/provisioning/soft-limits.js +17 -0
- package/modules/public-landing/public/assets/world/r5-life-1-primordial-sea-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r5-life-1-primordial-sea-cut.webp.json +16 -0
- package/modules/public-landing/public/assets/world/r5-life-1-primordial-sea.jpg +0 -0
- package/modules/public-landing/public/assets/world/r5-life-1-primordial-sea.jpg.json +16 -0
- package/modules/public-landing/public/assets/world/r5-life-2-first-cells-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r5-life-2-first-cells-cut.webp.json +16 -0
- package/modules/public-landing/public/assets/world/r5-life-2-first-cells.jpg +0 -0
- package/modules/public-landing/public/assets/world/r5-life-2-first-cells.jpg.json +16 -0
- package/modules/public-landing/public/assets/world/r5-life-3-genome-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r5-life-3-genome-cut.webp.json +16 -0
- package/modules/public-landing/public/assets/world/r5-life-3-genome.jpg +0 -0
- package/modules/public-landing/public/assets/world/r5-life-3-genome.jpg.json +16 -0
- package/modules/public-landing/public/assets/world/r5-life-4-membrane-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r5-life-4-membrane-cut.webp.json +16 -0
- package/modules/public-landing/public/assets/world/r5-life-4-membrane.jpg +0 -0
- package/modules/public-landing/public/assets/world/r5-life-4-membrane.jpg.json +16 -0
- package/modules/public-landing/public/assets/world/r5-life-5-nervous-system-cut.webp +0 -0
- package/modules/public-landing/public/assets/world/r5-life-5-nervous-system-cut.webp.json +16 -0
- package/modules/public-landing/public/assets/world/r5-life-5-nervous-system.jpg +0 -0
- package/modules/public-landing/public/assets/world/r5-life-5-nervous-system.jpg.json +16 -0
- package/modules/sessions/db.js +8 -0
- package/modules/status-ui/public/status.js +3 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +16 -0
- package/src/module-api.js +1 -1
- package/tests/genesis_life_plates.mjs +63 -0
- package/tests/page_tweak_artist.mjs +236 -0
- package/tests/provisioning_planet_physics.mjs +14 -0
|
@@ -2759,5 +2759,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2759
2759
|
landed since 1.20.49 with no explicit bump. run 36815399738. (task 1002620)
|
|
2760
2760
|
1.20.51 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2761
2761
|
landed since 1.20.50 with no explicit bump. run 36816320939. (task 1002620)
|
|
2762
|
+
1.20.52 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2763
|
+
landed since 1.20.51 with no explicit bump. run 36817286385. (task 1002620)
|
|
2764
|
+
1.20.53 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2765
|
+
landed since 1.20.52 with no explicit bump. run 36818406554. (task 1002620)
|
|
2762
2766
|
---------------------------------------------------------------------------
|
|
2763
2767
|
```
|
package/docs/page-readings.json
CHANGED
|
@@ -1353,7 +1353,7 @@
|
|
|
1353
1353
|
"surface": "builders",
|
|
1354
1354
|
"title": "Builders' Hall",
|
|
1355
1355
|
"states_read": ["home","standing","standing-karma","out"],
|
|
1356
|
-
"files_hash": "
|
|
1356
|
+
"files_hash": "959a729d1d7eba2b",
|
|
1357
1357
|
"reading_hash": "1debe0f979ce3b59",
|
|
1358
1358
|
"counts": {"lines":204,"placed":68,"shared":7,"unplaced":129},
|
|
1359
1359
|
"omitted": {"data":182},
|
|
@@ -1398,9 +1398,9 @@
|
|
|
1398
1398
|
{"key":"L0037","section":"From the system","text":"From the system","placement":"placed","file":"modules/hall-ui/public/hall-render.js","line":707,"string_id":"d32c5e058d1e"},
|
|
1399
1399
|
{"key":"L0038","section":"From the system","text":"{…} and {…}","placement":"placed","file":"modules/hall-ui/public/profile-nudge.js","line":77,"string_id":"56b00d773232","occurrences":2},
|
|
1400
1400
|
{"key":"L0039","section":"From the system","text":"Open {…}","placement":"placed","file":"modules/hall-ui/public/goals.js","line":67,"string_id":"f6f68ba322be"},
|
|
1401
|
-
{"key":"L0040","section":"Ideas awaiting triage 151","text":"Ideas awaiting triage","placement":"placed","file":"modules/hall-ui/public/builders.js","line":
|
|
1401
|
+
{"key":"L0040","section":"Ideas awaiting triage 151","text":"Ideas awaiting triage","placement":"placed","file":"modules/hall-ui/public/builders.js","line":897,"string_id":"e181637c5a5e"},
|
|
1402
1402
|
{"key":"L0041","section":"Ideas awaiting triage 151","text":"Triage","placement":"unplaced","occurrences":5},
|
|
1403
|
-
{"key":"L0042","section":"Ideas awaiting triage 151","text":"…{…} more open ideas — triage clears them one at a time.","placement":"placed","file":"modules/hall-ui/public/builders.js","line":
|
|
1403
|
+
{"key":"L0042","section":"Ideas awaiting triage 151","text":"…{…} more open ideas — triage clears them one at a time.","placement":"placed","file":"modules/hall-ui/public/builders.js","line":895,"string_id":"e35f11bdc5d7"},
|
|
1404
1404
|
{"key":"L0043","section":"Was this removed? 2","text":"Was this removed?","placement":"unplaced"},
|
|
1405
1405
|
{"key":"L0044","section":"Was this removed? 2","text":"{…}{…} — open it to see what went","placement":"placed","file":"modules/hall-ui/public/goal-inbox.js","line":374,"string_id":"7f79bedecf60","occurrences":2},
|
|
1406
1406
|
{"key":"L0045","section":"Was this removed? 2","text":"Confirm","placement":"unplaced","occurrences":2},
|
|
@@ -4318,7 +4318,7 @@
|
|
|
4318
4318
|
"surface": "status",
|
|
4319
4319
|
"title": "{{worldName}} — the public ledger",
|
|
4320
4320
|
"states_read": ["default"],
|
|
4321
|
-
"files_hash": "
|
|
4321
|
+
"files_hash": "35acac6a3253f014",
|
|
4322
4322
|
"reading_hash": "2853e52b66ba076c",
|
|
4323
4323
|
"counts": {"lines":87,"placed":49,"shared":14,"unplaced":24},
|
|
4324
4324
|
"omitted": {"data":89},
|
|
@@ -4327,7 +4327,7 @@
|
|
|
4327
4327
|
{"key":"L0001","section":"top bar","text":"The Public Ledger of the City's Making","placement":"shared","file":"modules/status-ui/public/index.html","line":125,"string_id":"acdf97805931"},
|
|
4328
4328
|
{"key":"L0002","section":"top bar","text":"A shipping container has washed ashore upon the coast south of Athens. About it a new civilization rises. Set out below: the city's affairs, openly recorded.","placement":"shared","file":"modules/status-ui/public/index.html","line":126,"string_id":"f4a664041da5"},
|
|
4329
4329
|
{"key":"L0003","section":"top bar","text":"Cross into the city →","placement":"shared","file":"modules/status-ui/public/index.html","line":128,"string_id":"3aa071ef1142"},
|
|
4330
|
-
{"key":"L0004","section":"top bar","text":"tablet last consulted at {…}","placement":"shared","file":"modules/status-ui/public/status.js","line":
|
|
4330
|
+
{"key":"L0004","section":"top bar","text":"tablet last consulted at {…}","placement":"shared","file":"modules/status-ui/public/status.js","line":1169,"string_id":"c1e3c2212207"},
|
|
4331
4331
|
{"key":"L0005","section":"navigation","text":"Pulse","placement":"shared"},
|
|
4332
4332
|
{"key":"L0006","section":"navigation","text":"Watch","placement":"shared"},
|
|
4333
4333
|
{"key":"L0007","section":"navigation","text":"Architects","placement":"shared"},
|
|
@@ -4403,7 +4403,7 @@
|
|
|
4403
4403
|
{"key":"L0077","section":"Lately Inscribed","text":"inscribed by {…} · {…}","placement":"unplaced","occurrences":6},
|
|
4404
4404
|
{"key":"L0078","section":"Lately Inscribed","text":"The site now has the three pages every real website needs — privacy, terms and a contact page — linked from the footer of the front page and the projects hub. The privacy page says plainly what is kept about a GitHub account and a project, who else sees it, and how long it stays; the contact page points at the two doors that actually exist, the repository and the builders' hall. Every project built on the platform gets the same pages under its own name, with the claims that are only true of the platform itself removed.","placement":"unplaced"},
|
|
4405
4405
|
{"key":"L0079","section":"Lately Inscribed","text":"‹ earlier page","placement":"placed","file":"modules/status-ui/public/index.html","line":321,"string_id":"6d0d64f10bd9"},
|
|
4406
|
-
{"key":"L0080","section":"Lately Inscribed","text":"page {…} of {…}","placement":"placed","file":"modules/status-ui/public/status.js","line":
|
|
4406
|
+
{"key":"L0080","section":"Lately Inscribed","text":"page {…} of {…}","placement":"placed","file":"modules/status-ui/public/status.js","line":1012,"string_id":"4a00bdbfd9be"},
|
|
4407
4407
|
{"key":"L0081","section":"Lately Inscribed","text":"later page ›","placement":"placed","file":"modules/status-ui/public/index.html","line":323,"string_id":"9b019d632d3e"},
|
|
4408
4408
|
{"key":"L0082","section":"The World","text":"The World","placement":"placed","file":"modules/status-ui/public/index.html","line":331,"string_id":"3e465b62cea8"},
|
|
4409
4409
|
{"key":"L0083","section":"The World","text":"A true-to-life portrait of {{worldName}} as she stands today, rendered from the city's own stones.","placement":"placed","file":"modules/status-ui/public/index.html","line":332,"string_id":"623d6b7ae6b7"},
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
-- core_267 — an approved page tweak already shipped names its artist (task 1004432,
|
|
2
|
+
-- WA6.RP02, goal 1000095)
|
|
3
|
+
--
|
|
4
|
+
-- From this release, shipTask (modules/lifecycle/db-ship.js) writes tasks.shipped_by
|
|
5
|
+
-- as the round's ARTIST when a page tweak ships, instead of the session that applied
|
|
6
|
+
-- it. Everything that counts or lists shipped work reads shipped_by (the profile,
|
|
7
|
+
-- the shipped count, the activity log, the status page's recent list, streaks,
|
|
8
|
+
-- achievements, the hub rollup), so that one write moves every one of them.
|
|
9
|
+
--
|
|
10
|
+
-- The rounds that shipped BEFORE this release still name their applier, so on every
|
|
11
|
+
-- one of those surfaces the artist who wrote the page is absent and the applier is
|
|
12
|
+
-- credited with work that was not theirs. This brings them in line, once.
|
|
13
|
+
--
|
|
14
|
+
-- The artist is page-tweak-artist.js's rule, spelled out here because a migration
|
|
15
|
+
-- cannot call it: the builder of the round's LATEST WEB claim (worktree_name and
|
|
16
|
+
-- creator_session_id both NULL), the same person economy's awardArtistPageCredit
|
|
17
|
+
-- already paid. A round with no web claim keeps its applier, exactly as the ship
|
|
18
|
+
-- path does. tests/page_tweak_artist.mjs holds this predicate to the helper's.
|
|
19
|
+
--
|
|
20
|
+
-- Data only, no column or constraint: forward-safe against a live database, and an
|
|
21
|
+
-- older release reading the rows just sees a different builder id. Idempotent: a
|
|
22
|
+
-- second run matches nothing, because the IS DISTINCT FROM guard skips rows that
|
|
23
|
+
-- already name their artist. Money is untouched: credit_log is not read or written,
|
|
24
|
+
-- and the artist was paid at the land.
|
|
25
|
+
|
|
26
|
+
UPDATE tasks t
|
|
27
|
+
SET shipped_by = w.builder_id
|
|
28
|
+
FROM (
|
|
29
|
+
SELECT DISTINCT ON (c.task_id) c.task_id, c.builder_id
|
|
30
|
+
FROM claims c
|
|
31
|
+
JOIN tasks pt ON pt.id = c.task_id
|
|
32
|
+
WHERE pt.source = 'page-tweak'
|
|
33
|
+
AND pt.status = 'shipped'
|
|
34
|
+
AND c.worktree_name IS NULL AND c.creator_session_id IS NULL
|
|
35
|
+
ORDER BY c.task_id, c.claimed_at DESC, c.id DESC
|
|
36
|
+
) w
|
|
37
|
+
WHERE t.id = w.task_id
|
|
38
|
+
AND t.shipped_by IS DISTINCT FROM w.builder_id;
|
|
39
|
+
|
|
40
|
+
INSERT INTO schema_migrations (version) VALUES ('core_267_page_tweak_author_of_record')
|
|
41
|
+
ON CONFLICT DO NOTHING;
|
|
@@ -762,7 +762,9 @@
|
|
|
762
762
|
if (!tbody) return;
|
|
763
763
|
const slice = window.OTBKit.paginate(acclaimState.tasks, acclaimPager);
|
|
764
764
|
tbody.innerHTML = slice.map((t) => {
|
|
765
|
-
|
|
765
|
+
// task 1004432: an approved page is its artist's; the applying session rides along as a helper.
|
|
766
|
+
const helperName = t.helper_display_name || (t.helper_login ? `@${t.helper_login}` : '');
|
|
767
|
+
const by = escapeHtml(t.display_name || (t.github_login ? `@${t.github_login}` : `builder #${t.shipped_by}`)) + (helperName ? ` <span class="acclaim__evidence">with help from ${escapeHtml(helperName)}</span>` : '');
|
|
766
768
|
const net = Number(t.net_votes) || 0;
|
|
767
769
|
const netCls = signClasses(net, 'acclaim__net');
|
|
768
770
|
const netText = net > 0 ? `+${net}` : `${net}`;
|
|
@@ -14,6 +14,7 @@ const api = require('../../src/module-api');
|
|
|
14
14
|
const { pool, withTx } = api;
|
|
15
15
|
const { SMOKE_NOT_LIKE, gradePassCount, gradeQualityWhere, median } = require('./db-shared.js');
|
|
16
16
|
const { CREDITS_AWARDED_SUBQUERY } = require('./db-tasks.js');
|
|
17
|
+
const { helperJoinSql } = require('./page-tweak-artist.js');
|
|
17
18
|
|
|
18
19
|
// -------------------------------------------------------------------------
|
|
19
20
|
// Cost log + summary carved into modules/economy/cost.js (BV1.R77 / ADR 0093
|
|
@@ -47,9 +48,13 @@ async function recentShippedTasks(limit = 30, { days = null } = {}) {
|
|
|
47
48
|
CASE WHEN t.security_sensitive THEN NULL ELSE t.visual_url END AS visual_url, CASE WHEN t.security_sensitive THEN NULL ELSE t.visual_alt END AS visual_alt,
|
|
48
49
|
${CREDITS_AWARDED_SUBQUERY} AS credits_awarded,
|
|
49
50
|
t.est_minutes, t.actual_minutes,
|
|
50
|
-
b.github_login, b.display_name
|
|
51
|
+
b.github_login, b.display_name,
|
|
52
|
+
-- task 1004432: an approved page is its artist's (shipped_by); the session
|
|
53
|
+
-- that applied it rides along as a helper, so a reader still sees both.
|
|
54
|
+
helper.helper_login, helper.helper_display_name
|
|
51
55
|
FROM tasks t
|
|
52
56
|
LEFT JOIN builders b ON b.id = t.shipped_by
|
|
57
|
+
${helperJoinSql('t')}
|
|
53
58
|
WHERE ${whereClause}
|
|
54
59
|
ORDER BY t.shipped_at DESC NULLS LAST
|
|
55
60
|
LIMIT $1`,
|
|
@@ -19,6 +19,7 @@ const { THETES_GRADUATION_THRESHOLD, maybeAutoGraduateToThetes, foldWorktreeName
|
|
|
19
19
|
const { mostRecentClaimHolderId, sealSessionLog, staleClaimHourPredicate } = require('./db-shared.js');
|
|
20
20
|
const { LIVE_STATUSES } = require('./dead-deps.js');
|
|
21
21
|
const { isPageTweak } = require('./page-tweak-hold.js');
|
|
22
|
+
const pageTweakArtist = require('./page-tweak-artist.js');
|
|
22
23
|
|
|
23
24
|
// shouldRefuseClaimResolve — may THIS caller resolve THIS claim? (task 1003767)
|
|
24
25
|
//
|
|
@@ -512,6 +513,9 @@ async function needsRebaseTasksForBuilder(builderId, client = pool) {
|
|
|
512
513
|
// The ship gate (task 1002483, ADR 0184) — see ./ship-assurance for the why.
|
|
513
514
|
const { normalizeShipAssurance, assertShipAssurance, recordShipAssurance, SHIP_ASSURANCE_KINDS } = require('./ship-assurance');
|
|
514
515
|
|
|
516
|
+
// The author of record (task 1004432): a page tweak names its ARTIST at this flip only.
|
|
517
|
+
const SHIPPED_BY_SQL = pageTweakArtist.shippedBySql('$2');
|
|
518
|
+
|
|
515
519
|
async function shipTask({ taskId, builderId, landAssurance }, deps = {}) {
|
|
516
520
|
const shipAssuranceValue = assertShipAssurance(landAssurance); // FAIL-CLOSED, ADR 0184
|
|
517
521
|
// #538 test seam (mirrors confirmTask): a caller-supplied pool is honored ONLY
|
|
@@ -523,7 +527,7 @@ async function shipTask({ taskId, builderId, landAssurance }, deps = {}) {
|
|
|
523
527
|
`UPDATE tasks
|
|
524
528
|
SET status = 'shipped',
|
|
525
529
|
shipped_at = now(),
|
|
526
|
-
shipped_by =
|
|
530
|
+
shipped_by = ${SHIPPED_BY_SQL},
|
|
527
531
|
needs_rebase = false,
|
|
528
532
|
needs_rebase_reason = NULL,
|
|
529
533
|
needs_rebase_at = NULL,
|
|
@@ -642,10 +646,13 @@ async function shipTask({ taskId, builderId, landAssurance }, deps = {}) {
|
|
|
642
646
|
// credit it keys off (breaking-the-100c-bar etc.) now lands here.
|
|
643
647
|
let justUnlocked = [];
|
|
644
648
|
const rewardPort = seams.resolveOptional('reward');
|
|
645
|
-
|
|
649
|
+
// A page tweak's ship count and credit moved to its artist (above), so the
|
|
650
|
+
// artist is whose achievements this ship can unlock; the applier's did not move.
|
|
651
|
+
const achievementBuilderId = isPageTweak(rows[0]) ? actualShipperId : creditedBuilderId;
|
|
652
|
+
if (rewardPort && rewardPort.reconcileBuilderAchievements && achievementBuilderId != null) {
|
|
646
653
|
await client.query('SAVEPOINT ach_reconcile_ship');
|
|
647
654
|
try {
|
|
648
|
-
justUnlocked = await rewardPort.reconcileBuilderAchievements(client,
|
|
655
|
+
justUnlocked = await rewardPort.reconcileBuilderAchievements(client, achievementBuilderId, { taskId });
|
|
649
656
|
await client.query('RELEASE SAVEPOINT ach_reconcile_ship');
|
|
650
657
|
} catch (_) {
|
|
651
658
|
await client.query('ROLLBACK TO SAVEPOINT ach_reconcile_ship');
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// modules/lifecycle/page-tweak-artist.js — WHO a page-tweak round belongs to,
|
|
2
|
+
// written once (task 1004432 / WA6.RP02, ADR 0341 D10).
|
|
3
|
+
//
|
|
4
|
+
// A round has two people. The ARTIST wrote the words in the studio and holds the
|
|
5
|
+
// round's WEB claim (no worktree, no creating session: the claim the submit
|
|
6
|
+
// released). The APPLIER is the session that put the words into the page, on an
|
|
7
|
+
// ordinary CLI claim in a worktree. The artist is the author of record: they are
|
|
8
|
+
// the one paid (economy's awardArtistPageCredit), the one the changelog names
|
|
9
|
+
// (page-tweak-reads.js), and, since this task, the one `tasks.shipped_by` names
|
|
10
|
+
// once the round ships. The applier is a helper.
|
|
11
|
+
//
|
|
12
|
+
// Pure: SQL fragments and nothing else, so every reader takes the rule from here
|
|
13
|
+
// instead of re-typing it. Economy cannot require this file (a module reaches
|
|
14
|
+
// another only through a port), so credits.js keeps its own copy of the
|
|
15
|
+
// predicate and tests/page_tweak_artist.mjs holds the two strings together.
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const PAGE_TWEAK_SOURCE = 'page-tweak';
|
|
20
|
+
|
|
21
|
+
// A WEB claim: the artist's. Neither a worktree (a CLI applier) nor a creating
|
|
22
|
+
// session (an agent-made claim) is on it.
|
|
23
|
+
function webClaimPredicate(alias = 'c') {
|
|
24
|
+
return `${alias}.worktree_name IS NULL AND ${alias}.creator_session_id IS NULL`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// The newest claim first; the id breaks a same-instant tie the same way in
|
|
28
|
+
// every reader.
|
|
29
|
+
function latestFirst(alias = 'c') {
|
|
30
|
+
return `${alias}.claimed_at DESC, ${alias}.id DESC`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// The round's artist: the builder of its latest web claim, as a scalar
|
|
34
|
+
// subquery over `taskIdExpr` (a column or a bind parameter). NULL when the
|
|
35
|
+
// round never had a web claim, which a caller treats as "no artist".
|
|
36
|
+
function artistIdSql(taskIdExpr) {
|
|
37
|
+
return `(SELECT c.builder_id FROM claims c
|
|
38
|
+
WHERE c.task_id = ${taskIdExpr} AND ${webClaimPredicate('c')}
|
|
39
|
+
ORDER BY ${latestFirst('c')}
|
|
40
|
+
LIMIT 1)`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// THE AUTHOR OF RECORD, as shipTask writes it (db-ship.js). Everywhere shipped
|
|
44
|
+
// work is counted or listed reads tasks.shipped_by: the profile, the shipped
|
|
45
|
+
// count, the activity log, the status page's recent list, streaks,
|
|
46
|
+
// achievements, the hub rollup. For an ordinary task that is the claim holder,
|
|
47
|
+
// set at confirm, and this keeps it (COALESCE with `fallbackParam`, the caller's
|
|
48
|
+
// builder). For a page tweak it is the ARTIST, written at the SHIPPED flip and
|
|
49
|
+
// nowhere earlier: until a round ships, shipped_by still names the applier,
|
|
50
|
+
// because the rebase gate (needsRebaseTasksForBuilder, status='confirmed') must
|
|
51
|
+
// refuse the session that can actually rebase, never an artist with no
|
|
52
|
+
// worktree. A round with no web claim keeps its applier. Used inside
|
|
53
|
+
// `UPDATE tasks`, so the row is `tasks`.
|
|
54
|
+
function shippedBySql(fallbackParam) {
|
|
55
|
+
return `CASE WHEN source = '${PAGE_TWEAK_SOURCE}'
|
|
56
|
+
THEN COALESCE(${artistIdSql('tasks.id')}, shipped_by, ${fallbackParam})
|
|
57
|
+
ELSE COALESCE(shipped_by, ${fallbackParam}) END`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// The helper beside a shipped row, for a list that shows people. The helper is
|
|
61
|
+
// the round's APPLIER, the builder of its latest WORKTREE claim: display only
|
|
62
|
+
// ("with help from"), never attribution or pay. A LEFT JOIN
|
|
63
|
+
// LATERAL yielding helper_login and helper_display_name on a page tweak whose
|
|
64
|
+
// applier is someone other than the author of record, and nothing otherwise
|
|
65
|
+
// (an ordinary task, a self-applied round, a round with no worktree claim).
|
|
66
|
+
// `alias` is the tasks alias of the surrounding query.
|
|
67
|
+
//
|
|
68
|
+
// THE COST, since it runs once per row of a feed. The source test sits inside
|
|
69
|
+
// the claims scan and names only the outer row, so Postgres plans it as a
|
|
70
|
+
// one-time filter: an ordinary task reads no claim at all. A page tweak's lookup
|
|
71
|
+
// is idx_claims_task_all (core_263, claims (task_id)) plus a sort of that one
|
|
72
|
+
// task's few claims, and the feeds are LIMITed.
|
|
73
|
+
function helperJoinSql(alias = 't') {
|
|
74
|
+
return `LEFT JOIN LATERAL (
|
|
75
|
+
SELECT hb.github_login AS helper_login, hb.display_name AS helper_display_name
|
|
76
|
+
FROM (SELECT c.builder_id FROM claims c
|
|
77
|
+
WHERE ${alias}.source = '${PAGE_TWEAK_SOURCE}'
|
|
78
|
+
AND c.task_id = ${alias}.id AND c.worktree_name IS NOT NULL
|
|
79
|
+
ORDER BY ${latestFirst('c')}
|
|
80
|
+
LIMIT 1) ap
|
|
81
|
+
JOIN builders hb ON hb.id = ap.builder_id
|
|
82
|
+
WHERE ap.builder_id IS DISTINCT FROM ${alias}.shipped_by
|
|
83
|
+
) helper ON true`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = { PAGE_TWEAK_SOURCE, webClaimPredicate, latestFirst, artistIdSql, shippedBySql, helperJoinSql };
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
const api = require('../../src/module-api');
|
|
15
15
|
const hold = require('./page-tweak-hold');
|
|
16
|
+
const artistRule = require('./page-tweak-artist');
|
|
16
17
|
|
|
17
18
|
// Every page-tweak task, oldest first, with what the round state (D7) and the
|
|
18
19
|
// changelog (D8) need beside the task itself:
|
|
@@ -35,9 +36,10 @@ const hold = require('./page-tweak-hold');
|
|
|
35
36
|
// * the active claim is a plain join: uniq_active_claim_per_task allows at
|
|
36
37
|
// most one row per task, and its partial index serves it.
|
|
37
38
|
// * the latest web claim is ONE pass over claims (the `web` CTE, DISTINCT ON
|
|
38
|
-
// per task), joined once, rather than a correlated subquery per task
|
|
39
|
-
// claims
|
|
40
|
-
//
|
|
39
|
+
// per task), joined once, rather than a correlated subquery per task.
|
|
40
|
+
// (When this was written claims had no full index on task_id; core_263 has
|
|
41
|
+
// since added idx_claims_task_all, so a per-task lookup is an index probe.
|
|
42
|
+
// The single pass stays: it is still the cheaper plan for every round at once.)
|
|
41
43
|
//
|
|
42
44
|
// `pageId` narrows it to ONE page's rounds (task 1004319 / TW08): the draft
|
|
43
45
|
// autosave runs this on every debounced save, and a page's rounds are a handful
|
|
@@ -52,8 +54,8 @@ async function listPageTweakTasks({ pageId = null } = {}) {
|
|
|
52
54
|
), web AS (
|
|
53
55
|
SELECT DISTINCT ON (c.task_id) c.task_id, c.builder_id
|
|
54
56
|
FROM claims c JOIN pt ON pt.id = c.task_id
|
|
55
|
-
WHERE
|
|
56
|
-
ORDER BY c.task_id,
|
|
57
|
+
WHERE ${artistRule.webClaimPredicate('c')}
|
|
58
|
+
ORDER BY c.task_id, ${artistRule.latestFirst('c')}
|
|
57
59
|
)
|
|
58
60
|
SELECT t.id, t.status, t.title, t.description, t.source_ref, t.touches,
|
|
59
61
|
t.shipped_at, t.created_at, t.updated_at,
|
|
@@ -33,6 +33,7 @@ const { depRefusalRow } = require('../dead-deps.js');
|
|
|
33
33
|
// quality verdict rather than an outage or an errored worker (task 1002652).
|
|
34
34
|
// The acclaim feed reads it so a grader outage never reaches a voter as 0.0.
|
|
35
35
|
const { gradeQualityWhere } = require('../db-shared');
|
|
36
|
+
const { helperJoinSql } = require('../page-tweak-artist'); // task 1004432: a page tweak's applier as a helper
|
|
36
37
|
const auth = api;
|
|
37
38
|
const db = require('../db');
|
|
38
39
|
const pageTweakReads = require('../page-tweak-reads'); const { HELD_NOT_ARMED } = require('../page-tweak-hold'); // ADR 0341 D7 (tests stub the read)
|
|
@@ -783,6 +784,7 @@ module.exports = function buildTasksRouter() {
|
|
|
783
784
|
`SELECT t.id, t.title, t.value_summary, t.version_id, t.status,
|
|
784
785
|
t.shipped_at, t.shipped_by,
|
|
785
786
|
b.github_login, b.display_name,
|
|
787
|
+
helper.helper_login, helper.helper_display_name,
|
|
786
788
|
COALESCE(pv.net, 0)::int AS net_votes,
|
|
787
789
|
mine.vote AS my_vote,
|
|
788
790
|
-- task 1002835: a karma vote is uninformed without the two facts
|
|
@@ -801,6 +803,7 @@ module.exports = function buildTasksRouter() {
|
|
|
801
803
|
cost.total_usd AS cost_usd
|
|
802
804
|
FROM tasks t
|
|
803
805
|
LEFT JOIN builders b ON b.id = t.shipped_by
|
|
806
|
+
${helperJoinSql('t')}
|
|
804
807
|
LEFT JOIN task_grades g ON g.task_id = t.id
|
|
805
808
|
LEFT JOIN LATERAL (
|
|
806
809
|
SELECT SUM(amount_usd) AS total_usd
|
|
@@ -56,6 +56,23 @@ const SOFT_LIMIT_RULES = Object.freeze([
|
|
|
56
56
|
effect: 'grey',
|
|
57
57
|
reason: 'A nonprofit has no owners, so it has no shares to give.',
|
|
58
58
|
}),
|
|
59
|
+
// The owner's call (task 1004475, 2026-09-30): a single-owner company and a research
|
|
60
|
+
// entity are greyed too, each with the reason that fits it. Only a for-profit company
|
|
61
|
+
// or a division of a corporation is offered equity.
|
|
62
|
+
Object.freeze({
|
|
63
|
+
id: 'equity-not-in-a-single-owner-llc',
|
|
64
|
+
when: { entity: { in: ['single-owner-llc'] } },
|
|
65
|
+
options: ['reward_intents:equity'],
|
|
66
|
+
effect: 'grey',
|
|
67
|
+
reason: 'A single-owner company has one owner. Giving builders a stake means taking on more owners first.',
|
|
68
|
+
}),
|
|
69
|
+
Object.freeze({
|
|
70
|
+
id: 'equity-not-in-a-research-entity',
|
|
71
|
+
when: { entity: { in: ['research'] } },
|
|
72
|
+
options: ['reward_intents:equity'],
|
|
73
|
+
effect: 'grey',
|
|
74
|
+
reason: "A research entity usually has no shares to give, so equity is off unless it's set up as a company.",
|
|
75
|
+
}),
|
|
59
76
|
Object.freeze({
|
|
60
77
|
id: 'no-cash-payouts-to-eu-builders-yet',
|
|
61
78
|
when: { builder_geography: { in: ['us-eu', 'global'] } },
|
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "cutout",
|
|
3
|
+
"model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
|
|
4
|
+
"aspect_ratio": "1:1",
|
|
5
|
+
"generated": "2026-09-30",
|
|
6
|
+
"round": 5,
|
|
7
|
+
"task": 1004425,
|
|
8
|
+
"stage": "scope",
|
|
9
|
+
"label": "primordial sea",
|
|
10
|
+
"prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. A chrome planet whose whole surface is a slow, liquid chrome ocean with faint swirling currents, calm and empty, nothing living yet.",
|
|
11
|
+
"reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
|
|
12
|
+
"resolution": "1024x1024 from the same panel",
|
|
13
|
+
"source": "r5-life-1-primordial-sea.jpg with the black ground removed: flood-fill from the edges on near-black pixels, luminance-feathered",
|
|
14
|
+
"encoded": "canvas WebP quality 0.88 with alpha",
|
|
15
|
+
"use": "light mode: the genesis life strip, stage scope (primordial sea)"
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "plate",
|
|
3
|
+
"model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
|
|
4
|
+
"aspect_ratio": "1:1",
|
|
5
|
+
"generated": "2026-09-30",
|
|
6
|
+
"round": 5,
|
|
7
|
+
"task": 1004425,
|
|
8
|
+
"stage": "scope",
|
|
9
|
+
"label": "primordial sea",
|
|
10
|
+
"prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. A chrome planet whose whole surface is a slow, liquid chrome ocean with faint swirling currents, calm and empty, nothing living yet.",
|
|
11
|
+
"reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
|
|
12
|
+
"resolution": "source panel about 220px across the object in a 1024x559 sheet, squared and upscaled to 1024px; soft if shown large, sized for the genesis life strip",
|
|
13
|
+
"source": "the owner's Gemini sheet, split by the ui-design kit browser (panel detection on the white dividers)",
|
|
14
|
+
"encoded": "canvas JPEG quality 0.92, 1024x1024, pure black ground",
|
|
15
|
+
"use": "dark mode: the genesis life strip, stage scope (primordial sea)"
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "cutout",
|
|
3
|
+
"model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
|
|
4
|
+
"aspect_ratio": "1:1",
|
|
5
|
+
"generated": "2026-09-30",
|
|
6
|
+
"round": 5,
|
|
7
|
+
"task": 1004425,
|
|
8
|
+
"stage": "strategize",
|
|
9
|
+
"label": "first cells",
|
|
10
|
+
"prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. The same chrome planet with a cluster of small glossy chrome beads, like the first cells, gathering on one side of its surface.",
|
|
11
|
+
"reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
|
|
12
|
+
"resolution": "1024x1024 from the same panel",
|
|
13
|
+
"source": "r5-life-2-first-cells.jpg with the black ground removed: flood-fill from the edges on near-black pixels, luminance-feathered",
|
|
14
|
+
"encoded": "canvas WebP quality 0.88 with alpha",
|
|
15
|
+
"use": "light mode: the genesis life strip, stage strategize (first cells)"
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "plate",
|
|
3
|
+
"model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
|
|
4
|
+
"aspect_ratio": "1:1",
|
|
5
|
+
"generated": "2026-09-30",
|
|
6
|
+
"round": 5,
|
|
7
|
+
"task": 1004425,
|
|
8
|
+
"stage": "strategize",
|
|
9
|
+
"label": "first cells",
|
|
10
|
+
"prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. The same chrome planet with a cluster of small glossy chrome beads, like the first cells, gathering on one side of its surface.",
|
|
11
|
+
"reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
|
|
12
|
+
"resolution": "source panel about 220px across the object in a 1024x559 sheet, squared and upscaled to 1024px; soft if shown large, sized for the genesis life strip",
|
|
13
|
+
"source": "the owner's Gemini sheet, split by the ui-design kit browser (panel detection on the white dividers)",
|
|
14
|
+
"encoded": "canvas JPEG quality 0.92, 1024x1024, pure black ground",
|
|
15
|
+
"use": "dark mode: the genesis life strip, stage strategize (first cells)"
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "cutout",
|
|
3
|
+
"model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
|
|
4
|
+
"aspect_ratio": "1:1",
|
|
5
|
+
"generated": "2026-09-30",
|
|
6
|
+
"round": 5,
|
|
7
|
+
"task": 1004425,
|
|
8
|
+
"stage": "charter",
|
|
9
|
+
"label": "the genome",
|
|
10
|
+
"prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. The same chrome planet wrapped loosely by a single double-helix ribbon of polished chrome, like DNA, circling it at an angle.",
|
|
11
|
+
"reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
|
|
12
|
+
"resolution": "1024x1024 from the same panel",
|
|
13
|
+
"source": "r5-life-3-genome.jpg with the black ground removed: flood-fill from the edges on near-black pixels, luminance-feathered",
|
|
14
|
+
"encoded": "canvas WebP quality 0.88 with alpha",
|
|
15
|
+
"use": "light mode: the genesis life strip, stage charter (the genome)"
|
|
16
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "plate",
|
|
3
|
+
"model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
|
|
4
|
+
"aspect_ratio": "1:1",
|
|
5
|
+
"generated": "2026-09-30",
|
|
6
|
+
"round": 5,
|
|
7
|
+
"task": 1004425,
|
|
8
|
+
"stage": "charter",
|
|
9
|
+
"label": "the genome",
|
|
10
|
+
"prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. The same chrome planet wrapped loosely by a single double-helix ribbon of polished chrome, like DNA, circling it at an angle.",
|
|
11
|
+
"reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
|
|
12
|
+
"resolution": "source panel about 220px across the object in a 1024x559 sheet, squared and upscaled to 1024px; soft if shown large, sized for the genesis life strip",
|
|
13
|
+
"source": "the owner's Gemini sheet, split by the ui-design kit browser (panel detection on the white dividers)",
|
|
14
|
+
"encoded": "canvas JPEG quality 0.92, 1024x1024, pure black ground",
|
|
15
|
+
"use": "dark mode: the genesis life strip, stage charter (the genome)"
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "cutout",
|
|
3
|
+
"model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
|
|
4
|
+
"aspect_ratio": "1:1",
|
|
5
|
+
"generated": "2026-09-30",
|
|
6
|
+
"round": 5,
|
|
7
|
+
"task": 1004425,
|
|
8
|
+
"stage": "legalize",
|
|
9
|
+
"label": "the membrane",
|
|
10
|
+
"prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. The same chrome planet sealed inside a thin, translucent frosted-glass shell, a soft membrane with a gentle inner glow.",
|
|
11
|
+
"reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
|
|
12
|
+
"resolution": "1024x1024 from the same panel",
|
|
13
|
+
"source": "r5-life-4-membrane.jpg with the black ground removed: flood-fill from the edges on near-black pixels, luminance-feathered; the halo outside the shell rim un-multiplied from black so the glow fades on light paper instead of darkening",
|
|
14
|
+
"encoded": "canvas WebP quality 0.88 with alpha",
|
|
15
|
+
"use": "light mode: the genesis life strip, stage legalize (the membrane)"
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "plate",
|
|
3
|
+
"model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
|
|
4
|
+
"aspect_ratio": "1:1",
|
|
5
|
+
"generated": "2026-09-30",
|
|
6
|
+
"round": 5,
|
|
7
|
+
"task": 1004425,
|
|
8
|
+
"stage": "legalize",
|
|
9
|
+
"label": "the membrane",
|
|
10
|
+
"prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. The same chrome planet sealed inside a thin, translucent frosted-glass shell, a soft membrane with a gentle inner glow.",
|
|
11
|
+
"reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
|
|
12
|
+
"resolution": "source panel about 220px across the object in a 1024x559 sheet, squared and upscaled to 1024px; soft if shown large, sized for the genesis life strip",
|
|
13
|
+
"source": "the owner's Gemini sheet, split by the ui-design kit browser (panel detection on the white dividers)",
|
|
14
|
+
"encoded": "canvas JPEG quality 0.92, 1024x1024, pure black ground",
|
|
15
|
+
"use": "dark mode: the genesis life strip, stage legalize (the membrane)"
|
|
16
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "cutout",
|
|
3
|
+
"model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
|
|
4
|
+
"aspect_ratio": "1:1",
|
|
5
|
+
"generated": "2026-09-30",
|
|
6
|
+
"round": 5,
|
|
7
|
+
"task": 1004425,
|
|
8
|
+
"stage": "legislation",
|
|
9
|
+
"label": "the nervous system",
|
|
10
|
+
"prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. The same chrome planet with fine branching chrome filaments spreading across its surface like neurons, a faint warm glow where they meet.",
|
|
11
|
+
"reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
|
|
12
|
+
"resolution": "1024x1024 from the same panel",
|
|
13
|
+
"source": "r5-life-5-nervous-system.jpg with the black ground removed: flood-fill from the edges on near-black pixels, luminance-feathered",
|
|
14
|
+
"encoded": "canvas WebP quality 0.88 with alpha",
|
|
15
|
+
"use": "light mode: the genesis life strip, stage legislation (the nervous system)"
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "plate",
|
|
3
|
+
"model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
|
|
4
|
+
"aspect_ratio": "1:1",
|
|
5
|
+
"generated": "2026-09-30",
|
|
6
|
+
"round": 5,
|
|
7
|
+
"task": 1004425,
|
|
8
|
+
"stage": "legislation",
|
|
9
|
+
"label": "the nervous system",
|
|
10
|
+
"prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. The same chrome planet with fine branching chrome filaments spreading across its surface like neurons, a faint warm glow where they meet.",
|
|
11
|
+
"reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
|
|
12
|
+
"resolution": "source panel about 220px across the object in a 1024x559 sheet, squared and upscaled to 1024px; soft if shown large, sized for the genesis life strip",
|
|
13
|
+
"source": "the owner's Gemini sheet, split by the ui-design kit browser (panel detection on the white dividers)",
|
|
14
|
+
"encoded": "canvas JPEG quality 0.92, 1024x1024, pure black ground",
|
|
15
|
+
"use": "dark mode: the genesis life strip, stage legislation (the nervous system)"
|
|
16
|
+
}
|
package/modules/sessions/db.js
CHANGED
|
@@ -741,6 +741,10 @@ async function sessionUploadCoverage({ days, windowHours } = {}) {
|
|
|
741
741
|
AND t.shipped_by IS NOT NULL
|
|
742
742
|
AND t.shipped_at IS NOT NULL
|
|
743
743
|
AND t.shipped_at >= now() - ($1 * interval '1 day')
|
|
744
|
+
-- task 1004432: a shipped page tweak names its ARTIST, who writes in the
|
|
745
|
+
-- studio and runs no session, so it can never be "covered"; the session
|
|
746
|
+
-- that applied it is the applier's, on an ordinary task of its own kind.
|
|
747
|
+
AND t.source IS DISTINCT FROM 'page-tweak'
|
|
744
748
|
GROUP BY t.shipped_by, b.github_login
|
|
745
749
|
ORDER BY 4 DESC`,
|
|
746
750
|
[d, w]
|
|
@@ -765,6 +769,10 @@ async function shipsMissingSessionRecord({ days, windowHours, limit, builderId }
|
|
|
765
769
|
AND t.shipped_by IS NOT NULL
|
|
766
770
|
AND t.shipped_at IS NOT NULL
|
|
767
771
|
AND t.shipped_at >= now() - ($1 * interval '1 day')
|
|
772
|
+
-- task 1004432: a shipped page tweak names its ARTIST, who writes in the
|
|
773
|
+
-- studio and runs no session, so it can never be "covered"; the session
|
|
774
|
+
-- that applied it is the applier's, on an ordinary task of its own kind.
|
|
775
|
+
AND t.source IS DISTINCT FROM 'page-tweak'
|
|
768
776
|
AND ($3::bigint IS NULL OR t.shipped_by = $3)
|
|
769
777
|
AND NOT EXISTS (
|
|
770
778
|
SELECT 1
|
|
@@ -984,7 +984,9 @@ function renderShippedPage() {
|
|
|
984
984
|
const meta = document.createElement('p');
|
|
985
985
|
meta.className = 'shipped__meta';
|
|
986
986
|
const by = t.display_name || t.github_login;
|
|
987
|
-
|
|
987
|
+
// task 1004432: an approved page is the artist's; the session that applied it is a helper.
|
|
988
|
+
const helper = t.helper_display_name || t.helper_login;
|
|
989
|
+
meta.textContent = `${by ? `inscribed by ${by} · ` : ''}${helper ? `with help from ${helper} · ` : ''}${fmtRelativeTime(t.shipped_at)}`;
|
|
988
990
|
li.appendChild(title);
|
|
989
991
|
if (thumb) li.appendChild(thumb);
|
|
990
992
|
li.appendChild(meta);
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.20.
|
|
3
|
+
"version": "1.20.53",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.20.
|
|
9
|
+
"version": "1.20.53",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|