@bongos/core 1.19.492 → 1.19.494

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.
Files changed (110) hide show
  1. package/.bongos-core.json +449 -34
  2. package/clients/bongos-client/README.md +1 -1
  3. package/clients/bongos-client/bongos-client.global.js +2 -0
  4. package/clients/bongos-client/index.cjs +2 -0
  5. package/clients/bongos-client/index.d.ts +4 -0
  6. package/clients/bongos-client/index.mjs +2 -0
  7. package/docs/adr/0233-a-copy-proposal-is-a-task-carrying-a-patch.md +151 -0
  8. package/docs/adr/README.md +2 -0
  9. package/docs/api/openapi.json +106 -3
  10. package/docs/api-reference.md +3 -2
  11. package/docs/copy-inventory.md +26 -14
  12. package/docs/copy-registry.json +123 -15
  13. package/docs/design/reviews/hall-v2/README.md +124 -0
  14. package/docs/design/reviews/hall-v2/atlas--archon.webp +0 -0
  15. package/docs/design/reviews/hall-v2/atlas--metic.webp +0 -0
  16. package/docs/design/reviews/hall-v2/atlas--xenos.webp +0 -0
  17. package/docs/design/reviews/hall-v2/collab--archon.webp +0 -0
  18. package/docs/design/reviews/hall-v2/collab--metic.webp +0 -0
  19. package/docs/design/reviews/hall-v2/collab--xenos.webp +0 -0
  20. package/docs/design/reviews/hall-v2/copy-desk--archon.webp +0 -0
  21. package/docs/design/reviews/hall-v2/copy-desk--metic.webp +0 -0
  22. package/docs/design/reviews/hall-v2/copy-desk--xenos.webp +0 -0
  23. package/docs/design/reviews/hall-v2/diagrams--archon.webp +0 -0
  24. package/docs/design/reviews/hall-v2/diagrams--metic.webp +0 -0
  25. package/docs/design/reviews/hall-v2/diagrams--xenos.webp +0 -0
  26. package/docs/design/reviews/hall-v2/drachmae--archon.webp +0 -0
  27. package/docs/design/reviews/hall-v2/drachmae--metic.webp +0 -0
  28. package/docs/design/reviews/hall-v2/drachmae--xenos.webp +0 -0
  29. package/docs/design/reviews/hall-v2/gate--archon.webp +0 -0
  30. package/docs/design/reviews/hall-v2/gate--metic.webp +0 -0
  31. package/docs/design/reviews/hall-v2/gate--xenos.webp +0 -0
  32. package/docs/design/reviews/hall-v2/goals--archon.webp +0 -0
  33. package/docs/design/reviews/hall-v2/goals--metic.webp +0 -0
  34. package/docs/design/reviews/hall-v2/goals--xenos.webp +0 -0
  35. package/docs/design/reviews/hall-v2/government--archon.webp +0 -0
  36. package/docs/design/reviews/hall-v2/government--metic.webp +0 -0
  37. package/docs/design/reviews/hall-v2/government--xenos.webp +0 -0
  38. package/docs/design/reviews/hall-v2/harbor--archon.webp +0 -0
  39. package/docs/design/reviews/hall-v2/harbor--metic.webp +0 -0
  40. package/docs/design/reviews/hall-v2/harbor--xenos.webp +0 -0
  41. package/docs/design/reviews/hall-v2/idea--archon.webp +0 -0
  42. package/docs/design/reviews/hall-v2/idea--metic.webp +0 -0
  43. package/docs/design/reviews/hall-v2/idea--xenos.webp +0 -0
  44. package/docs/design/reviews/hall-v2/ideas--archon.webp +0 -0
  45. package/docs/design/reviews/hall-v2/ideas--metic.webp +0 -0
  46. package/docs/design/reviews/hall-v2/ideas--xenos.webp +0 -0
  47. package/docs/design/reviews/hall-v2/index--archon.webp +0 -0
  48. package/docs/design/reviews/hall-v2/index--metic.webp +0 -0
  49. package/docs/design/reviews/hall-v2/index--xenos.webp +0 -0
  50. package/docs/design/reviews/hall-v2/modules--archon.webp +0 -0
  51. package/docs/design/reviews/hall-v2/modules--metic.webp +0 -0
  52. package/docs/design/reviews/hall-v2/modules--xenos.webp +0 -0
  53. package/docs/design/reviews/hall-v2/not-ready--archon.webp +0 -0
  54. package/docs/design/reviews/hall-v2/not-ready--metic.webp +0 -0
  55. package/docs/design/reviews/hall-v2/not-ready--xenos.webp +0 -0
  56. package/docs/design/reviews/hall-v2/pair--archon.webp +0 -0
  57. package/docs/design/reviews/hall-v2/pair--metic.webp +0 -0
  58. package/docs/design/reviews/hall-v2/pair--xenos.webp +0 -0
  59. package/docs/design/reviews/hall-v2/people--archon.webp +0 -0
  60. package/docs/design/reviews/hall-v2/people--metic.webp +0 -0
  61. package/docs/design/reviews/hall-v2/people--xenos.webp +0 -0
  62. package/docs/design/reviews/hall-v2/primer--archon.webp +0 -0
  63. package/docs/design/reviews/hall-v2/primer--metic.webp +0 -0
  64. package/docs/design/reviews/hall-v2/primer--xenos.webp +0 -0
  65. package/docs/design/reviews/hall-v2/profile--archon.webp +0 -0
  66. package/docs/design/reviews/hall-v2/profile--metic.webp +0 -0
  67. package/docs/design/reviews/hall-v2/profile--xenos.webp +0 -0
  68. package/docs/design/reviews/hall-v2/ranks--archon.webp +0 -0
  69. package/docs/design/reviews/hall-v2/ranks--metic.webp +0 -0
  70. package/docs/design/reviews/hall-v2/ranks--xenos.webp +0 -0
  71. package/docs/design/reviews/hall-v2/roadmap--archon.webp +0 -0
  72. package/docs/design/reviews/hall-v2/roadmap--metic.webp +0 -0
  73. package/docs/design/reviews/hall-v2/roadmap--xenos.webp +0 -0
  74. package/docs/design/reviews/hall-v2/roster--archon.webp +0 -0
  75. package/docs/design/reviews/hall-v2/roster--metic.webp +0 -0
  76. package/docs/design/reviews/hall-v2/roster--xenos.webp +0 -0
  77. package/docs/design/reviews/hall-v2/sessions--archon.webp +0 -0
  78. package/docs/design/reviews/hall-v2/sessions--metic.webp +0 -0
  79. package/docs/design/reviews/hall-v2/sessions--xenos.webp +0 -0
  80. package/docs/design/reviews/hall-v2/settings--archon.webp +0 -0
  81. package/docs/design/reviews/hall-v2/settings--metic.webp +0 -0
  82. package/docs/design/reviews/hall-v2/settings--xenos.webp +0 -0
  83. package/docs/design/reviews/hall-v2/task--archon.webp +0 -0
  84. package/docs/design/reviews/hall-v2/task--metic.webp +0 -0
  85. package/docs/design/reviews/hall-v2/task--xenos.webp +0 -0
  86. package/docs/design/reviews/hall-v2/watch--archon.webp +0 -0
  87. package/docs/design/reviews/hall-v2/watch--metic.webp +0 -0
  88. package/docs/design/reviews/hall-v2/watch--xenos.webp +0 -0
  89. package/docs/design/reviews/hall-v2/work--archon.webp +0 -0
  90. package/docs/design/reviews/hall-v2/work--metic.webp +0 -0
  91. package/docs/design/reviews/hall-v2/work--xenos.webp +0 -0
  92. package/docs/file-map.md +2 -1
  93. package/docs/module-api-changelog.md +4 -0
  94. package/modules/copy-desk/module.json +2 -2
  95. package/modules/copy-desk/proposals.js +278 -0
  96. package/modules/copy-desk/routes/copy-desk.js +173 -9
  97. package/modules/copy-desk/tests/copy_no_cms.mjs +90 -12
  98. package/modules/copy-desk/tests/copy_proposals.mjs +279 -0
  99. package/modules/dev-box/app/src/vendor/bongos-client.cjs +2 -0
  100. package/modules/hall-ui/public/copy-desk.css +9 -1
  101. package/modules/hall-ui/public/copy-desk.html +33 -0
  102. package/modules/hall-ui/public/copy-desk.js +134 -3
  103. package/modules/hall-ui/public/copy-desk.states.json +18 -1
  104. package/modules/lifecycle/lifecycle.js +6 -0
  105. package/package-lock.json +2 -2
  106. package/package.json +1 -1
  107. package/scripts/gds/copy-apply.js +338 -0
  108. package/scripts/gds/copy-inventory.js +128 -30
  109. package/src/module-api.js +1 -1
  110. package/tests/hall_collab_world.mjs +24 -4
@@ -287,6 +287,8 @@ function createClient(opts = {}) {
287
287
  postCopyDeskFlags: (args) => request("POST", "/copy-desk/flags", { hasBody: true }, args),
288
288
  // POST /copy-desk/flags/{id}/close — rank: any-builder — POST /copy-desk/flags/:id/close
289
289
  postCopyDeskFlagsIdClose: (args) => request("POST", "/copy-desk/flags/{id}/close", { hasBody: true }, args),
290
+ // POST /copy-desk/proposals — rank: any-builder — POST /copy-desk/proposals
291
+ postCopyDeskProposals: (args) => request("POST", "/copy-desk/proposals", { hasBody: true }, args),
290
292
  // GET /copy-desk/queue — rank: any-builder — GET /copy-desk/queue
291
293
  getCopyDeskQueue: (args) => request("GET", "/copy-desk/queue", { hasBody: false }, args),
292
294
  // GET /copy-desk/strings — rank: any-builder — GET /copy-desk/strings
@@ -286,6 +286,8 @@ function createClient(opts = {}) {
286
286
  postCopyDeskFlags: (args) => request("POST", "/copy-desk/flags", { hasBody: true }, args),
287
287
  // POST /copy-desk/flags/{id}/close — rank: any-builder — POST /copy-desk/flags/:id/close
288
288
  postCopyDeskFlagsIdClose: (args) => request("POST", "/copy-desk/flags/{id}/close", { hasBody: true }, args),
289
+ // POST /copy-desk/proposals — rank: any-builder — POST /copy-desk/proposals
290
+ postCopyDeskProposals: (args) => request("POST", "/copy-desk/proposals", { hasBody: true }, args),
289
291
  // GET /copy-desk/queue — rank: any-builder — GET /copy-desk/queue
290
292
  getCopyDeskQueue: (args) => request("GET", "/copy-desk/queue", { hasBody: false }, args),
291
293
  // GET /copy-desk/strings — rank: any-builder — GET /copy-desk/strings
@@ -230,6 +230,8 @@ export interface PostCopyDeskFlagsIdCloseRequest { status: string; resolution_no
230
230
  export interface PostCopyDeskFlagsIdCloseResponse { ok: boolean; flag: unknown }
231
231
  export interface PostCopyDeskFlagsRequest { scope?: string; surface: string; string_id?: string; reason: string; kind?: string }
232
232
  export interface PostCopyDeskFlagsResponse { ok: boolean; flag: unknown }
233
+ export interface PostCopyDeskProposalsRequest { string_id: string; proposed_text: string; why: string; file?: string; line?: number; flag_id?: number }
234
+ export interface PostCopyDeskProposalsResponse { ok: boolean; task: unknown; created: boolean; patch?: unknown }
233
235
  export interface PostCostRequest { task_id?: number; amount_usd: number; category: string; description?: string; source?: string; source_ref?: string; recorded_at?: string; fresh_input_usd?: number; cache_read_usd?: number; cache_write_usd?: number; output_usd?: number; builder_id?: number; skill_name?: string }
234
236
  export interface PostCostResponse { skipped?: boolean; reason?: unknown; id?: unknown; recorded_at?: unknown }
235
237
  export interface PostDependenciesResponse { ok: boolean; dependency: unknown }
@@ -595,6 +597,8 @@ export interface BongosClient {
595
597
  postCopyDeskFlags(args: RequestArgs & { body: PostCopyDeskFlagsRequest }): Promise<PostCopyDeskFlagsResponse>;
596
598
  /** POST /copy-desk/flags/{id}/close — rank: any-builder */
597
599
  postCopyDeskFlagsIdClose(args: RequestArgs & { body: PostCopyDeskFlagsIdCloseRequest }): Promise<PostCopyDeskFlagsIdCloseResponse>;
600
+ /** POST /copy-desk/proposals — rank: any-builder */
601
+ postCopyDeskProposals(args: RequestArgs & { body: PostCopyDeskProposalsRequest }): Promise<PostCopyDeskProposalsResponse>;
598
602
  /** GET /copy-desk/queue — rank: any-builder */
599
603
  getCopyDeskQueue(args?: RequestArgs): Promise<ApiResponse>;
600
604
  /** GET /copy-desk/strings — rank: any-builder */
@@ -283,6 +283,8 @@ export function createClient(opts = {}) {
283
283
  postCopyDeskFlags: (args) => request("POST", "/copy-desk/flags", { hasBody: true }, args),
284
284
  // POST /copy-desk/flags/{id}/close — rank: any-builder — POST /copy-desk/flags/:id/close
285
285
  postCopyDeskFlagsIdClose: (args) => request("POST", "/copy-desk/flags/{id}/close", { hasBody: true }, args),
286
+ // POST /copy-desk/proposals — rank: any-builder — POST /copy-desk/proposals
287
+ postCopyDeskProposals: (args) => request("POST", "/copy-desk/proposals", { hasBody: true }, args),
286
288
  // GET /copy-desk/queue — rank: any-builder — GET /copy-desk/queue
287
289
  getCopyDeskQueue: (args) => request("GET", "/copy-desk/queue", { hasBody: false }, args),
288
290
  // GET /copy-desk/strings — rank: any-builder — GET /copy-desk/strings
@@ -0,0 +1,151 @@
1
+ # 0233 — A copy proposal is a task carrying a patch, and the applier is the only writer
2
+
3
+ - **Status:** Accepted
4
+ - **Date:** 2026-09-02
5
+ - **Tasks:** [#1003114](https://cloudbongos.com/builders#/task/1003114) (R03 of goal 1000074, the artist's loop) — builds on [#1003112](https://cloudbongos.com/builders#/task/1003112) (R01, the registry) and [#1003113](https://cloudbongos.com/builders#/task/1003113) (R02, the flag and the queue)
6
+ - **Extends:** [ADR 0081](<redacted>.md) — the repo is the source of truth, with swappable tool adapters that land their output as a committed diff. This applies that contract to TEXT.
7
+ - **Extends:** [ADR 0178](<redacted>.md) — which pinned that `copy_desk_flags` may never hold replacement text, and named this task as the one that would have to keep that true while adding an editing affordance.
8
+ - **Related:** 0083 (module system, ports), 0086 (goals), 0016 (authority resolved server-side), 0162 (a verdict is advisory, never self-executing), 0197 (the ui-design module)
9
+
10
+ ## Context
11
+
12
+ Goal 1000074 asks for two things that pull against each other, and says so in its own text.
13
+
14
+ The artist must be able to **change copy without an engineer** — that is the criterion
15
+ `artist-edits-land-as-tasks`. And there must be **no live CMS**: "every copy change is a claimed,
16
+ graded, shipped GDS task ... never a write path that bypasses the ledger."
17
+
18
+ R02 built the half that needs no reconciliation: the **flag**, a judgement with a pointer. It
19
+ deliberately shipped with no way to say what the copy *should* say, because the obvious place to put
20
+ that — a column beside the flag — is one plausible read away from making the copy desk a copy source.
21
+ ADR 0178 §3 pinned the absence and named the erosion path: not a dramatic "add a CMS" commit, but one
22
+ plausible column, then one plausible read of it, and the ledger rule is dead with nobody having
23
+ decided to kill it.
24
+
25
+ So R03 is the load-bearing one. An affordance that edits, and a ledger that must record every edit,
26
+ are one bad column apart from being incompatible.
27
+
28
+ ## Decision
29
+
30
+ ### 1. The shape is borrowed from the design half, not invented
31
+
32
+ ADR 0081 already solved this exact problem for *form*. A tool authors; `adapter.export()` lands the
33
+ tool's output into the repo as a committed diff under a claimed task, inside a path guard; the repo
34
+ stays the source of truth and the tool never writes to production. The `ui-design` module has run
35
+ that contract for design tokens and component surfaces since ADR 0197.
36
+
37
+ **A copy proposal is that same contract for text.** The artist is the tool, the copy desk is where
38
+ they author, and the proposal is the export. Nothing about this needed a new idea — it needed the
39
+ existing one applied to the other half of the design contract, which is what makes the two halves
40
+ read as one system rather than two features that happen to share a goal.
41
+
42
+ ### 2. The proposal lives on the TASK, as a fenced patch — not in a table
43
+
44
+ `POST /copy-desk/proposals` creates a **GDS task** whose description carries a ```` ```copy-proposal ````
45
+ block: schema, string id, surface, file, line, render path, the current wording and the proposed one.
46
+
47
+ This is the whole answer to the tension, and every part of it is doing work:
48
+
49
+ - **The ledger is where a proposal belongs anyway.** The methodology already requires every change to
50
+ be a claimed task. A proposal is a change that has not happened yet, so the task IS the record —
51
+ not a shadow of one kept somewhere else and reconciled later.
52
+ - **Nothing renders a task description as product copy**, so the wording has no path to a reader that
53
+ does not pass through a claim, a diff and a grade.
54
+ - **A fenced block is readable by the reviewer and parseable by the applier.** The point of a proposal
55
+ is that it is arguable before it lands; a reviewer has to be able to see the change without running
56
+ anything.
57
+ - **`copy-desk` gains no migration.** `tests/copy_no_cms.mjs` asserts the module still has exactly one,
58
+ because the obvious-but-wrong implementation of R03 is a `copy_desk_proposals` table — which is
59
+ ADR 0178's forbidden column with extra steps.
60
+
61
+ The module's manifest gains `consumes: ["lifecycle"]` and calls `lifecycle.createTask` through the
62
+ kernel port. That direction is the safe one and is the point: this module can put a work request
63
+ **into** the ledger, and nothing can pull copy **out** of this module. `provides` stays empty.
64
+
65
+ ### 3. The applier re-derives the position; the registry never stores one
66
+
67
+ `scripts/gds/copy-apply.js` is what whoever claims the task runs. It does **not** trust the patch's
68
+ line number. It re-runs R01's own extractor over the working tree, finds the occurrence there, and
69
+ replaces exactly the characters that string occupies.
70
+
71
+ That required teaching the extractor to record a **source span** alongside every string it finds —
72
+ and deliberately **dropping it from the committed registry**. An offset goes stale the moment anything
73
+ above it in the file changes, while the registry's id is designed to survive precisely that movement;
74
+ a committed offset would be wrong far more often than the id ever is. So the span is a live fact,
75
+ recomputed at the moment of the edit, by the same code that decided the string was copy in the first
76
+ place. One set of matching rules, in one file, used by both the inventory and the applier — which is
77
+ what stops the applier from editing a byte the inventory would never have called copy.
78
+
79
+ The registry's committed bytes are unchanged by this; `--check` and `tests/copy_inventory.mjs` are
80
+ what prove it.
81
+
82
+ ### 4. The placeholder arithmetic is the rule that makes a patch mechanical
83
+
84
+ The registry writes `{…}` where the source has `${expr}`, so an artist edits a readable sentence with
85
+ holes in it. The applier puts the source's **own** expressions back, the k-th hole taking the k-th
86
+ expression — so an artist may move a value within a sentence (`Hello ${name}, welcome back` →
87
+ `Good to see you, ${name} — welcome back`) but may not add or drop one. The count is checked when the
88
+ proposal is made **and again against the real source** at apply time, because the source is what is
89
+ about to be written.
90
+
91
+ That single rule is what turns "a note for an engineer" into "a change that lands with one command",
92
+ which is the difference between meeting the criterion and describing it.
93
+
94
+ ### 5. Every ambiguity is a named refusal, because a wrong edit is worse than no edit
95
+
96
+ The applier refuses, by name and with the reason on one line, when: the wording is already gone
97
+ (`target_gone`), the sentence appears at several sites and the line does not disambiguate
98
+ (`ambiguous_target`), the literal is escaped so no exact span exists (`no_exact_span`), the
99
+ placeholder count does not hold (`placeholder_mismatch`), or the file is outside the surface the
100
+ registry defines (`path_outside_surface` — the direct analogue of the design adapter's path-escape
101
+ guard, derived from R01's own `SURFACES` rather than a second list).
102
+
103
+ None of these means the wording was wrong. They mean a person should look. And after writing, the
104
+ applier regenerates the registry and **checks that what it wrote is what the inventory now reads** —
105
+ an independent read of the file it just edited.
106
+
107
+ ### 6. The command takes no id, because the task cannot know its own
108
+
109
+ A proposal task has to print the exact line that lands it, and its description is composed *before*
110
+ the row exists. Every way of closing that gap is worse than the rule it would paper over — a
111
+ placeholder is a step for the claimer to get wrong, and a second write to patch the description in
112
+ afterwards buys a port widening and a failure mode for a string.
113
+
114
+ So the bare call, `node scripts/gds/copy-apply.js`, acts on **the task the caller is holding**.
115
+ Holding no claim is a named refusal that says to claim it first. The command may only run under a
116
+ claim — that is the whole feature — and a claim already names a task, so the constraint and the
117
+ ergonomics are the same fact. `--task <id>` remains for someone holding several.
118
+
119
+ ### 7. Proposing is open to every rank, and lands in `backlog`
120
+
121
+ Same floor as filing, for ADR 0178 §4's reason: an artist is a craft, not a rank, and a rank floor
122
+ would hide the loop from the people most likely to use it. The quality gates are the required `why`
123
+ (the same judgement floor a flag's reason carries), the placeholder arithmetic, and the fact that the
124
+ target is resolved **server-side from the registry** — a caller cannot describe a change to a
125
+ sentence that does not exist.
126
+
127
+ The task is created at `backlog`, so a proposal is a *request*: it needs the board's ordinary triage
128
+ before anyone can claim it. Landing it `ready` would let anyone with an opinion mint claimable work.
129
+
130
+ ## Consequences
131
+
132
+ - **The write surface of `copy-desk` is now three verbs, not two**, and `tests/copy_no_cms.mjs` says so
133
+ in a diff a reviewer can see — which is exactly what R02 built that list for.
134
+ - **`scripts/gds/copy-apply.js` is the only thing in this feature that writes a surface file**, it is a
135
+ command rather than a route, and the test asserts no file in the module's request path imports it.
136
+ Naming it is fine and required: the task tells its claimer the exact line to run.
137
+ - **The `lifecycle` port gained `findTaskBySourceRef`** so a double-submitted proposal resolves to the
138
+ task it already made. A genuinely different second attempt at the same string is a new task, which
139
+ is right — two people can disagree about the fix, and the board should hold both.
140
+ - **A proposal targets ONE occurrence.** When a string is shown in several places the caller must say
141
+ which, and the refusal points at the duplicates the copy desk already surfaces. Rewording a sentence
142
+ said in seven places is usually the wrong answer to it being said seven times.
143
+ - **A quoted literal containing an escape has no span**, so ~9 of ~1,600 inventoried strings cannot be
144
+ patched by command and are refused by name. Buying them would mean an escape-aware index map — a
145
+ second place for the escaping rules to be wrong, for nine strings.
146
+ - **The copy desk route file moved off `console.error` to the doorway's `api.logger`**, the seam
147
+ builder-settings / dev-box / ideas / government already use. Structured, attributable, and it keeps
148
+ an error's stack instead of stringifying it.
149
+ - **R05 still owns the hard question.** Nothing here gates a ship. Whether an artist's judgement can
150
+ ever block, and how that coexists with ADR 0162, is R05's to decide — a proposal is a work request
151
+ by construction until it does.
@@ -320,4 +320,6 @@ This keeps the decision history honest and traceable.
320
320
  | 0226 | [**One stale-claim timer; reporting degrades where enforcement refuses** ([task 1003476](https://cloudbongos.com/builders#/task/1003476), goal 1000072, criterion `legibility-sort-and-staleness`). "How long may a claim go silent" had FOUR answers and none of them was the project's: the sweeper that actually releases a claim read `GDS_STALE_CLAIM_HOURS` (else 6h); `GET /builders/roster` and `GET /builders/:id/active-claims` each hardcoded 24h in byte-identical blocks; and the two `session_pulses` helpers defaulted to 30 minutes. The 6-vs-24 pair is the harm: the roster is the screen an Archon opens to decide whether to intervene, and for **eighteen hours** after the sweeper had released a claim, stamped its session log `[auto-released:stale]` and returned the task to `ready`, that screen still showed it healthy — while the roster's own comment claimed to "mirror" the sweeper, true of the definition and false of the number. Decision: ONE resolver, `src/stale-timer.js` (a KERNEL file, the `instance-config.js`/`branding.js` category — host config a module reads, never hardcodes), reached by modules through the doorway and by core routes and the CLI directly. Precedence: explicit override (`--threshold-hours`, `?stale_hours=` — a LENS, labelled `query`, never a reconfiguration) → `<PREFIX>_STALE_CLAIM_HOURS` via `instance-config.resolveEnv`, whose legacy prefix chain still honours the live `GDS_STALE_CLAIM_HOURS` so **nothing in a running deployment has to change** (and which is the channel ADR 0190's runner already patches into `web.env`) → **6**, the SWEEP's historical number and deliberately not the roster's 24, because the sweep is the surface that acts and unifying on 24 would have made every project three times more patient with a dead claim under cover of a refactor. Bound 1..720h — the range `/builders/roster` has published since V3.R14; one timer, one bound. Every surface that prints the timer prints its PROVENANCE (`stale_hours_source`, the sweep header, the auto-release note): a number without it is exactly what let 6 and 24 coexist unnoticed. **The fail posture is asymmetric and that is the decision**: the resolver reports an unusable value rather than throwing, then a surface that only READS degrades to the default and names the bad var (500-ing the roster an Archon opened to find the problem would hide the problem), while the SWEEP refuses to run at all (it releases other people's claims and stamps their logs; no reload undoes that) — a surface that only reads may guess and say so, a surface that acts may not guess. Both routes now share one `staleHoursFor(req, res)`; two copies of a literal is how the drift happened twice inside one file. Rejected: a new `config/stale.json` tier (the env route already IS the per-project channel); making 24 the shared default; throwing from the resolver (forces a second reader — the original bug); folding in the boards' 14/30/60-DAY task buckets (a different question — days, about a task, a browsing lens with no enforcement behind it); a DB row + settings UI (a reasonable next step, but the defect was four numbers, not a missing knob — a row added before the readers agreed would have been a fifth).](<redacted>.md) | GDS / claims + ops |
321
321
  | 0232 | [**Rot is derived, not swept; and `water` is the only new verb** ([task 1003279](https://cloudbongos.com/builders#/task/1003279) · goal 1000072, criterion `legibility-sort-and-staleness`). The inbox half [ADR 0226](<redacted>.md) split out and left open. Owner decision (2026-08-25): a goal or task past the timer raises a **needs-attention line** — not an automatic status change (it mutates real work with no human in the loop, and a wrong timer then silently freezes it) and not a nightly digest (a notification is not an action, and one you scroll past is indistinguishable from the silent decay it was meant to fix). The build note asked for an idempotent poller; the decision is that **there is no poller and no items table** — a line is a `SELECT` comparing the thing's rot clock to the timer, so "exactly one item per stale thing, not one per sweep" is true by arithmetic rather than by deduplication code that must be written correctly and then stay correct, and clearing falls out for free (an edit moves `updated_at`, a decision moves `status`, watering moves the mark). A stored item would have been a cache of a query whose inputs it could not observe. **The exclusions are the load-bearing part**: rot is work nobody picked up (`backlog`/`ready`, and `open` goals), and `active` is deliberately excluded because the claim-silence timer already reports it in hours — listing it here in days would put one rot in two inboxes under two numbers, the 6h-vs-24h split one tier up; `blocked`, `completed`/`confirmed` and the terminal states are excluded for the same reason, each naming the surface that owns it. The clock is **not a new definition of touched** — it is `task-lib.js`'s published `max(updated_at, shipped_at)` (task 1003278) plus a water mark, pinned by a test asserting the domain expression appears verbatim in migration `core_231`'s index expressions, because drift there is silent. The timer is a **second** timer and defends itself as a different question (days vs hours, DB vs env, asks a human vs releases a claim), stored in a new unseeded generic `project_settings` table — no row means "never configured", which is what lets a settings page tell that apart from "set to the default" — with ADR 0226's precedence and its reporting-degrades fail posture, and an unreachable DB explicitly not treated as a bad value. Three of the four verbs (**prioritise/prune/kill**) reuse `promote`/`demote`/`abandon`/`archive`, the task-1003066 precedent, so they add no permission; only `water` is new, gated on ownership OR `builder.roster.read` inside the handler (a creator holding no atom must still get through). Verbs are **served with each row** rather than hardcoded, since a goal legitimately gets two where a task gets four. Rejected: an items table, a sweep, a digest, auto-demotion, and a fourth verb on goals that would render a dead button.](<redacted>.md) | GDS / legibility + hall |
322
322
 
323
+ | 0233 | [**A copy proposal is a task carrying a patch, and the applier is the only writer** ([task 1003114](https://cloudbongos.com/builders#/task/1003114) · goal 1000074, criterion `artist-edits-land-as-tasks`). Goal 1000074 asks for two things that pull against each other in its own text: the artist must change copy WITHOUT an engineer, and there must be NO LIVE CMS because every copy change is a claimed, graded, shipped task. R02 shipped the half that needs no reconciliation (the flag) and [ADR 0178](<redacted>.md) §3 pinned the absence of any column that could hold replacement text, naming this task as the one that would have to keep it true. Decisions: (1) the shape is **borrowed from the design half, not invented** — ADR 0081 already has a tool authoring and an `adapter.export()` landing the output as a committed diff under a claim inside a path guard, so the artist is the tool, the copy desk is where they author, and the proposal is the export. (2) A proposal is a **TASK carrying a fenced `copy-proposal` patch**, never a row: the ledger is where a proposal belongs anyway, nothing renders a task description as product copy, a reviewer can read the change without running anything, and `copy-desk` gains NO migration (the obvious-but-wrong R03 is a `copy_desk_proposals` table, which is the forbidden column with extra steps). The module gains `consumes: ["lifecycle"]` and calls `createTask` through the port — it can put a request INTO the ledger and nothing can pull copy OUT of it. (3) `scripts/gds/copy-apply.js` **re-derives the position** rather than trusting the patch, which required teaching R01s extractor to record a source SPAN and deliberately dropping it from the committed registry: an offset goes stale the moment anything above it changes, while the id is designed to survive exactly that movement, so the span is recomputed at the moment of the edit by the same code that decided the string was copy. (4) The **placeholder arithmetic** is what makes a patch mechanical rather than a note for an engineer — the applier puts the sources own `${expr}` back, k-th hole to k-th expression, so an artist may MOVE a value within a sentence but never add or drop one, checked at proposal time and again against the real source. (5) Every ambiguity is a **named refusal** (`target_gone`, `ambiguous_target`, `no_exact_span`, `placeholder_mismatch`, `path_outside_surface` — the design adapters path guard, derived from R01s own SURFACES), because a wrong edit is worse than no edit; after writing, the applier regenerates the registry and checks that what it wrote is what the inventory now reads. (6) Proposing is **open to every rank** (an artist is a craft, not a rank) and lands at `backlog`, so a proposal is a request that passes ordinary triage. Rejected: a proposals table, a column beside the flag, trusting the patchs line number, an escape-aware index map to buy spans for nine strings, auto-closing the flag a proposal answers.](<redacted>.md) | copy-desk / the artist loop |
324
+
323
325
  > ⚠️ **Numbering collisions are now machine-enforced, not narrated.** Twenty numbers are shared by two ADRs each — assigned in parallel sessions before anything checked. The files keep their filenames (renumbering would break every existing citation, and a number once assigned is never reused), and this table disambiguates each pair as `NNNN-a` / `NNNN-b`. The authoritative list is `LEGACY_DUPLICATE_ADRS` in [`scripts/gds/adr-namespace.js`](../../scripts/gds/adr-namespace.js), frozen by exact filename: a **new** collision — or a third file joining a legacy number, or a rename of either half — hard-fails the `unit` CI gate, as does an index row that points at no file or an ADR with no row. The list may only shrink. This paragraph used to narrate the collisions one by one and had fallen eight behind reality; see [ADR 0195](<redacted>.md) for why the check exists and why the pairs are grandfathered rather than renumbered.
@@ -3923,6 +3923,55 @@
3923
3923
  ]
3924
3924
  }
3925
3925
  },
3926
+ "/copy-desk/proposals": {
3927
+ "post": {
3928
+ "operationId": "post_copy_desk_proposals",
3929
+ "tags": [
3930
+ "copy-desk"
3931
+ ],
3932
+ "summary": "POST /copy-desk/proposals",
3933
+ "description": "------------------------------------------------------------------------- POST /copy-desk/proposals — an artist proposes new wording, and it becomes a TASK (task 1003114 / R03 of goal 1000074). Body: { string_id, proposed_text, why, file?, line?, flag_id? } rank: any signed-in builder — the same floor as filing, for the same reason (an artist is a craft, not a rank; see the header). THIS IS THE ROUTE THE NON-GOAL IS ABOUT, so read what it does NOT do. It writes no file. It stores no wording in any table of this module — ADR 0178 §3 pins that copy_desk_flags can never hold replacement text, and this route's whole design is what makes that pin survivable rather than merely strict. What it produces is a TASK carrying a patch, in the ledger every change already has to pass through, which nothing renders from. The wording reaches a person only after somebody claims that task, runs `node scripts/gds/copy-apply.js --task <id>`, and ships the resulting diff through the normal pipeline with the grader reading it. The shape is borrowed, not invented: it is `adapter.export()` from the design half (ADR 0081) applied to text. See modules/copy-desk/proposals.js. -------------------------------------------------------------------------\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
3934
+ "x-rank": "any-builder",
3935
+ "x-source": "modules/copy-desk/routes/copy-desk.js",
3936
+ "requestBody": {
3937
+ "required": true,
3938
+ "content": {
3939
+ "application/json": {
3940
+ "schema": {
3941
+ "$ref": "#/components/schemas/PostCopyDeskProposalsRequest"
3942
+ }
3943
+ }
3944
+ },
3945
+ "x-validated": true
3946
+ },
3947
+ "responses": {
3948
+ "200": {
3949
+ "description": "Success.",
3950
+ "content": {
3951
+ "application/json": {
3952
+ "schema": {
3953
+ "$ref": "#/components/schemas/PostCopyDeskProposalsResponse"
3954
+ }
3955
+ }
3956
+ }
3957
+ },
3958
+ "400": {
3959
+ "$ref": "#/components/responses/ValidationFailed"
3960
+ },
3961
+ "401": {
3962
+ "$ref": "#/components/responses/Unauthorized"
3963
+ },
3964
+ "403": {
3965
+ "$ref": "#/components/responses/Forbidden"
3966
+ }
3967
+ },
3968
+ "security": [
3969
+ {
3970
+ "builderSession": []
3971
+ }
3972
+ ]
3973
+ }
3974
+ },
3926
3975
  "/copy-desk/queue": {
3927
3976
  "get": {
3928
3977
  "operationId": "get_copy_desk_queue",
@@ -18804,6 +18853,60 @@
18804
18853
  "flag"
18805
18854
  ]
18806
18855
  },
18856
+ "PostCopyDeskProposalsRequest": {
18857
+ "type": "object",
18858
+ "properties": {
18859
+ "string_id": {
18860
+ "type": "string",
18861
+ "minLength": 1,
18862
+ "maxLength": 64
18863
+ },
18864
+ "proposed_text": {
18865
+ "type": "string",
18866
+ "minLength": 1
18867
+ },
18868
+ "why": {
18869
+ "type": "string",
18870
+ "minLength": 1
18871
+ },
18872
+ "file": {
18873
+ "type": "string",
18874
+ "maxLength": 400
18875
+ },
18876
+ "line": {
18877
+ "type": "integer",
18878
+ "minimum": 1
18879
+ },
18880
+ "flag_id": {
18881
+ "type": "integer",
18882
+ "minimum": 1
18883
+ }
18884
+ },
18885
+ "required": [
18886
+ "string_id",
18887
+ "proposed_text",
18888
+ "why"
18889
+ ],
18890
+ "additionalProperties": false
18891
+ },
18892
+ "PostCopyDeskProposalsResponse": {
18893
+ "type": "object",
18894
+ "properties": {
18895
+ "ok": {
18896
+ "type": "boolean"
18897
+ },
18898
+ "task": {},
18899
+ "created": {
18900
+ "type": "boolean"
18901
+ },
18902
+ "patch": {}
18903
+ },
18904
+ "required": [
18905
+ "ok",
18906
+ "task",
18907
+ "created"
18908
+ ]
18909
+ },
18807
18910
  "PostCostRequest": {
18808
18911
  "type": "object",
18809
18912
  "properties": {
@@ -21649,9 +21752,9 @@
21649
21752
  "description": "A required dependency/feature is not configured or is temporarily down."
21650
21753
  }
21651
21754
  },
21652
- "x-endpoint-count": 344,
21653
- "x-schema-count": 375,
21755
+ "x-endpoint-count": 345,
21756
+ "x-schema-count": 377,
21654
21757
  "x-undocumented-bodies": 9,
21655
- "x-response-schemas": 256,
21758
+ "x-response-schemas": 257,
21656
21759
  "x-generated-by": "scripts/gds/gen-api-docs.js"
21657
21760
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Bongos API reference
4
4
 
5
- > **Generated from the live route files** — the route file is authoritative. 344 endpoints across 56 route files.
5
+ > **Generated from the live route files** — the route file is authoritative. 345 endpoints across 56 route files.
6
6
  > Machine-readable spec: [`docs/api/openapi.json`](api/openapi.json) (OpenAPI 3.1). Rendered docs site: **`/docs`** (e.g. `cloudbongos.com/docs`).
7
7
 
8
8
  Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>.md)): `public` < `any-builder` < `metic+archon` < `archon`.
@@ -166,12 +166,13 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
166
166
  | GET | `/api/bongos/connections/degree/:handle` | `any-builder` | — | the degree label between the CALLER and one handle, plus the mutuals it was computed from (own-scope read, D4 §4). |
167
167
  | GET | `/api/bongos/connections/requests` | `any-builder` | — | MY pending requests, both directions (own data). |
168
168
 
169
- ## `copy-desk` (4)
169
+ ## `copy-desk` (5)
170
170
 
171
171
  | Method | Path | Rank | Body | Description |
172
172
  |---|---|---|---|---|
173
173
  | POST | `/api/bongos/copy-desk/flags` | `any-builder` | `scope`, `surface`, `string_id`, `reason`, `kind` | ------------------------------------------------------------------------- POST /copy-desk/flags — file a flag. |
174
174
  | POST | `/api/bongos/copy-desk/flags/:id/close` | `any-builder` | `status`, `resolution_note`, `resolved_task_id` | ------------------------------------------------------------------------- POST /copy-desk/flags/:id/close — resolve or dismiss. |
175
+ | POST | `/api/bongos/copy-desk/proposals` | `any-builder` | `string_id`, `proposed_text`, `why`, `file`, `line`, `flag_id` | ------------------------------------------------------------------------- POST /copy-desk/proposals — an artist proposes new wording, and… |
175
176
  | GET | `/api/bongos/copy-desk/queue` | `any-builder` | — | ------------------------------------------------------------------------- GET /copy-desk/queue — the artist's working list. |
176
177
  | GET | `/api/bongos/copy-desk/strings` | `any-builder` | — | ------------------------------------------------------------------------- GET /copy-desk/strings — search the inventory, to find somethin… |
177
178
 
@@ -4,13 +4,13 @@
4
4
  > Deterministic on a given tree — regenerate and commit rather than hand-editing.
5
5
  > Machine-readable twin: [`copy-registry.json`](copy-registry.json).
6
6
 
7
- **1600 strings** across 3 surfaces · 1391 distinct pieces of copy · 1600 certain, 0 heuristic · 150 shown in more than one place.
7
+ **1612 strings** across 3 surfaces · 1403 distinct pieces of copy · 1612 certain, 0 heuristic · 150 shown in more than one place.
8
8
 
9
9
  ## Surfaces
10
10
 
11
11
  | surface | lives at | files | strings |
12
12
  |---|---|---|---|
13
- | `builders-hall` | `modules/hall-ui/public` | 73 | 1157 |
13
+ | `builders-hall` | `modules/hall-ui/public` | 73 | 1169 |
14
14
  | `status-dashboard` | `modules/status-ui/public` | 4 | 98 |
15
15
  | `landing` | `modules/public-landing/public` | 5 | 345 |
16
16
 
@@ -78,7 +78,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
78
78
  | `563d29ed86d8` | 2x | Bulk actions | modules/hall-ui/public/goals.html:172, modules/hall-ui/public/work.html:129 |
79
79
  | … | | _110 more in the JSON registry_ | |
80
80
 
81
- ## `builders-hall` — 1157 strings
81
+ ## `builders-hall` — 1169 strings
82
82
 
83
83
  | id | conf | text | where |
84
84
  |---|---|---|---|
@@ -116,7 +116,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
116
116
  | `3e5a62bc4887` | certain | A GitHub username is needed. | `modules/hall-ui/public/hall-render.js:361` |
117
117
  | `3e5a62bc4887` | certain | A GitHub username is needed. | `modules/hall-ui/public/watch.js:279` |
118
118
  | `2c23271d5d71` | certain | A device is asking to act as you | `modules/hall-ui/public/pair.html:32` |
119
- | `a22df5ebbd5e` | certain | A flag needs a reason an artist can act on. What is wrong with it, and what would better look like? | `modules/hall-ui/public/copy-desk.js:334` |
119
+ | `a22df5ebbd5e` | certain | A flag needs a reason an artist can act on. What is wrong with it, and what would better look like? | `modules/hall-ui/public/copy-desk.js:372` |
120
120
  | `634738746b81` | certain | A fresh box has a | `modules/hall-ui/public/harbor.js:125` |
121
121
  | `df5eb70b5a05` | certain | A higher rank. Its details are hidden until you reach the rank directly below it. | `modules/hall-ui/public/ranks.js:125` |
122
122
  | `2e17d7e236d9` | certain | A module built here can be submitted upstream for review, so it can join the shared catalog with your name and | `modules/hall-ui/public/modules.html:71` |
@@ -138,7 +138,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
138
138
  | `62eded408097` | certain | Add {…} | `modules/hall-ui/public/profile-nudge.js:68` |
139
139
  | `8b736e365191` | certain | All builders | `modules/hall-ui/public/hall-render.js:1132` |
140
140
  | `b5e03617b319` | certain | All goals | `modules/hall-ui/public/goals.html:53` |
141
- | `bea7817e57a4` | certain | All surfaces | `modules/hall-ui/public/copy-desk.js:263` |
141
+ | `bea7817e57a4` | certain | All surfaces | `modules/hall-ui/public/copy-desk.js:301` |
142
142
  | `627c683a5e5d` | certain | All time | `modules/hall-ui/public/hall-widgets.js:130` |
143
143
  | `b38c01691857` | certain | All your work → | `modules/hall-ui/public/hall-widgets.js:110` |
144
144
  | `e99cc5d37404` | certain | Already a member? | `modules/hall-ui/public/hall-render.js:330` |
@@ -227,8 +227,8 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
227
227
  | `512ab32e3bf2` | certain | Close box | `modules/hall-ui/public/box-panel.js:226` |
228
228
  | `70f6a704d504` | certain | Close it | `modules/hall-ui/public/copy-desk.html:140` |
229
229
  | `d12a2fb5730d` | certain | Close this flag | `modules/hall-ui/public/copy-desk.html:116` |
230
- | `3d148e19e03a` | certain | Closed. | `modules/hall-ui/public/copy-desk.js:393` |
231
- | `2654dc3ded21` | certain | Closing is a judgement too — say what you decided, so the next person does not re-litigate it. | `modules/hall-ui/public/copy-desk.js:378` |
230
+ | `3d148e19e03a` | certain | Closed. | `modules/hall-ui/public/copy-desk.js:510` |
231
+ | `2654dc3ded21` | certain | Closing is a judgement too — say what you decided, so the next person does not re-litigate it. | `modules/hall-ui/public/copy-desk.js:495` |
232
232
  | `f8a914076098` | certain | Closing the terminal tab keeps your session running — reopen to resume right where you left off. To shut the b | `modules/hall-ui/public/box-panel.js:226` |
233
233
  | `cfe0f4c4fcc2` | certain | Closing your box… (a few seconds) | `modules/hall-ui/public/box-panel.js:265` |
234
234
  | `cfe0f4c4fcc2` | certain | Closing your box… (a few seconds) | `modules/hall-ui/public/box-panel.js:370` |
@@ -395,6 +395,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
395
395
  | `647513e4838a` | certain | Everything this builder has shipped. Seen one craft at a time, with goals and ideas, under | `modules/hall-ui/public/profile.html:108` |
396
396
  | `efdb46f76b02` | certain | Everything this instance can run: what the core ships, what this project built, and who keeps each one true as | `modules/hall-ui/public/modules.html:32` |
397
397
  | `d629e152f074` | certain | Expires {…} · refreshes on use. | `modules/hall-ui/public/settings.js:992` |
398
+ | `0c4632f983db` | certain | File the change | `modules/hall-ui/public/copy-desk.html:173` |
398
399
  | `536a7e99640a` | certain | File the flag | `modules/hall-ui/public/copy-desk.html:108` |
399
400
  | `077cf99f49f6` | certain | File this idea | `modules/hall-ui/public/ideas.html:90` |
400
401
  | `1def0e7c716e` | certain | Find something to flag | `modules/hall-ui/public/copy-desk.html:64` |
@@ -407,8 +408,8 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
407
408
  | `78107a76a755` | certain | Five diagrams that show, at a glance, how work flows through {{worldName}}'s development system. | `modules/hall-ui/public/diagrams.html:33` |
408
409
  | `18ab1be9d3ad` | certain | Five questions, and a score that counts how much of each you actually answered. | `modules/hall-ui/public/ideas.js:448` |
409
410
  | `b9267033b1f1` | certain | Flag this copy | `modules/hall-ui/public/copy-desk.html:83` |
410
- | `82149b1deacd` | certain | Flag this… | `modules/hall-ui/public/copy-desk.js:146` |
411
- | `448d00771dc8` | certain | Flagged. It is in the queue. | `modules/hall-ui/public/copy-desk.js:353` |
411
+ | `82149b1deacd` | certain | Flag this… | `modules/hall-ui/public/copy-desk.js:164` |
412
+ | `448d00771dc8` | certain | Flagged. It is in the queue. | `modules/hall-ui/public/copy-desk.js:391` |
412
413
  | `6149e9ca69f7` | certain | Fleet summary | `modules/hall-ui/public/harbor.html:44` |
413
414
  | `617f13fa3fdb` | certain | Flip the switch. | `modules/hall-ui/public/hall-render.js:744` |
414
415
  | `42cee5cb3584` | certain | Fold this idea into # | `modules/hall-ui/public/task-detail.js:457` |
@@ -494,6 +495,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
494
495
  | `2bfc5a50ce88` | certain | Jump to a page or record | `modules/hall-ui/public/shell.js:344` |
495
496
  | `eba610d25523` | certain | Jump to… | `modules/hall-ui/public/shell.js:346` |
496
497
  | `bc779353aa0c` | certain | Karma — peer recognition and approvals, a separate counter from {…}. | `modules/hall-ui/public/profile.js:65` |
498
+ | `de0e1590667f` | certain | Keep all {…} {…} — they are the values the code drops in, and the change cannot land without them. | `modules/hall-ui/public/copy-desk.js:442` |
497
499
  | `32d25ef2182d` | certain | Keep box | `modules/hall-ui/public/box-panel.js:164` |
498
500
  | `fbe40b5818a3` | certain | Key removed. | `modules/hall-ui/public/settings.js:1193` |
499
501
  | `93c17262bacf` | certain | Key storage is being set up by an Archon — check back shortly. {…} | `modules/hall-ui/public/settings.js:1072` |
@@ -598,6 +600,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
598
600
  | `2c19f41704b9` | certain | Not yet available — {{worldName}} | `modules/hall-ui/public/not-ready.html:6` |
599
601
  | `571b9b22dbda` | certain | Not yet ready — blocked by dependencies or awaiting promotion. These become claimable when their blockers ship | `modules/hall-ui/public/work.html:98` |
600
602
  | `0c59d118db9b` | certain | Note (optional) | `modules/hall-ui/public/task-detail.js:460` |
603
+ | `cd652d04d616` | certain | Nothing changes yet. This files a task carrying your wording; whoever claims it lands the diff, and it ships g | `modules/hall-ui/public/copy-desk.html:168` |
601
604
  | `fec5d78497e8` | certain | Nothing in the backlog right now. | `modules/hall-ui/public/work.js:1055` |
602
605
  | `2c6e0dfbd781` | certain | Nothing is waiting on you. Requests reach you by your craft — set yours in | `modules/hall-ui/public/collab.html:44` |
603
606
  | `2ec0fbb20a42` | certain | Nothing is waiting. A spark appears here when its author offers a quick idea up for development. | `modules/hall-ui/public/ideas.js:365` |
@@ -669,6 +672,8 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
669
672
  | `dd4bc23c7759` | certain | Prompt for #{…} copied to your clipboard. | `modules/hall-ui/public/work.js:550` |
670
673
  | `cd7dc83871b6` | certain | Prompt for #{…} copied. | `modules/hall-ui/public/goals-page.js:373` |
671
674
  | `cd7dc83871b6` | certain | Prompt for #{…} copied. | `modules/hall-ui/public/work.js:561` |
675
+ | `fe7f3488fda8` | certain | Propose new wording | `modules/hall-ui/public/copy-desk.html:152` |
676
+ | `abee9a18325d` | certain | Propose wording… | `modules/hall-ui/public/copy-desk.js:179` |
672
677
  | `3f85bdc47099` | certain | Protected module scope — join by request; an Archon admits members | `modules/hall-ui/public/goals-render.js:792` |
673
678
  | `e5c292c8b361` | certain | Provides (seams) | `modules/hall-ui/public/atlas.js:288` |
674
679
  | `b9c6def6b120` | certain | Rank set to {…}. | `modules/hall-ui/public/watch.js:625` |
@@ -739,8 +744,9 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
739
744
  | `11486793742e` | certain | Saves the moment you toggle; applies from your next session. | `modules/hall-ui/public/settings.html:284` |
740
745
  | `e8e4e49fe8f4` | certain | Saves when you press Save; shows everywhere at once. | `modules/hall-ui/public/settings.html:88` |
741
746
  | `42cbc20a6ec8` | certain | Saves when you press Save; your next | `modules/hall-ui/public/settings.html:136` |
747
+ | `fc9d783ed0f5` | certain | Say why it is better. Whoever reviews this decides it from your reason, without asking you. | `modules/hall-ui/public/copy-desk.js:446` |
742
748
  | `46711f7bc3df` | certain | Search every string the product shows a person, by what it says or the file it lives in. | `modules/hall-ui/public/copy-desk.html:65` |
743
- | `5d2ba8e72fa1` | certain | Search failed. | `modules/hall-ui/public/copy-desk.js:305` |
749
+ | `5d2ba8e72fa1` | certain | Search failed. | `modules/hall-ui/public/copy-desk.js:343` |
744
750
  | `373a7639bcb9` | certain | Sections in this primer | `modules/hall-ui/public/primer.html:52` |
745
751
  | `00ca4e3c7c7d` | certain | Sections in this reference | `modules/hall-ui/public/drachmae.html:49` |
746
752
  | `ab8af800f2bb` | certain | Security reports | `modules/hall-ui/public/watch.html:47` |
@@ -825,6 +831,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
825
831
  | `44579eef0c8d` | certain | Tasks — {{worldName}} | `modules/hall-ui/public/work.html:6` |
826
832
  | `2f4f047dc1b4` | certain | Text size | `modules/hall-ui/public/shell.js:349` |
827
833
  | `545c59c1deb7` | certain | That is the session you are using right now. Ending it signs you out of this page. | `modules/hall-ui/public/settings-sessions.js:74` |
834
+ | `282c3fa2d456` | certain | That is the wording it already has. Change something, or file a flag instead if you only want to mark it. | `modules/hall-ui/public/copy-desk.js:438` |
828
835
  | `c0e28f30524d` | certain | That project's own standing, as reported by that project. | `modules/hall-ui/public/profile.js:285` |
829
836
  | `e99d4a811e92` | certain | That request is no longer available. | `modules/hall-ui/public/profile-connections.js:270` |
830
837
  | `50198c465225` | certain | The 16-value rank enum in three bands, which ranks are live, and the permissions each rank holds. | `modules/hall-ui/public/diagrams.html:59` |
@@ -871,7 +878,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
871
878
  | `7d016f4a65bf` | certain | The system is mid-build. These diagrams deliberately separate what stands today from what is yet planned, so t | `modules/hall-ui/public/diagrams.html:40` |
872
879
  | `198ba9353bf6` | certain | The task feed did not answer. Nothing on the record has changed, only the read failed. Try again in a moment, | `modules/hall-ui/public/task.js:632` |
873
880
  | `34555793f33e` | certain | The task list could not be loaded right now. | `modules/hall-ui/public/work.js:1215` |
874
- | `a42a37c43bfb` | certain | The words this project shows people — what has been flagged, and what nobody has looked at. | `modules/hall-ui/public/copy-desk.js:290` |
881
+ | `a42a37c43bfb` | certain | The words this project shows people — what has been flagged, and what nobody has looked at. | `modules/hall-ui/public/copy-desk.js:328` |
875
882
  | `bcdbded93d4e` | certain | The work board failed to load. Reload to try again. | `modules/hall-ui/public/work.js:1401` |
876
883
  | `25794c439321` | certain | Then copy the project onto your machine and run the one-shot bootstrap (it checks your setup, installs depende | `modules/hall-ui/public/hall-render.js:811` |
877
884
  | `df316c3484de` | certain | Then drive it however you like: | `modules/hall-ui/public/hall-render.js:744` |
@@ -897,6 +904,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
897
904
  | `9a685b01e2c6` | certain | This page opens at a higher rank. | `modules/hall-ui/public/watch.html:40` |
898
905
  | `87f1c2e07790` | certain | This page unlocks as you advance. | `modules/hall-ui/public/not-ready.html:26` |
899
906
  | `69e8beced2ff` | certain | This sends the idea back to its author. Say why, and which section. | `modules/hall-ui/public/government.js:962` |
907
+ | `09256febc32d` | certain | This sentence has {…} {…} where a value is dropped in when it is shown. | `modules/hall-ui/public/copy-desk.js:422` |
900
908
  | `c9e50a67988a` | certain | This task has shipped and its credits have landed. Neither Ship it nor Confirm is available. | `modules/hall-ui/public/task.js:568` |
901
909
  | `7217c4f1769d` | certain | This will {…} | `modules/hall-ui/public/harbor.js:125` |
902
910
  | `2ad5823d2cd7` | certain | Three dials, all settings rather than code: | `modules/hall-ui/public/government.html:55` |
@@ -933,6 +941,8 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
933
941
  | `70dc106494cf` | certain | What is wrong with it, and what would better look like? | `modules/hall-ui/public/copy-desk.html:101` |
934
942
  | `ae44a375e814` | certain | What it unlocks | `modules/hall-ui/public/ranks.js:109` |
935
943
  | `00dc1f0f83e1` | certain | What kind of wrong? | `modules/hall-ui/public/copy-desk.html:87` |
944
+ | `9a85f2c19b48` | certain | What should it say? | `modules/hall-ui/public/copy-desk.html:156` |
945
+ | `2dd116d2d817` | certain | What the current wording gets wrong, and what yours does about it. | `modules/hall-ui/public/copy-desk.html:166` |
936
946
  | `5f2b989bbf6a` | certain | What this builder has done, seen one craft at a time. | `modules/hall-ui/public/profile.html:134` |
937
947
  | `32d675a2b022` | certain | What this rank is for (optional) | `modules/hall-ui/public/government.html:92` |
938
948
  | `3dade88f0584` | certain | What your box would cost: | `modules/hall-ui/public/box-panel.js:235` |
@@ -947,6 +957,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
947
957
  | `fe85463560e0` | certain | Who builds on this project, ranked by work shipped. This project's own ledger — unlike the cross-project direc | `modules/hall-ui/public/roster.html:28` |
948
958
  | `7dbdb882e967` | certain | Who can find you. Your profile page can be public or private, and recruiters looking for builders are allowed | `modules/hall-ui/public/settings.html:111` |
949
959
  | `045c629729b7` | certain | Who’s on this goal, and what they’ve contributed to it. | `modules/hall-ui/public/goals.html:198` |
960
+ | `62ab2e60f629` | certain | Why is this better? | `modules/hall-ui/public/copy-desk.html:164` |
950
961
  | `fbd8a815b808` | certain | Why — the reason the author will act on | `modules/hall-ui/public/government.js:962` |
951
962
  | `a3ac116b499a` | certain | Why? | `modules/hall-ui/public/copy-desk.html:99` |
952
963
  | `0dcb485477b6` | certain | Windows (ARM) | `modules/hall-ui/public/hall-render.js:744` |
@@ -957,6 +968,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
957
968
  | `0e6b8e8f26cc` | certain | Works completed | `modules/hall-ui/public/profile.html:140` |
958
969
  | `52e5fe3c6075` | certain | Write a Full Idea | `modules/hall-ui/public/ideas.html:52` |
959
970
  | `7664f4373d1f` | certain | Write one | `modules/hall-ui/public/idea.js:258` |
971
+ | `47f61e576454` | certain | Write the wording you want instead. | `modules/hall-ui/public/copy-desk.js:436` |
960
972
  | `c368b56ecf40` | certain | Xenos builders start with a focused set of pages — ship your first | `modules/hall-ui/public/not-ready.html:31` |
961
973
  | `b6b0fcb2bcee` | certain | You already have a pending request or invitation for this goal. | `modules/hall-ui/public/goals-render.js:1059` |
962
974
  | `52e93bbc07a6` | certain | You are already a member of this goal. | `modules/hall-ui/public/goals-render.js:1060` |
@@ -1040,7 +1052,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
1040
1052
  | `df941da50c7c` | certain | fell short | `modules/hall-ui/public/builders.js:568` |
1041
1053
  | `31735e119a04` | certain | first vote | `modules/hall-ui/public/government.js:584` |
1042
1054
  | `9563bd9f4971` | certain | first — your username is all we need. | `modules/hall-ui/public/hall-render.js:278` |
1043
- | `2e111f3a4912` | certain | flagged by | `modules/hall-ui/public/copy-desk.js:130` |
1055
+ | `2e111f3a4912` | certain | flagged by | `modules/hall-ui/public/copy-desk.js:148` |
1044
1056
  | `1874087e6d87` | certain | for Mac or Windows, sign in with your | `modules/hall-ui/public/hall-render.js:744` |
1045
1057
  | `836646d95f03` | certain | for a link that runs this session in Claude's own web/app interface, the same UI you'd use on a computer. | `modules/hall-ui/public/hall-render.js:744` |
1046
1058
  | `50eb308666b2` | certain | for version-level progress and goals. | `modules/hall-ui/public/work.html:84` |
@@ -1110,7 +1122,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
1110
1122
  | `f83f0a9d6b6a` | certain | recorded, on the Operations tab | `modules/hall-ui/public/watch.html:49` |
1111
1123
  | `26da1df6fd4a` | certain | reflects it. | `modules/hall-ui/public/settings.html:136` |
1112
1124
  | `594962dc9ec3` | certain | s into the view containers below (falling back to | `modules/hall-ui/public/index.html:52` |
1113
- | `2a42549b34ae` | certain | said, when flagged: | `modules/hall-ui/public/copy-desk.js:120` |
1125
+ | `2a42549b34ae` | certain | said, when flagged: | `modules/hall-ui/public/copy-desk.js:124` |
1114
1126
  | `9bec9eb4829e` | certain | scope: {…} | `modules/hall-ui/public/goals-render.js:774` |
1115
1127
  | `a6f55d7aa6f7` | certain | shipped (live) | `modules/hall-ui/public/roadmap.js:273` |
1116
1128
  | `026ba1289d4d` | certain | shipped · | `modules/hall-ui/public/goals-render.js:696` |
@@ -1119,7 +1131,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
1119
1131
  | `0215b2cdca8c` | certain | submit for review | `modules/hall-ui/public/modules.js:132` |
1120
1132
  | `da30b952a60b` | certain | system / unattributed | `modules/hall-ui/public/goals-page.js:249` |
1121
1133
  | `da30b952a60b` | certain | system / unattributed | `modules/hall-ui/public/work.js:498` |
1122
- | `a6e803a31a4d` | certain | task #{…} | `modules/hall-ui/public/copy-desk.js:101` |
1134
+ | `a6e803a31a4d` | certain | task #{…} | `modules/hall-ui/public/copy-desk.js:105` |
1123
1135
  | `4d8ee7d68974` | certain | task {…} | `modules/hall-ui/public/gate.js:142` |
1124
1136
  | `4d8ee7d68974` | certain | task {…} | `modules/hall-ui/public/hall-render.js:599` |
1125
1137
  | `4d8ee7d68974` | certain | task {…} | `modules/hall-ui/public/idea.js:207` |