@bongos/core 1.17.2 → 1.18.0
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 +426 -96
- package/.devcontainer/healthcheck.sh +0 -0
- package/.devcontainer/init-firewall.sh +0 -0
- package/.devcontainer/post-create.sh +0 -0
- package/GOVERNANCE.md +1 -1
- package/README.md +26 -22
- package/bin/bongos.js +0 -0
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +2 -0
- package/clients/bongos-client/index.cjs +2 -0
- package/clients/bongos-client/index.d.ts +4 -2
- package/clients/bongos-client/index.mjs +2 -0
- package/docker-entrypoint.sh +0 -0
- package/docs/adr/0139-platform-history-migration-to-cloudbongos.md +94 -0
- package/docs/adr/0140-cloudbongos-standalone-reprovision.md +43 -0
- package/docs/adr/README.md +3 -0
- package/docs/api/openapi.json +68 -370
- package/docs/api-reference.md +7 -18
- package/docs/file-map.md +7 -25
- package/docs/onboarding/diagrams/05-versioning.mmd +4 -6
- package/docs/onboarding/diagrams/assertions.json +5 -9
- package/modules/hall-ui/public/builders.js +132 -645
- package/modules/hall-ui/public/goal-inbox.js +67 -26
- package/modules/hall-ui/public/goals-page.js +102 -177
- package/modules/hall-ui/public/goals.css +183 -0
- package/modules/hall-ui/public/goals.html +35 -253
- package/modules/hall-ui/public/goals.js +7 -2
- package/modules/hall-ui/public/hall-kit.js +474 -0
- package/modules/hall-ui/public/hall-widgets.js +24 -20
- package/modules/hall-ui/public/instances-panel.js +24 -3
- package/modules/hall-ui/public/new-project-wizard.js +6 -5
- package/modules/hall-ui/public/settings.css +355 -0
- package/modules/hall-ui/public/settings.html +86 -414
- package/modules/hall-ui/public/settings.js +96 -35
- package/modules/hall-ui/public/style.css +156 -0
- package/modules/hall-ui/public/watch.css +184 -0
- package/modules/hall-ui/public/watch.html +111 -285
- package/modules/hall-ui/public/watch.js +59 -21
- package/modules/hall-ui/public/work.css +194 -0
- package/modules/hall-ui/public/work.html +46 -374
- package/modules/hall-ui/public/work.js +137 -102
- package/modules/lifecycle/criterion-suggest.js +80 -0
- package/modules/lifecycle/db.js +42 -5
- package/modules/lifecycle/routes/goals.js +26 -0
- package/modules/lifecycle/routes/tasks.js +21 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/create.html +83 -7
- package/public-landing/index.html +11 -76
- package/scripts/cron/style_guide_audit.sh +0 -0
- package/scripts/gds/api.js +0 -0
- package/scripts/gds/architect-audit.js +0 -0
- package/scripts/gds/audit-authorship.sh +0 -0
- package/scripts/gds/audit-replay.sh +0 -0
- package/scripts/gds/box.js +0 -0
- package/scripts/gds/capture.js +0 -0
- package/scripts/gds/conductor-dispatch.js +0 -0
- package/scripts/gds/coreB-carve-proof.sh +0 -0
- package/scripts/gds/db-backup-nightly.sh +0 -0
- package/scripts/gds/db-backup-offsite.sh +0 -0
- package/scripts/gds/doctor.js +0 -0
- package/scripts/gds/grade-smoke.js +0 -0
- package/scripts/gds/hooks/pre-push.js +0 -0
- package/scripts/gds/install-git-hooks.js +0 -0
- package/scripts/gds/main-audit.js +0 -0
- package/scripts/gds/oauth-secret.js +0 -0
- package/scripts/gds/onboarding-analyze.js +0 -0
- package/scripts/gds/onboarding-ship.js +0 -0
- package/scripts/gds/paste-token.js +0 -0
- package/scripts/gds/portability-smoke-art.sh +0 -0
- package/scripts/gds/portability-smoke-memory.sh +0 -0
- package/scripts/gds/provision.js +125 -4
- package/scripts/gds/push-main.js +0 -0
- package/scripts/gds/render-ideas.js +0 -0
- package/scripts/gds/security-baseline-check.sh +0 -0
- package/scripts/gds/session.js +0 -0
- package/scripts/gds/ship.js +11 -1
- package/scripts/gds/smoke-batch-claim.sh +0 -0
- package/scripts/gds/smoke-batch-release.sh +0 -0
- package/scripts/gds/smoke-box.sh +0 -0
- package/scripts/gds/smoke-claim-consistency.sh +0 -0
- package/scripts/gds/smoke-dependencies.sh +0 -0
- package/scripts/gds/smoke-write.sh +0 -0
- package/scripts/hall-preview/README.md +103 -0
- package/scripts/hall-preview/fixtures/access-requests.json +20 -0
- package/scripts/hall-preview/fixtures/achievements.json +76 -0
- package/scripts/hall-preview/fixtures/analytics__builder.json +251 -0
- package/scripts/hall-preview/fixtures/audit-log.json +68 -0
- package/scripts/hall-preview/fixtures/blockers__detail.json +12 -0
- package/scripts/hall-preview/fixtures/box__me.json +5 -0
- package/scripts/hall-preview/fixtures/boxes__cost-ledger.json +69 -0
- package/scripts/hall-preview/fixtures/builders__active-claims.json +1 -0
- package/scripts/hall-preview/fixtures/builders__directory.json +81 -0
- package/scripts/hall-preview/fixtures/builders__roster.json +48 -0
- package/scripts/hall-preview/fixtures/goals.json +502 -0
- package/scripts/hall-preview/fixtures/goals__25.json +96 -0
- package/scripts/hall-preview/fixtures/goals__26.json +67 -0
- package/scripts/hall-preview/fixtures/goals__3.json +103 -0
- package/scripts/hall-preview/fixtures/goals__35.json +49 -0
- package/scripts/hall-preview/fixtures/goals__conflicts.json +154 -0
- package/scripts/hall-preview/fixtures/goals__detail.json +96 -0
- package/scripts/hall-preview/fixtures/goals__inbox.json +5 -0
- package/scripts/hall-preview/fixtures/goals__requests.json +3 -0
- package/scripts/hall-preview/fixtures/grades__by-builder.json +32 -0
- package/scripts/hall-preview/fixtures/inbox.json +330 -0
- package/scripts/hall-preview/fixtures/inbox__detail.json +15 -0
- package/scripts/hall-preview/fixtures/instance.json +124 -0
- package/scripts/hall-preview/fixtures/leaderboard.json +184 -0
- package/scripts/hall-preview/fixtures/me.json +476 -0
- package/scripts/hall-preview/fixtures/me__art-key__own.json +6 -0
- package/scripts/hall-preview/fixtures/me__event-sound-prefs.json +209 -0
- package/scripts/hall-preview/fixtures/me__recent-ships.json +304 -0
- package/scripts/hall-preview/fixtures/me__render.json +5 -0
- package/scripts/hall-preview/fixtures/me__skill-prefs.json +143 -0
- package/scripts/hall-preview/fixtures/me__sound-prefs.json +67 -0
- package/scripts/hall-preview/fixtures/me__wandering.json +43 -0
- package/scripts/hall-preview/fixtures/override-requests.json +15 -0
- package/scripts/hall-preview/fixtures/provisioning__fleet.json +1 -0
- package/scripts/hall-preview/fixtures/provisioning__fleet__cost-ledger.json +1 -0
- package/scripts/hall-preview/fixtures/provisioning__instances.json +1 -0
- package/scripts/hall-preview/fixtures/public__analytics__ecosystem.json +516 -0
- package/scripts/hall-preview/fixtures/public__bounty-table.json +37 -0
- package/scripts/hall-preview/fixtures/public__cost-summary.json +742 -0
- package/scripts/hall-preview/fixtures/public__estimation-drift.json +41 -0
- package/scripts/hall-preview/fixtures/public__grades.json +50 -0
- package/scripts/hall-preview/fixtures/public__progress.json +124 -0
- package/scripts/hall-preview/fixtures/public__recent-shipped.json +426 -0
- package/scripts/hall-preview/fixtures/public__repo-health.json +160 -0
- package/scripts/hall-preview/fixtures/public__repo-info.json +5 -0
- package/scripts/hall-preview/fixtures/security__docs.json +10 -0
- package/scripts/hall-preview/fixtures/security__reports.json +31 -0
- package/scripts/hall-preview/fixtures/sessions__search.json +9 -0
- package/scripts/hall-preview/fixtures/tasks.json +2420 -0
- package/scripts/hall-preview/fixtures/tasks__claimable.json +2555 -0
- package/scripts/hall-preview/fixtures/tasks__detail.json +111 -0
- package/scripts/hall-preview/fixtures/tasks__peer-votes__feed.json +330 -0
- package/scripts/hall-preview/fixtures/versions.json +89 -0
- package/scripts/hall-preview/refresh-fixtures.js +77 -0
- package/scripts/hall-preview/server.js +195 -0
- package/scripts/migrate.sh +29 -4
- package/scripts/migrate_cost_ledger.sh +0 -0
- package/scripts/render-diagrams.sh +0 -0
- package/scripts/security/dep-audit.js +0 -0
- package/scripts/setup-builder.js +0 -0
- package/scripts/setup-builder.sh +0 -0
- package/scripts/snapshots/maybe-render-on-deploy.sh +0 -0
- package/src/bongos/optimizer.js +37 -2
- package/src/module-api.js +20 -1
- package/tests/criterion_suggest.mjs +106 -0
- package/tests/migrate_root_resolution.mjs +97 -0
- package/tests/provision.mjs +69 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/* work.css — page-local styles for the Work Board (task 2170; extracted from
|
|
2
|
+
work.html's old inline <style> per the hall placement rules). Only what the
|
|
3
|
+
shared kit doesn't carry lives here: in-flight actions, claimable task
|
|
4
|
+
cards (dense 2-line layout), history list, bulk toolbar. Shared pieces —
|
|
5
|
+
.pager--paged, .count-pill, .kit-tabs, .filters overflow — are in
|
|
6
|
+
style.css. Everything derives from the shared design tokens so instance
|
|
7
|
+
packs reskin this page along with the rest of the hall. */
|
|
8
|
+
|
|
9
|
+
.kbd {
|
|
10
|
+
display: inline-block;
|
|
11
|
+
padding: 0 6px;
|
|
12
|
+
border: 1px solid var(--border-strong);
|
|
13
|
+
border-radius: 5px;
|
|
14
|
+
background: var(--surface-2);
|
|
15
|
+
font-size: 0.85em;
|
|
16
|
+
font-weight: 600;
|
|
17
|
+
color: var(--ink-soft);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.wip-builder { color: var(--ink); font-weight: 600; }
|
|
21
|
+
.wip-builder small { color: var(--ink-soft); font-weight: 400; }
|
|
22
|
+
|
|
23
|
+
/* Filtered-count line under the filter bar. */
|
|
24
|
+
.claim-count-line {
|
|
25
|
+
margin: 0 0 10px;
|
|
26
|
+
font-size: 12.5px;
|
|
27
|
+
color: var(--ink-faint);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* ---- #616 / task 2170: claim/release actions + the dense task card ---- */
|
|
31
|
+
|
|
32
|
+
/* task 2170 (owner call 2026-07-10): denser 2-line cards. The card keeps its
|
|
33
|
+
grid but tightens padding and type, actions go horizontal, and the touches
|
|
34
|
+
path-dump becomes a "✎ N files" chip with the full list in the tooltip. */
|
|
35
|
+
.task { padding: 8px 12px; gap: 2px 12px; }
|
|
36
|
+
.task__title { font-size: 13px; line-height: 1.35; }
|
|
37
|
+
.task__meta { margin-top: 1px; row-gap: 2px; }
|
|
38
|
+
|
|
39
|
+
.task__actions {
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
gap: 6px;
|
|
43
|
+
}
|
|
44
|
+
/* Claim is the primary action — filled accent so it reads above Copy. */
|
|
45
|
+
.task__claim {
|
|
46
|
+
background: var(--accent);
|
|
47
|
+
border: 1px solid var(--accent-deep);
|
|
48
|
+
color: #fff;
|
|
49
|
+
border-radius: var(--radius-sm);
|
|
50
|
+
font-size: 12.5px;
|
|
51
|
+
font-weight: 600;
|
|
52
|
+
padding: 5px 14px;
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
white-space: nowrap;
|
|
55
|
+
transition: background 150ms, opacity 150ms;
|
|
56
|
+
}
|
|
57
|
+
.task__claim:hover { background: var(--accent-deep); }
|
|
58
|
+
.task__claim:disabled { opacity: 0.55; cursor: default; }
|
|
59
|
+
|
|
60
|
+
/* Files a claimable task is responsible for — a compact count chip in the
|
|
61
|
+
meta row; the full path list rides the tooltip (task 2170 density pass). */
|
|
62
|
+
.task__touches {
|
|
63
|
+
color: var(--ink-faint);
|
|
64
|
+
font-size: 12px;
|
|
65
|
+
white-space: nowrap;
|
|
66
|
+
cursor: help;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.task__tag--newcomer {
|
|
70
|
+
background: var(--ok-weak);
|
|
71
|
+
color: var(--ok);
|
|
72
|
+
border-color: color-mix(in srgb, var(--ok) 30%, transparent);
|
|
73
|
+
}
|
|
74
|
+
.task__tag--mine {
|
|
75
|
+
background: var(--accent-weak);
|
|
76
|
+
color: var(--accent-text);
|
|
77
|
+
border-color: color-mix(in srgb, var(--accent) 35%, transparent);
|
|
78
|
+
}
|
|
79
|
+
/* task 1498 / ADR 0084: per-task rank floor. Outlined + emphasized (not a
|
|
80
|
+
filled tag) so a gated task reads as "authority required", distinct from
|
|
81
|
+
the filled newcomer tag. */
|
|
82
|
+
.task__tag--rank {
|
|
83
|
+
background: transparent;
|
|
84
|
+
color: var(--warn);
|
|
85
|
+
border-color: color-mix(in srgb, var(--warn) 45%, transparent);
|
|
86
|
+
font-weight: 600;
|
|
87
|
+
letter-spacing: 0.02em;
|
|
88
|
+
}
|
|
89
|
+
/* task 1752: the open (no floor) state — muted/neutral so "Any rank" reads as
|
|
90
|
+
"open to all", not as a warning like the gated --rank tag above. */
|
|
91
|
+
.task__tag--rank-open {
|
|
92
|
+
background: transparent;
|
|
93
|
+
color: var(--ink-faint);
|
|
94
|
+
border-color: var(--border);
|
|
95
|
+
}
|
|
96
|
+
/* task 1753: the whole card is clickable → toggles selection (task 1869). */
|
|
97
|
+
.task { cursor: pointer; }
|
|
98
|
+
.backlog-count { font-size: 13px; color: var(--ink-faint); margin-left: 8px; }
|
|
99
|
+
#backlog-list .history__row { cursor: pointer; }
|
|
100
|
+
|
|
101
|
+
/* In-flight table: release action on the viewer's own claims. */
|
|
102
|
+
.wip__action { text-align: right; white-space: nowrap; }
|
|
103
|
+
.wip__release {
|
|
104
|
+
background: var(--surface);
|
|
105
|
+
border: 1px solid var(--border-strong);
|
|
106
|
+
color: var(--ink-soft);
|
|
107
|
+
border-radius: var(--radius-sm);
|
|
108
|
+
font-size: 12px;
|
|
109
|
+
font-weight: 600;
|
|
110
|
+
padding: 4px 11px;
|
|
111
|
+
cursor: pointer;
|
|
112
|
+
transition: background 150ms, color 150ms, border-color 150ms;
|
|
113
|
+
}
|
|
114
|
+
.wip__release:hover:not(:disabled) {
|
|
115
|
+
background: var(--danger-weak);
|
|
116
|
+
color: var(--danger);
|
|
117
|
+
border-color: color-mix(in srgb, var(--danger) 40%, transparent);
|
|
118
|
+
}
|
|
119
|
+
.wip__release:disabled { opacity: 0.55; cursor: default; }
|
|
120
|
+
.wip__mine-none { color: var(--ink-faint); }
|
|
121
|
+
.wip__row--mine { background: var(--accent-weak); }
|
|
122
|
+
|
|
123
|
+
/* Recently-shipped history. */
|
|
124
|
+
.history { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; }
|
|
125
|
+
.history__row {
|
|
126
|
+
display: flex; align-items: baseline; gap: 12px;
|
|
127
|
+
padding: 6px 4px;
|
|
128
|
+
border-bottom: 1px solid var(--rule-soft);
|
|
129
|
+
}
|
|
130
|
+
.history__row:last-child { border-bottom: none; }
|
|
131
|
+
.history__id {
|
|
132
|
+
font-weight: 600; color: var(--ink-soft);
|
|
133
|
+
font-variant-numeric: tabular-nums; min-width: 3.2em;
|
|
134
|
+
}
|
|
135
|
+
.history__title { flex: 1 1 auto; color: var(--ink); min-width: 0; }
|
|
136
|
+
.history__when { color: var(--ink-faint); font-size: 12.5px; white-space: nowrap; }
|
|
137
|
+
|
|
138
|
+
@media (max-width: 640px) {
|
|
139
|
+
.task__actions { grid-column: 2; justify-self: start; }
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* ---- task 1759: multi-select + the bottom bulk-action toolbar ---- */
|
|
143
|
+
|
|
144
|
+
.wip__select-cell { width: 1%; text-align: center; }
|
|
145
|
+
.wip__select { width: 15px; height: 15px; cursor: pointer; accent-color: var(--accent); }
|
|
146
|
+
|
|
147
|
+
.bulk-toolbar {
|
|
148
|
+
position: fixed;
|
|
149
|
+
left: 50%;
|
|
150
|
+
bottom: 20px;
|
|
151
|
+
transform: translateX(-50%);
|
|
152
|
+
z-index: 120;
|
|
153
|
+
display: flex;
|
|
154
|
+
align-items: center;
|
|
155
|
+
gap: 14px;
|
|
156
|
+
flex-wrap: wrap;
|
|
157
|
+
background: var(--ink);
|
|
158
|
+
color: var(--bg-card);
|
|
159
|
+
padding: 10px 16px;
|
|
160
|
+
border-radius: var(--radius);
|
|
161
|
+
box-shadow: var(--shadow-pop);
|
|
162
|
+
max-width: min(92vw, 720px);
|
|
163
|
+
}
|
|
164
|
+
.bulk-toolbar[hidden] { display: none; }
|
|
165
|
+
.bulk-toolbar__count { font-size: 13px; font-weight: 600; white-space: nowrap; }
|
|
166
|
+
.bulk-toolbar__actions { display: flex; gap: 8px; flex-wrap: wrap; }
|
|
167
|
+
.bulk-toolbar__btn {
|
|
168
|
+
background: rgba(255, 255, 255, 0.12);
|
|
169
|
+
border: 1px solid rgba(255, 255, 255, 0.25);
|
|
170
|
+
color: var(--bg-card);
|
|
171
|
+
border-radius: var(--radius-sm);
|
|
172
|
+
font-size: 12.5px;
|
|
173
|
+
font-weight: 600;
|
|
174
|
+
padding: 6px 12px;
|
|
175
|
+
cursor: pointer;
|
|
176
|
+
white-space: nowrap;
|
|
177
|
+
transition: background 150ms, opacity 150ms;
|
|
178
|
+
}
|
|
179
|
+
.bulk-toolbar__btn:hover:not(:disabled) { background: rgba(255, 255, 255, 0.22); }
|
|
180
|
+
.bulk-toolbar__btn:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
181
|
+
.bulk-toolbar__clear {
|
|
182
|
+
background: transparent;
|
|
183
|
+
border: none;
|
|
184
|
+
color: var(--bg-card);
|
|
185
|
+
opacity: 0.75;
|
|
186
|
+
font-size: 12.5px;
|
|
187
|
+
cursor: pointer;
|
|
188
|
+
padding: 4px 6px;
|
|
189
|
+
}
|
|
190
|
+
.bulk-toolbar__clear:hover { opacity: 1; }
|
|
191
|
+
|
|
192
|
+
@media (max-width: 640px) {
|
|
193
|
+
.bulk-toolbar { left: 12px; right: 12px; bottom: 12px; transform: none; max-width: none; }
|
|
194
|
+
}
|
|
@@ -10,218 +10,10 @@
|
|
|
10
10
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
11
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
12
12
|
<link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
|
|
13
|
-
<style>
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
derives from the shared design tokens so instance packs reskin this
|
|
18
|
-
page along with the rest of the hall. */
|
|
19
|
-
|
|
20
|
-
.kbd {
|
|
21
|
-
display: inline-block;
|
|
22
|
-
padding: 0 6px;
|
|
23
|
-
border: 1px solid var(--border-strong);
|
|
24
|
-
border-radius: 5px;
|
|
25
|
-
background: var(--surface-2);
|
|
26
|
-
font-size: 0.85em;
|
|
27
|
-
font-weight: 600;
|
|
28
|
-
color: var(--ink-soft);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.wip-builder { color: var(--ink); font-weight: 600; }
|
|
32
|
-
.wip-builder small { color: var(--ink-soft); font-weight: 400; }
|
|
33
|
-
|
|
34
|
-
/* ---- #616: claim/release actions on the work board ---- */
|
|
35
|
-
|
|
36
|
-
/* Per-task action stack (Claim + Copy prompt). Sits in the task card's last
|
|
37
|
-
grid cell. */
|
|
38
|
-
.task__actions {
|
|
39
|
-
display: flex;
|
|
40
|
-
flex-direction: column;
|
|
41
|
-
align-items: stretch;
|
|
42
|
-
gap: 6px;
|
|
43
|
-
}
|
|
44
|
-
/* Claim is the primary action — filled accent so it reads above Copy. */
|
|
45
|
-
.task__claim {
|
|
46
|
-
background: var(--accent);
|
|
47
|
-
border: 1px solid var(--accent-deep);
|
|
48
|
-
color: #fff;
|
|
49
|
-
border-radius: var(--radius-sm);
|
|
50
|
-
font-size: 12.5px;
|
|
51
|
-
font-weight: 600;
|
|
52
|
-
padding: 5px 14px;
|
|
53
|
-
cursor: pointer;
|
|
54
|
-
white-space: nowrap;
|
|
55
|
-
transition: background 150ms, opacity 150ms;
|
|
56
|
-
}
|
|
57
|
-
.task__claim:hover { background: var(--accent-deep); }
|
|
58
|
-
.task__claim:disabled { opacity: 0.55; cursor: default; }
|
|
59
|
-
|
|
60
|
-
/* Files a claimable task is responsible for (touches[]). */
|
|
61
|
-
.task__touches {
|
|
62
|
-
margin: 6px 0 0;
|
|
63
|
-
font-size: 12px;
|
|
64
|
-
color: var(--ink-faint);
|
|
65
|
-
word-break: break-word;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.task__tag--newcomer {
|
|
69
|
-
background: var(--ok-weak);
|
|
70
|
-
color: var(--ok);
|
|
71
|
-
border-color: color-mix(in srgb, var(--ok) 30%, transparent);
|
|
72
|
-
}
|
|
73
|
-
.task__tag--mine {
|
|
74
|
-
background: var(--accent-weak);
|
|
75
|
-
color: var(--accent-text);
|
|
76
|
-
border-color: color-mix(in srgb, var(--accent) 35%, transparent);
|
|
77
|
-
}
|
|
78
|
-
/* task 1498 / ADR 0084: per-task rank floor. Outlined + emphasized (not a
|
|
79
|
-
filled tag) so a gated task reads as "authority required", distinct from
|
|
80
|
-
the filled newcomer tag. */
|
|
81
|
-
.task__tag--rank {
|
|
82
|
-
background: transparent;
|
|
83
|
-
color: var(--warn);
|
|
84
|
-
border-color: color-mix(in srgb, var(--warn) 45%, transparent);
|
|
85
|
-
font-weight: 600;
|
|
86
|
-
letter-spacing: 0.02em;
|
|
87
|
-
}
|
|
88
|
-
/* task 1752: the open (no floor) state — muted/neutral so "Any rank" reads as
|
|
89
|
-
"open to all", not as a warning like the gated --rank tag above. */
|
|
90
|
-
.task__tag--rank-open {
|
|
91
|
-
background: transparent;
|
|
92
|
-
color: var(--ink-faint);
|
|
93
|
-
border-color: var(--border);
|
|
94
|
-
}
|
|
95
|
-
/* task 1753: the whole card is clickable → opens the shared task-detail overlay. */
|
|
96
|
-
.task { cursor: pointer; }
|
|
97
|
-
/* task 1754: collapsed Backlog group. */
|
|
98
|
-
.backlog-summary { cursor: pointer; display: flex; align-items: baseline; gap: 8px; }
|
|
99
|
-
.backlog-summary .scroll__h { display: inline; margin: 0; }
|
|
100
|
-
.backlog-count { font-size: 13px; color: var(--ink-faint); }
|
|
101
|
-
#backlog-list .history__row { cursor: pointer; }
|
|
102
|
-
|
|
103
|
-
/* In-flight table: release action on the viewer's own claims. */
|
|
104
|
-
.wip__action { text-align: right; white-space: nowrap; }
|
|
105
|
-
.wip__release {
|
|
106
|
-
background: var(--surface);
|
|
107
|
-
border: 1px solid var(--border-strong);
|
|
108
|
-
color: var(--ink-soft);
|
|
109
|
-
border-radius: var(--radius-sm);
|
|
110
|
-
font-size: 12px;
|
|
111
|
-
font-weight: 600;
|
|
112
|
-
padding: 4px 11px;
|
|
113
|
-
cursor: pointer;
|
|
114
|
-
transition: background 150ms, color 150ms, border-color 150ms;
|
|
115
|
-
}
|
|
116
|
-
.wip__release:hover:not(:disabled) {
|
|
117
|
-
background: var(--danger-weak);
|
|
118
|
-
color: var(--danger);
|
|
119
|
-
border-color: color-mix(in srgb, var(--danger) 40%, transparent);
|
|
120
|
-
}
|
|
121
|
-
.wip__release:disabled { opacity: 0.55; cursor: default; }
|
|
122
|
-
.wip__mine-none { color: var(--ink-faint); }
|
|
123
|
-
.wip__row--mine { background: var(--accent-weak); }
|
|
124
|
-
|
|
125
|
-
/* Recently-shipped history. */
|
|
126
|
-
.history { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; }
|
|
127
|
-
.history__row {
|
|
128
|
-
display: flex; align-items: baseline; gap: 12px;
|
|
129
|
-
padding: 6px 4px;
|
|
130
|
-
border-bottom: 1px solid var(--rule-soft);
|
|
131
|
-
}
|
|
132
|
-
.history__row:last-child { border-bottom: none; }
|
|
133
|
-
.history__id {
|
|
134
|
-
font-weight: 600; color: var(--ink-soft);
|
|
135
|
-
font-variant-numeric: tabular-nums; min-width: 3.2em;
|
|
136
|
-
}
|
|
137
|
-
.history__title { flex: 1 1 auto; color: var(--ink); min-width: 0; }
|
|
138
|
-
.history__when { color: var(--ink-faint); font-size: 12.5px; white-space: nowrap; }
|
|
139
|
-
|
|
140
|
-
@media (max-width: 640px) {
|
|
141
|
-
.task__actions { grid-column: 2; justify-self: start; flex-direction: row; }
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/* ---- task 1759: multi-select + the bottom bulk-action toolbar ---- */
|
|
145
|
-
|
|
146
|
-
.wip__select-cell { width: 1%; text-align: center; }
|
|
147
|
-
.wip__select { width: 15px; height: 15px; cursor: pointer; accent-color: var(--accent); }
|
|
148
|
-
|
|
149
|
-
.bulk-toolbar {
|
|
150
|
-
position: fixed;
|
|
151
|
-
left: 50%;
|
|
152
|
-
bottom: 20px;
|
|
153
|
-
transform: translateX(-50%);
|
|
154
|
-
z-index: 120;
|
|
155
|
-
display: flex;
|
|
156
|
-
align-items: center;
|
|
157
|
-
gap: 14px;
|
|
158
|
-
flex-wrap: wrap;
|
|
159
|
-
background: var(--ink);
|
|
160
|
-
color: var(--bg-card);
|
|
161
|
-
padding: 10px 16px;
|
|
162
|
-
border-radius: var(--radius);
|
|
163
|
-
box-shadow: var(--shadow-pop);
|
|
164
|
-
max-width: min(92vw, 720px);
|
|
165
|
-
}
|
|
166
|
-
.bulk-toolbar[hidden] { display: none; }
|
|
167
|
-
.bulk-toolbar__count { font-size: 13px; font-weight: 600; white-space: nowrap; }
|
|
168
|
-
.bulk-toolbar__actions { display: flex; gap: 8px; flex-wrap: wrap; }
|
|
169
|
-
.bulk-toolbar__btn {
|
|
170
|
-
background: rgba(255, 255, 255, 0.12);
|
|
171
|
-
border: 1px solid rgba(255, 255, 255, 0.25);
|
|
172
|
-
color: var(--bg-card);
|
|
173
|
-
border-radius: var(--radius-sm);
|
|
174
|
-
font-size: 12.5px;
|
|
175
|
-
font-weight: 600;
|
|
176
|
-
padding: 6px 12px;
|
|
177
|
-
cursor: pointer;
|
|
178
|
-
white-space: nowrap;
|
|
179
|
-
transition: background 150ms, opacity 150ms;
|
|
180
|
-
}
|
|
181
|
-
.bulk-toolbar__btn:hover:not(:disabled) { background: rgba(255, 255, 255, 0.22); }
|
|
182
|
-
.bulk-toolbar__btn:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
183
|
-
.bulk-toolbar__clear {
|
|
184
|
-
background: transparent;
|
|
185
|
-
border: none;
|
|
186
|
-
color: var(--bg-card);
|
|
187
|
-
opacity: 0.75;
|
|
188
|
-
font-size: 12.5px;
|
|
189
|
-
cursor: pointer;
|
|
190
|
-
padding: 4px 6px;
|
|
191
|
-
}
|
|
192
|
-
.bulk-toolbar__clear:hover { opacity: 1; }
|
|
193
|
-
|
|
194
|
-
@media (max-width: 640px) {
|
|
195
|
-
.bulk-toolbar { left: 12px; right: 12px; bottom: 12px; transform: none; max-width: none; }
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/* ---- task 1899: capped lists (the goals-page pattern, task 1888) ---- */
|
|
199
|
-
/* Small header count pill; .pager / .pager__range / .btn-ghost live in the
|
|
200
|
-
shared style.css and are reused as-is. */
|
|
201
|
-
.count-pill {
|
|
202
|
-
display: inline-flex; align-items: center; justify-content: center;
|
|
203
|
-
min-width: 20px; height: 18px; padding: 0 6px; margin-left: 8px;
|
|
204
|
-
border-radius: 999px; background: var(--surface-2); color: var(--ink-soft);
|
|
205
|
-
font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums;
|
|
206
|
-
vertical-align: 2px;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/* ---- task 1931: paginated Ready list (Per-page dropdown + Prev/Next) ---- */
|
|
210
|
-
/* .pager / .pager__range / .btn-ghost come from the shared style.css; these
|
|
211
|
-
extend that pager into a two-sided bar (range left, controls right). */
|
|
212
|
-
.pager--paged { justify-content: space-between; flex-wrap: wrap; gap: 10px 16px; }
|
|
213
|
-
.pager__nav { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
|
214
|
-
.pager__perpage {
|
|
215
|
-
display: inline-flex; align-items: center; gap: 6px;
|
|
216
|
-
font-size: 12px; color: var(--ink-faint);
|
|
217
|
-
}
|
|
218
|
-
.pager__perpage select { height: 30px; padding: 2px 8px; font-size: 13px; }
|
|
219
|
-
.pager__page {
|
|
220
|
-
font-size: 12.5px; color: var(--ink-soft);
|
|
221
|
-
font-variant-numeric: tabular-nums; min-width: 6.5em; text-align: center;
|
|
222
|
-
}
|
|
223
|
-
.pager__nav .pager__btn:disabled { opacity: 0.4; cursor: default; }
|
|
224
|
-
</style>
|
|
13
|
+
<!-- Page-local styles (task 2170) — extracted from the old inline <style>
|
|
14
|
+
block per the hall placement rules; shared pager/count-pill styles now
|
|
15
|
+
live in style.css's hall-kit section. -->
|
|
16
|
+
<link rel="stylesheet" href="/builders/work.css">
|
|
225
17
|
</head>
|
|
226
18
|
<body data-page="work" data-page-title="Work">
|
|
227
19
|
|
|
@@ -249,10 +41,34 @@
|
|
|
249
41
|
</p>
|
|
250
42
|
</section>
|
|
251
43
|
|
|
252
|
-
<!--
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
44
|
+
<!-- task 2170: one queue at a time. The four groups are hash-synced tabs
|
|
45
|
+
(OTBKit.makeTabs toggles [hidden] — the window stays the only
|
|
46
|
+
scroller): Ready (default) | In Progress | Backlog | Completed.
|
|
47
|
+
Deep links: /work#in-progress etc. -->
|
|
48
|
+
<nav id="work-tabs" aria-label="Work board sections"></nav>
|
|
49
|
+
|
|
50
|
+
<!-- READY — the claimable queue: the page's primary object, above the
|
|
51
|
+
fold as the default tab. -->
|
|
52
|
+
<section class="scroll" id="claim-scroll" aria-labelledby="claim-h">
|
|
53
|
+
<h2 id="claim-h" class="scroll__h">Ready — work to do<span class="count-pill" id="ready-count" hidden></span></h2>
|
|
54
|
+
<p class="scroll__lede">Click <span class="kbd">Claim</span> to take a task right here — no terminal needed. Then click <span class="kbd">📋</span> to copy the task prompt and paste it into a fresh Claude Code session to begin.</p>
|
|
55
|
+
|
|
56
|
+
<!-- task 2170: the filter bar is built by OTBKit.makeFilterBar —
|
|
57
|
+
Search / Sort / Version primary, the rest behind a "Filters"
|
|
58
|
+
disclosure; state rides the URL query. Control ids are unchanged
|
|
59
|
+
(f-search, f-sort, f-version, f-goal, f-kind, f-discipline,
|
|
60
|
+
f-newcomer, f-fit) so work.js reads stay stable. -->
|
|
61
|
+
<div id="work-filters"></div>
|
|
62
|
+
<p class="claim-count-line" id="claim-count" aria-live="polite"></p>
|
|
63
|
+
|
|
64
|
+
<div class="task-list" id="task-list">
|
|
65
|
+
<div class="profile__placeholder">…</div>
|
|
66
|
+
</div>
|
|
67
|
+
<div id="ready-pager-mount"></div>
|
|
68
|
+
</section>
|
|
69
|
+
|
|
70
|
+
<!-- IN PROGRESS — the live in-flight list (active claims, all builders). -->
|
|
71
|
+
<section class="scroll" id="wip-scroll" aria-labelledby="wip-h" hidden>
|
|
256
72
|
<h2 id="wip-h" class="scroll__h">In Progress<span class="count-pill" id="wip-count" hidden></span></h2>
|
|
257
73
|
<p class="scroll__lede">What is being built right now. See <a href="/roadmap">Roadmap</a> for version-level progress and goals.</p>
|
|
258
74
|
|
|
@@ -260,175 +76,29 @@
|
|
|
260
76
|
<thead><tr><th></th><th>#</th><th>Task</th><th>Builder</th><th>For</th><th></th></tr></thead>
|
|
261
77
|
<tbody><tr><td colspan="6" class="ledger__empty">…</td></tr></tbody>
|
|
262
78
|
</table>
|
|
263
|
-
<div
|
|
264
|
-
<span class="pager__range" id="wip-range"></span>
|
|
265
|
-
<div class="pager__nav">
|
|
266
|
-
<label class="pager__perpage">
|
|
267
|
-
<span>Per page</span>
|
|
268
|
-
<select id="wip-per-page" aria-label="In-progress tasks per page">
|
|
269
|
-
<option value="10">10</option>
|
|
270
|
-
<option value="25">25</option>
|
|
271
|
-
<option value="50">50</option>
|
|
272
|
-
<option value="100">100</option>
|
|
273
|
-
</select>
|
|
274
|
-
</label>
|
|
275
|
-
<button class="btn-ghost pager__btn" type="button" id="wip-prev" aria-label="Previous page">‹ Prev</button>
|
|
276
|
-
<span class="pager__page" id="wip-page" aria-live="polite"></span>
|
|
277
|
-
<button class="btn-ghost pager__btn" type="button" id="wip-next" aria-label="Next page">Next ›</button>
|
|
278
|
-
</div>
|
|
279
|
-
</div>
|
|
79
|
+
<div id="wip-pager-mount"></div>
|
|
280
80
|
</section>
|
|
281
81
|
|
|
282
|
-
<!--
|
|
283
|
-
|
|
82
|
+
<!-- BACKLOG — not-yet-ready tasks (read-only). Its own tab now; the old
|
|
83
|
+
collapsed <details> solved the same "don't stack it" problem tabs
|
|
84
|
+
solve properly (task 2170). -->
|
|
284
85
|
<section class="scroll" id="backlog-scroll" aria-labelledby="backlog-h" hidden>
|
|
285
|
-
<
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
<div class="pager pager--paged" id="backlog-pager" hidden>
|
|
290
|
-
<span class="pager__range" id="backlog-range"></span>
|
|
291
|
-
<div class="pager__nav">
|
|
292
|
-
<label class="pager__perpage">
|
|
293
|
-
<span>Per page</span>
|
|
294
|
-
<select id="backlog-per-page" aria-label="Backlog tasks per page">
|
|
295
|
-
<option value="10">10</option>
|
|
296
|
-
<option value="25">25</option>
|
|
297
|
-
<option value="50">50</option>
|
|
298
|
-
<option value="100">100</option>
|
|
299
|
-
</select>
|
|
300
|
-
</label>
|
|
301
|
-
<button class="btn-ghost pager__btn" type="button" id="backlog-prev" aria-label="Previous page">‹ Prev</button>
|
|
302
|
-
<span class="pager__page" id="backlog-page" aria-live="polite"></span>
|
|
303
|
-
<button class="btn-ghost pager__btn" type="button" id="backlog-next" aria-label="Next page">Next ›</button>
|
|
304
|
-
</div>
|
|
305
|
-
</div>
|
|
306
|
-
</details>
|
|
307
|
-
</section>
|
|
308
|
-
|
|
309
|
-
<section class="scroll" id="claim-scroll" aria-labelledby="claim-h">
|
|
310
|
-
<h2 id="claim-h" class="scroll__h">Ready — work to do<span class="count-pill" id="ready-count" hidden></span></h2>
|
|
311
|
-
<p class="scroll__lede">Click <span class="kbd">Claim</span> to take a task right here — no terminal needed. Then click <span class="kbd">📋</span> to copy the task prompt and paste it into a fresh Claude Code session to begin.</p>
|
|
312
|
-
|
|
313
|
-
<div class="filters">
|
|
314
|
-
<label class="filter filter--search">
|
|
315
|
-
<span>Search</span>
|
|
316
|
-
<input id="f-search" type="search" placeholder="task #, title, or goal…" aria-label="Search tasks by number, title, or goal" autocomplete="off">
|
|
317
|
-
</label>
|
|
318
|
-
<label class="filter">
|
|
319
|
-
<span>Version</span>
|
|
320
|
-
<select id="f-version"><option value="">— any —</option></select>
|
|
321
|
-
</label>
|
|
322
|
-
<label class="filter">
|
|
323
|
-
<span>Goal</span>
|
|
324
|
-
<select id="f-goal"><option value="">— all goals —</option></select>
|
|
325
|
-
</label>
|
|
326
|
-
<label class="filter">
|
|
327
|
-
<span>Kind</span>
|
|
328
|
-
<select id="f-kind">
|
|
329
|
-
<option value="">— any —</option>
|
|
330
|
-
<option value="feature">feature</option>
|
|
331
|
-
<option value="bug">bug</option>
|
|
332
|
-
<option value="infra">infra</option>
|
|
333
|
-
<option value="refactor">refactor</option>
|
|
334
|
-
<option value="spike">spike</option>
|
|
335
|
-
<option value="learning-capture">learning-capture</option>
|
|
336
|
-
<option value="cleanup">cleanup</option>
|
|
337
|
-
<option value="decision">decision</option>
|
|
338
|
-
<option value="blocker-resolution">blocker-resolution</option>
|
|
339
|
-
<option value="unclassified">unclassified</option>
|
|
340
|
-
</select>
|
|
341
|
-
</label>
|
|
342
|
-
<label class="filter">
|
|
343
|
-
<span>Discipline</span>
|
|
344
|
-
<select id="f-discipline">
|
|
345
|
-
<option value="">— any —</option>
|
|
346
|
-
<option value="engineer">engineer</option>
|
|
347
|
-
<option value="artist">artist</option>
|
|
348
|
-
<option value="ideator">ideator</option>
|
|
349
|
-
<option value="unclassified">unclassified</option>
|
|
350
|
-
</select>
|
|
351
|
-
</label>
|
|
352
|
-
<label class="filter">
|
|
353
|
-
<span>Newcomer</span>
|
|
354
|
-
<select id="f-newcomer">
|
|
355
|
-
<option value="">— any —</option>
|
|
356
|
-
<option value="yes">newcomer-friendly only</option>
|
|
357
|
-
</select>
|
|
358
|
-
</label>
|
|
359
|
-
<label class="filter">
|
|
360
|
-
<span>Run mode</span>
|
|
361
|
-
<select id="f-fit" title="Whether a task's files overlap other visible tasks">
|
|
362
|
-
<option value="">— any —</option>
|
|
363
|
-
<option value="parallel">parallel-safe only</option>
|
|
364
|
-
<option value="conflict">has file conflicts</option>
|
|
365
|
-
</select>
|
|
366
|
-
</label>
|
|
367
|
-
<label class="filter">
|
|
368
|
-
<span>Sort</span>
|
|
369
|
-
<select id="f-sort">
|
|
370
|
-
<option value="cpm" data-currency-tmpl="{label} / minute (high → low)">credits / minute (high → low)</option>
|
|
371
|
-
<option value="credits" data-currency-tmpl="{label} (high → low)">credits (high → low)</option>
|
|
372
|
-
<option value="minutes">minutes (low → high)</option>
|
|
373
|
-
<option value="priority">priority (P0 first)</option>
|
|
374
|
-
<option value="id">task number (low → high)</option>
|
|
375
|
-
</select>
|
|
376
|
-
</label>
|
|
377
|
-
<span id="claim-count" aria-live="polite" class="filter__count"></span>
|
|
378
|
-
</div>
|
|
379
|
-
|
|
380
|
-
<div class="task-list" id="task-list">
|
|
381
|
-
<div class="profile__placeholder">…</div>
|
|
382
|
-
</div>
|
|
383
|
-
<div class="pager pager--paged" id="ready-pager" hidden>
|
|
384
|
-
<span class="pager__range" id="ready-range"></span>
|
|
385
|
-
<div class="pager__nav">
|
|
386
|
-
<label class="pager__perpage">
|
|
387
|
-
<span>Per page</span>
|
|
388
|
-
<select id="ready-per-page" aria-label="Ready tasks per page">
|
|
389
|
-
<option value="10">10</option>
|
|
390
|
-
<option value="25">25</option>
|
|
391
|
-
<option value="50">50</option>
|
|
392
|
-
<option value="100">100</option>
|
|
393
|
-
</select>
|
|
394
|
-
</label>
|
|
395
|
-
<button class="btn-ghost pager__btn" type="button" id="ready-prev" aria-label="Previous page">‹ Prev</button>
|
|
396
|
-
<span class="pager__page" id="ready-page" aria-live="polite"></span>
|
|
397
|
-
<button class="btn-ghost pager__btn" type="button" id="ready-next" aria-label="Next page">Next ›</button>
|
|
398
|
-
</div>
|
|
399
|
-
</div>
|
|
86
|
+
<h2 id="backlog-h" class="scroll__h">Backlog<span class="backlog-count" id="backlog-count"></span></h2>
|
|
87
|
+
<p class="scroll__lede">Not yet ready — blocked by dependencies or awaiting promotion. These become claimable when their blockers ship. Click a task to see why.</p>
|
|
88
|
+
<div class="history" id="backlog-list"></div>
|
|
89
|
+
<div id="backlog-pager-mount"></div>
|
|
400
90
|
</section>
|
|
401
91
|
|
|
402
|
-
<!--
|
|
403
|
-
|
|
404
|
-
surface (GET /public/recent-shipped); never gated. -->
|
|
92
|
+
<!-- COMPLETED — recently shipped + the project-wide momentum summary
|
|
93
|
+
(task 1469). Public read surface; never gated. -->
|
|
405
94
|
<section class="scroll" id="history-scroll" aria-labelledby="history-h" hidden>
|
|
406
95
|
<h2 id="history-h" class="scroll__h">Completed<span class="count-pill" id="done-count" hidden></span></h2>
|
|
407
|
-
<!-- task 1469: project-wide "works completed" momentum summary — total
|
|
408
|
-
works shipped, total currency awarded, and the latest ship. Filled
|
|
409
|
-
by renderWorksSummary() from public reads; hidden until it loads. -->
|
|
410
96
|
<div class="stat-row" id="works-summary" style="margin-bottom: 14px" hidden></div>
|
|
411
97
|
<p class="scroll__lede">The most recently shipped tasks.</p>
|
|
412
98
|
<div class="history" id="history-list">
|
|
413
99
|
<div class="profile__placeholder">…</div>
|
|
414
100
|
</div>
|
|
415
|
-
<div
|
|
416
|
-
<span class="pager__range" id="done-range"></span>
|
|
417
|
-
<div class="pager__nav">
|
|
418
|
-
<label class="pager__perpage">
|
|
419
|
-
<span>Per page</span>
|
|
420
|
-
<select id="done-per-page" aria-label="Completed tasks per page">
|
|
421
|
-
<option value="10">10</option>
|
|
422
|
-
<option value="25">25</option>
|
|
423
|
-
<option value="50">50</option>
|
|
424
|
-
<option value="100">100</option>
|
|
425
|
-
</select>
|
|
426
|
-
</label>
|
|
427
|
-
<button class="btn-ghost pager__btn" type="button" id="done-prev" aria-label="Previous page">‹ Prev</button>
|
|
428
|
-
<span class="pager__page" id="done-page" aria-live="polite"></span>
|
|
429
|
-
<button class="btn-ghost pager__btn" type="button" id="done-next" aria-label="Next page">Next ›</button>
|
|
430
|
-
</div>
|
|
431
|
-
</div>
|
|
101
|
+
<div id="done-pager-mount"></div>
|
|
432
102
|
</section>
|
|
433
103
|
</main>
|
|
434
104
|
|
|
@@ -444,7 +114,7 @@
|
|
|
444
114
|
rows). work.js greys Bulk claim/release out when nothing of that kind is
|
|
445
115
|
selected, and Copy parallel/sequential prompt out when the selection
|
|
446
116
|
can't run that way (file/module conflicts, or an unorderable dependency
|
|
447
|
-
cycle). -->
|
|
117
|
+
cycle). Selection clears on tab switch (task 2170). -->
|
|
448
118
|
<div id="bulk-toolbar" class="bulk-toolbar" hidden role="region" aria-label="Bulk actions">
|
|
449
119
|
<span class="bulk-toolbar__count" id="bulk-count"></span>
|
|
450
120
|
<div class="bulk-toolbar__actions">
|
|
@@ -461,6 +131,8 @@
|
|
|
461
131
|
<script src="/builders/shell.js?v=2026-07-02-shell1"></script>
|
|
462
132
|
<!-- shared frontend toolkit (window.OTB) — loaded FIRST so work.js can pull from it. -->
|
|
463
133
|
<script src="/builders/dom-utils.js?v=2026-06-14-domutils" defer></script>
|
|
134
|
+
<!-- shared component kit (window.OTBKit) — tabs, pager, filter bar (task 2166). -->
|
|
135
|
+
<script src="/builders/hall-kit.js" defer></script>
|
|
464
136
|
<script src="/builders/task-detail.js?v=2026-07-03-taskdetail" defer></script>
|
|
465
137
|
<script src="/builders/brand-apply.js?v=2026-06-19-r56" defer></script>
|
|
466
138
|
<script src="/builders/work.js?v=2026-07-03-workboard3" defer></script>
|