@bongos/core 1.19.627 → 1.19.629
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 +26 -21
- package/.claude/skills/ideate/SKILL.md +11 -0
- package/.gitattributes +11 -1
- package/docs/adr/0271-the-ideator-surface-is-two-halves.md +233 -0
- package/docs/adr/README.md +1 -0
- package/docs/module-api-changelog.md +4 -0
- package/docs/recipes/windows-builders.md +1 -0
- package/modules/builder-settings/interaction-prefs.js +28 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/copy-inventory.js +28 -2
- package/scripts/gds/discipline-modes.json +1 -1
- package/src/module-api.js +1 -1
- package/tests/copy_inventory.mjs +0 -0
- package/tests/git_merge_regen.mjs +16 -1
- package/tests/interaction_prefs.mjs +43 -1
package/.bongos-core.json
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.19.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.629",
|
|
6
|
+
"core_contract": "1.19.629",
|
|
7
|
+
"source_commit": "80bf853bd558fa5134bd5bddcb0e4804579fc27e",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-
|
|
9
|
+
"built_at": "2026-09-09T22:26:18.376Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 466,
|
|
13
13
|
"agent_docs_stubbed": 24,
|
|
14
14
|
"functional_verbatim": 2098,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2588,
|
|
20
|
+
"tree_sha256": "c5bd187e5ed644428b20c65f6fee6c2b6b122ae4952a3e36e19b84cfa2e4d722",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/backlog-review/SKILL.md",
|
|
@@ -192,7 +192,7 @@
|
|
|
192
192
|
{
|
|
193
193
|
"path": ".claude/skills/ideate/SKILL.md",
|
|
194
194
|
"mode": "0000644",
|
|
195
|
-
"sha256": "
|
|
195
|
+
"sha256": "e4279d87f1f4bad8566bb69ea82278bde3a725a3879f972ad3cb34bb2cef9de5"
|
|
196
196
|
},
|
|
197
197
|
{
|
|
198
198
|
"path": ".claude/skills/merge-mode/SKILL.md",
|
|
@@ -332,7 +332,7 @@
|
|
|
332
332
|
{
|
|
333
333
|
"path": ".gitattributes",
|
|
334
334
|
"mode": "0000644",
|
|
335
|
-
"sha256": "
|
|
335
|
+
"sha256": "83c94b055dc619fdbf3bc1142fc5357810d901e4200644f1fb399e6939571c5f"
|
|
336
336
|
},
|
|
337
337
|
{
|
|
338
338
|
"path": "CLAUDE.md",
|
|
@@ -1874,10 +1874,15 @@
|
|
|
1874
1874
|
"mode": "0000644",
|
|
1875
1875
|
"sha256": "a16ee8bb0d163fec8ffce252e165de4010954e45a7f61bc205163c70077451ee"
|
|
1876
1876
|
},
|
|
1877
|
+
{
|
|
1878
|
+
"path": "docs/adr/0271-the-ideator-surface-is-two-halves.md",
|
|
1879
|
+
"mode": "0000644",
|
|
1880
|
+
"sha256": "0d4805df73af0b99c6dfd1c362debaa04d93a8a075f8e4b0cdf11af975bfc62c"
|
|
1881
|
+
},
|
|
1877
1882
|
{
|
|
1878
1883
|
"path": "docs/adr/README.md",
|
|
1879
1884
|
"mode": "0000644",
|
|
1880
|
-
"sha256": "
|
|
1885
|
+
"sha256": "9b75ec13500719a639364bcc9436e9f0cde63119a032c805312153dbb006f390"
|
|
1881
1886
|
},
|
|
1882
1887
|
{
|
|
1883
1888
|
"path": "docs/api-reference.md",
|
|
@@ -2762,7 +2767,7 @@
|
|
|
2762
2767
|
{
|
|
2763
2768
|
"path": "docs/module-api-changelog.md",
|
|
2764
2769
|
"mode": "0000644",
|
|
2765
|
-
"sha256": "
|
|
2770
|
+
"sha256": "aba3d7e763196a3052a53ee81f5327e0caad5274bd4de41b09b031b2277c82d2"
|
|
2766
2771
|
},
|
|
2767
2772
|
{
|
|
2768
2773
|
"path": "docs/modules-contract.md",
|
|
@@ -3007,7 +3012,7 @@
|
|
|
3007
3012
|
{
|
|
3008
3013
|
"path": "docs/recipes/windows-builders.md",
|
|
3009
3014
|
"mode": "0000644",
|
|
3010
|
-
"sha256": "
|
|
3015
|
+
"sha256": "b7d849d1786c7eca4156969d38343425839bef3bae19a10a94b5e54916e3ea09"
|
|
3011
3016
|
},
|
|
3012
3017
|
{
|
|
3013
3018
|
"path": "docs/recipes/work-on-a-task-from-your-dev-box.md",
|
|
@@ -3862,7 +3867,7 @@
|
|
|
3862
3867
|
{
|
|
3863
3868
|
"path": "modules/builder-settings/interaction-prefs.js",
|
|
3864
3869
|
"mode": "0000644",
|
|
3865
|
-
"sha256": "
|
|
3870
|
+
"sha256": "64575386423570872fd0b10751cc1863cc813c992ef27d0be26b7b924d9c6f52"
|
|
3866
3871
|
},
|
|
3867
3872
|
{
|
|
3868
3873
|
"path": "modules/builder-settings/model-alloc-prefs.js",
|
|
@@ -7692,12 +7697,12 @@
|
|
|
7692
7697
|
{
|
|
7693
7698
|
"path": "package-lock.json",
|
|
7694
7699
|
"mode": "0000644",
|
|
7695
|
-
"sha256": "
|
|
7700
|
+
"sha256": "41fdf5ef9ec5b4cb8a9f7a85f0efeba2595895fc5688c3c78702652fe4002d67"
|
|
7696
7701
|
},
|
|
7697
7702
|
{
|
|
7698
7703
|
"path": "package.json",
|
|
7699
7704
|
"mode": "0000644",
|
|
7700
|
-
"sha256": "
|
|
7705
|
+
"sha256": "c8bb2a1526d2abc3ba1bcb859d83ca86a081fd7c89a178f45f5b39a481ecaebe"
|
|
7701
7706
|
},
|
|
7702
7707
|
{
|
|
7703
7708
|
"path": "public-docs/index.html",
|
|
@@ -8007,7 +8012,7 @@
|
|
|
8007
8012
|
{
|
|
8008
8013
|
"path": "scripts/gds/copy-inventory.js",
|
|
8009
8014
|
"mode": "0000644",
|
|
8010
|
-
"sha256": "
|
|
8015
|
+
"sha256": "7269164648fc1d00e1bf3272a82f933a5f8da228e562e00848dbc29c671d3e94"
|
|
8011
8016
|
},
|
|
8012
8017
|
{
|
|
8013
8018
|
"path": "scripts/gds/coreB-carve-proof.sh",
|
|
@@ -8072,7 +8077,7 @@
|
|
|
8072
8077
|
{
|
|
8073
8078
|
"path": "scripts/gds/discipline-modes.json",
|
|
8074
8079
|
"mode": "0000644",
|
|
8075
|
-
"sha256": "
|
|
8080
|
+
"sha256": "c0c1849145999ef02f3022b22e718da8bb4fb680020124d115423f2f868834d1"
|
|
8076
8081
|
},
|
|
8077
8082
|
{
|
|
8078
8083
|
"path": "scripts/gds/discord-channels.js",
|
|
@@ -9437,7 +9442,7 @@
|
|
|
9437
9442
|
{
|
|
9438
9443
|
"path": "src/module-api.js",
|
|
9439
9444
|
"mode": "0000644",
|
|
9440
|
-
"sha256": "
|
|
9445
|
+
"sha256": "46df738e31dab69636774474ec6714365bf625a395df0a0e5ab4ecbacf178b16"
|
|
9441
9446
|
},
|
|
9442
9447
|
{
|
|
9443
9448
|
"path": "src/module-loader/catalog.js",
|
|
@@ -10132,7 +10137,7 @@
|
|
|
10132
10137
|
{
|
|
10133
10138
|
"path": "tests/copy_inventory.mjs",
|
|
10134
10139
|
"mode": "0000644",
|
|
10135
|
-
"sha256": "
|
|
10140
|
+
"sha256": "716aa03c5e93b71aec0c7e6fe646e5abd702b8b6ffda0ef6235a804d2c17a0ca"
|
|
10136
10141
|
},
|
|
10137
10142
|
{
|
|
10138
10143
|
"path": "tests/cosmos_token_contrast.mjs",
|
|
@@ -10512,7 +10517,7 @@
|
|
|
10512
10517
|
{
|
|
10513
10518
|
"path": "tests/git_merge_regen.mjs",
|
|
10514
10519
|
"mode": "0000644",
|
|
10515
|
-
"sha256": "
|
|
10520
|
+
"sha256": "c6cb1bda6809d8d384a9c35b376995af038d18c26b1b935cb5c0faebb163b72e"
|
|
10516
10521
|
},
|
|
10517
10522
|
{
|
|
10518
10523
|
"path": "tests/github_push_ancestry.mjs",
|
|
@@ -11287,7 +11292,7 @@
|
|
|
11287
11292
|
{
|
|
11288
11293
|
"path": "tests/interaction_prefs.mjs",
|
|
11289
11294
|
"mode": "0000644",
|
|
11290
|
-
"sha256": "
|
|
11295
|
+
"sha256": "e124b44e734806a3ee30b06bcaf3837237d98abf62e68a3709f4dab78c38a83c"
|
|
11291
11296
|
},
|
|
11292
11297
|
{
|
|
11293
11298
|
"path": "tests/join_application_note.mjs",
|
|
@@ -17,6 +17,17 @@ Concretely, that means:
|
|
|
17
17
|
- **Do the legwork they ask for, on demand.** Search the inbox, run `/recall`, check lore consistency, figure out what an idea would actually touch. You are their research arm; they should never have to do the digging.
|
|
18
18
|
- **The human decides what gets filed.** You only `capture.js` an idea once they've blessed it. The conversation is the product; the filed idea is its residue.
|
|
19
19
|
|
|
20
|
+
## The second rule: be quiet about the machinery
|
|
21
|
+
|
|
22
|
+
The *behavior* above inverts for this role — and so does the **surface**. An ideator came here to think about an idea, not to watch a build. So **report outcomes, not mechanism.**
|
|
23
|
+
|
|
24
|
+
- **Don't narrate the plumbing.** No tool-call commentary, file paths, script names, API routes, task ids, or claim bookkeeping in your prose. You still *use* all of it — you just don't make the ideator read about it.
|
|
25
|
+
- **Speak in the language of the idea.** "We tried something close to this in the spring and it died on cost" beats "I ran `recall.js` over the ADR corpus and got four hits."
|
|
26
|
+
- **One line out, then the answer.** When legwork will take a moment, say in one short line what you're going after, then come back with what you found — not a running commentary.
|
|
27
|
+
- **Quiet is never hiding.** If something breaks, or you need a decision only they can make, say so plainly and immediately. A quiet surface that swallows a problem is a broken surface.
|
|
28
|
+
|
|
29
|
+
**What this rule can and cannot reach.** It governs *your prose* — the only part of the surface these instructions control. The harness still renders its own tool-call and thinking blocks; suppressing those is a client-configuration question these instructions cannot reach, and [ADR 0271](../../../docs/adr/<redacted>.md) §4 records the two candidate levers and defers the choice. Don't apologize for it and don't invent workarounds.
|
|
30
|
+
|
|
20
31
|
## Step 0 — detect the mode, because it changes everything
|
|
21
32
|
|
|
22
33
|
**Is a human present in this session?**
|
package/.gitattributes
CHANGED
|
@@ -85,8 +85,18 @@ src/bongos/routes/CLAUDE.md merge=otb-regen
|
|
|
85
85
|
# files. Same otb-regen shape as docs/session-log-index.md above (whole-file
|
|
86
86
|
# rebuild, all three merge sides ignored); REGENERABLE in git-merge-regen.js
|
|
87
87
|
# carries the matching rule.
|
|
88
|
+
#
|
|
89
|
+
# The `text eol=lf` on the .md (task 1003619) is NOT cosmetic — it is what keeps
|
|
90
|
+
# `copy-inventory.js --check` honest on Windows. The generator writes LF and the
|
|
91
|
+
# check string-compares its output against the worktree file, so under the default
|
|
92
|
+
# `* text=auto` above (index LF, worktree CRLF) the check reported STALE forever on
|
|
93
|
+
# every Windows checkout while the committed content was already correct. The .json
|
|
94
|
+
# never had the bug because `*.json text eol=lf` above already pins it; the .md fell
|
|
95
|
+
# through to text=auto because no `*.md` rule exists (and none should — pinning all
|
|
96
|
+
# 685 tracked .md files would renormalize the whole docs tree for one generated file).
|
|
97
|
+
# `*.md` deliberately stays unpinned; only the GENERATED artifact is pinned here.
|
|
88
98
|
docs/copy-registry.json merge=otb-regen
|
|
89
|
-
docs/copy-inventory.md merge=otb-regen
|
|
99
|
+
docs/copy-inventory.md merge=otb-regen text eol=lf
|
|
90
100
|
|
|
91
101
|
# Append-only index tables (task 1385 / ADR 0082). docs/adr/README.md is a
|
|
92
102
|
# hand-maintained ADR index whose ONLY recurring conflict is two branches each
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# 0271 — The ideator surface is two halves: Bongos owns the prose, the harness owns the chrome
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-09-09
|
|
5
|
+
- **Tasks:** [#1003506](https://cloudbongos.com/builders#/task/1003506) (this record and the prose half). Promoted from idea 1000732 (owner, 2026-08-16).
|
|
6
|
+
- **Goal:** [#1000095](https://cloudbongos.com/builders#/goal/1000095) — Working area 6 (Governor / Builder / Artist / Ideator experience), criterion `wa6-kernel-and-packs`.
|
|
7
|
+
- **Builds on:** [ADR 0264](<redacted>.md) §1 (the area and its owner), [ADR 0016](<redacted>.md) (the trust boundary — why the permission prompt was never the gate), [ADR 0104](<redacted>.md) (the existing bypass posture on dev boxes).
|
|
8
|
+
- **Answers** the ideator half of the argument the `artist-mode` criterion answered for artists with the Studio (goal [#1000074](https://cloudbongos.com/builders#/goal/1000074)).
|
|
9
|
+
|
|
10
|
+
## 1. The decision
|
|
11
|
+
|
|
12
|
+
The ideator's complaint — *"there is really no need for an ideator to see all the
|
|
13
|
+
text and technical work being done, or even the inner Bongos logic"* — is two
|
|
14
|
+
problems wearing one sentence, and they have different owners.
|
|
15
|
+
|
|
16
|
+
**The prose half is Bongos's, and it ships with this record.** What Claude *says*
|
|
17
|
+
is reachable from configuration Bongos already owns and already injects. Three
|
|
18
|
+
pre-existing seams carry it; nothing new was plumbed:
|
|
19
|
+
|
|
20
|
+
| Axis | Seam | Behavior |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| per-**person** | `interaction-prefs.machinery` (`builder-settings`) | new field, `show` (default) \| `quiet`. Rides the live Conductor injection rail. |
|
|
23
|
+
| per-**role** | the `ideator` directive in `scripts/gds/discipline-modes.json` | a claimed ideator task opens a quiet session regardless of the person's setting. |
|
|
24
|
+
| per-**session** | `/ideate`'s new *"be quiet about the machinery"* section | the directive names `/ideate` as the only place the ideator experience is defined, so the contract has to live there too. |
|
|
25
|
+
|
|
26
|
+
**The chrome half is the harness's, and it is deferred to a spike rather than
|
|
27
|
+
guessed at.** Tool-call blocks and thinking blocks are drawn by the client, and
|
|
28
|
+
**no instruction Bongos injects can suppress them** — an injected contract is
|
|
29
|
+
content, not display configuration. But that does *not* make the chrome
|
|
30
|
+
unreachable, and this ADR was nearly written on the opposite, wrong assumption.
|
|
31
|
+
Two real levers exist (§4), and choosing between them needs a version-pinned
|
|
32
|
+
observation this record does not have. So the chrome half is **filed as a spike
|
|
33
|
+
plus an implementation task** — not attempted here, and not declared impossible.
|
|
34
|
+
|
|
35
|
+
**Bypass is explicitly NOT shipped as an ideator default**, rejecting that part
|
|
36
|
+
of the originating idea. §3 — and the reason is now partly technical, not only
|
|
37
|
+
prudential.
|
|
38
|
+
|
|
39
|
+
## 2. Why not a room, and why not wait for declared roles
|
|
40
|
+
|
|
41
|
+
**Why not a hall room (the Studio's answer).** Goal 1000074 answered the artist
|
|
42
|
+
half of this same argument with a place: *"a place an artist wants to be, not a
|
|
43
|
+
form."* `/builders/studio` is that room, and it is right for an artist, whose
|
|
44
|
+
craft has artifacts a room can hold. It cannot answer the ideator's version,
|
|
45
|
+
because **the ideator's complaint is about the surface of the Claude session,
|
|
46
|
+
which the hall does not render.** The Studio still earns its keep here as
|
|
47
|
+
precedent — the mode marker on the document root, and the discipline of a room
|
|
48
|
+
that writes nothing and links out to where the craft is done — but it is not the
|
|
49
|
+
vector. A room would have been a satisfying deliverable that missed the ask.
|
|
50
|
+
|
|
51
|
+
**Why this did not wait on per-session declared role.** The task recorded a
|
|
52
|
+
dependency on idea 1000729 (declared role), whose tasks
|
|
53
|
+
([#1002991](https://cloudbongos.com/builders#/task/1002991),
|
|
54
|
+
[#1002992](https://cloudbongos.com/builders#/task/1002992)) sit behind
|
|
55
|
+
[#1002989](https://cloudbongos.com/builders#/task/1002989) →
|
|
56
|
+
[#1002990](https://cloudbongos.com/builders#/task/1002990). That dependency is
|
|
57
|
+
**softer than it was filed.** The craft axis already exists: `discipline` is a
|
|
58
|
+
column on every task and `claim.js` already forks on it (task 1267), through the
|
|
59
|
+
file whose own documentation says *"to give a discipline its own experience
|
|
60
|
+
later, add an entry here."* The role-derived half could therefore ship today
|
|
61
|
+
through the seam built for exactly this.
|
|
62
|
+
|
|
63
|
+
When declaration lands, this contract **moves without being re-decided**: the
|
|
64
|
+
same sentence relocates from the claim directive into the composed Ideator pack,
|
|
65
|
+
and the per-person field stays put. Nothing here has to be undone.
|
|
66
|
+
|
|
67
|
+
## 3. Bypass: the idea's own caution, resolved against it
|
|
68
|
+
|
|
69
|
+
The idea proposed *"recommend bypass"* and then flagged the trade-off itself. The
|
|
70
|
+
flag wins, for three reasons in increasing order of force:
|
|
71
|
+
|
|
72
|
+
1. **The least technical role is the worst audience for removing confirmations.**
|
|
73
|
+
Shipping bypass *to ideators specifically* concentrates the one change that
|
|
74
|
+
removes review on the people least equipped to review.
|
|
75
|
+
2. **The permission prompt was never the gate.** [ADR 0016](<redacted>.md)
|
|
76
|
+
is load-bearing: authority is `builders.rank`, checked server-side per request
|
|
77
|
+
with no caching. The client prompt is a speed bump, not a boundary. The
|
|
78
|
+
protected-path globs and pre-push hook are what actually bound a sub-Metic
|
|
79
|
+
session — and an ideator is typically sub-Metic, so that narrowing is *already
|
|
80
|
+
in force*. Bypass would add exposure without adding capability.
|
|
81
|
+
3. **A project cannot ship it anyway.** The harness deliberately refuses it:
|
|
82
|
+
`permissions.defaultMode: "bypassPermissions"` set in a project's
|
|
83
|
+
`.claude/settings.json` or `.claude/settings.local.json` **does not take
|
|
84
|
+
effect — the session starts in Manual mode instead.** (`auto` is restricted
|
|
85
|
+
the same way.) Only user-level, `--settings`, or managed settings can select
|
|
86
|
+
it. So "ship bypass as an ideator default" is not merely unwise; it is
|
|
87
|
+
unavailable through the only channel Bongos can commit into a project repo.
|
|
88
|
+
Where bypass *is* the default today it was written into the machine's own user
|
|
89
|
+
settings — which is exactly what the dev box does (task 1251, ADR 0104).
|
|
90
|
+
|
|
91
|
+
The ideator's problem is the noise, not the prompt. This ADR treats only the
|
|
92
|
+
noise. Any future bypass default for a craft must arrive with a *narrower*
|
|
93
|
+
capability set, not a broader one.
|
|
94
|
+
|
|
95
|
+
## 4. The chrome half: two real levers, and what we do not yet know
|
|
96
|
+
|
|
97
|
+
Recorded so the follow-on spike starts from evidence instead of repeating the
|
|
98
|
+
search. Both are documented harness features, not speculation.
|
|
99
|
+
|
|
100
|
+
**Lever A — shipped harness defaults.** The client already has a quiet mode:
|
|
101
|
+
|
|
102
|
+
- **`/focus`** renders "only your last prompt, a one-line tool-call summary with
|
|
103
|
+
edit diffstats, and the final response" — very close to the owner's literal
|
|
104
|
+
ask. The `viewMode` setting is documented as the key that presets it.
|
|
105
|
+
- **`alwaysThinkingEnabled: false`** stops thinking being generated at all
|
|
106
|
+
(settable from any settings file), so there is nothing to render. Thinking is
|
|
107
|
+
already collapsed to a stub by default; `showThinkingSummaries` controls that
|
|
108
|
+
stub.
|
|
109
|
+
- Chrome trims confirmed committable: `showTurnDuration`, `spinnerTipsEnabled`,
|
|
110
|
+
`prefersReducedMotion`, and a custom `statusLine` — which has the useful side
|
|
111
|
+
effect of suppressing most footer keyboard hints.
|
|
112
|
+
- The built-in **`Concise` output style** ("leads with the result, skips preamble
|
|
113
|
+
and narration") is prior art for the prose contract in §1, and preserves error
|
|
114
|
+
reports and destructive-action confirmations the same way our contract does.
|
|
115
|
+
|
|
116
|
+
*Not chosen for the role axis* because `outputStyle` is a whole-session
|
|
117
|
+
setting: committing it would re-pitch every engineer session in the repo too,
|
|
118
|
+
and an output style that omits `keep-coding-instructions` drops the built-in
|
|
119
|
+
engineering instructions entirely. A role-scoped injected contract is the
|
|
120
|
+
narrower instrument.
|
|
121
|
+
|
|
122
|
+
**Three things Lever A does not settle, which the spike must:**
|
|
123
|
+
|
|
124
|
+
1. `/focus` is **fullscreen-renderer only** (a research preview), so presetting
|
|
125
|
+
it implies presetting `tui` as well.
|
|
126
|
+
2. Whether `viewMode` is settable from a **committed project** file is
|
|
127
|
+
**unverified**. Nothing should be built on it until `/config --help` is
|
|
128
|
+
checked against the pinned client version — a version-pinned observation beats
|
|
129
|
+
a docs page for this.
|
|
130
|
+
3. Thinking suppression is **model-dependent**: on some models the toggle,
|
|
131
|
+
`alwaysThinkingEnabled`, and `MAX_THINKING_TOKENS=0` have no effect at all.
|
|
132
|
+
|
|
133
|
+
**Lever B — an app-owned surface (vector (b) of the original idea).** Headless
|
|
134
|
+
mode emits no client chrome by construction: `claude -p --output-format
|
|
135
|
+
stream-json` yields a documented event stream (`system`/`init`, `assistant`,
|
|
136
|
+
`stream_event` carrying a `content_block_start` of type `tool_use`, and a
|
|
137
|
+
terminal `result` with final text, cost and `permission_denials`). The documented
|
|
138
|
+
streaming-UI pattern is precisely the owner's ask — show a working indicator on a
|
|
139
|
+
`tool_use` block, drop everything else, render on the result. This is the only
|
|
140
|
+
lever **fully in Bongos's control and model-independent**, and it is where the
|
|
141
|
+
desktop app would render the ideator's real interface.
|
|
142
|
+
|
|
143
|
+
**Bias for the spike:** prefer Lever B for the durable surface, and use Lever A
|
|
144
|
+
only for what it can do cheaply today. Lever A rests on a research-preview
|
|
145
|
+
renderer, an unverified setting, and per-model thinking behavior; Lever B rests
|
|
146
|
+
on a stable documented interface.
|
|
147
|
+
|
|
148
|
+
## 5. Consequences
|
|
149
|
+
|
|
150
|
+
- The default is `show`, so **no existing builder's experience changes.** The
|
|
151
|
+
contract injects only when a builder opts in or claims an ideator task —
|
|
152
|
+
consistent with the inject-only-when-non-default discipline the wandering and
|
|
153
|
+
interaction knobs already follow.
|
|
154
|
+
- The field needs **no UI work from this task**: `fieldCatalog()` is generic, so
|
|
155
|
+
`machinery` appears on `GET /me` and in `PATCH /me/interaction` validation
|
|
156
|
+
automatically, and whatever settings UI [#1002514](https://cloudbongos.com/builders#/task/1002514)
|
|
157
|
+
builds renders it for free.
|
|
158
|
+
- **Quiet must never hide a failure.** Both the injected contract and the
|
|
159
|
+
`/ideate` section carry that exception explicitly, and a unit test asserts the
|
|
160
|
+
contract string still contains it — this is the failure mode most likely to be
|
|
161
|
+
reintroduced by a later "tidy the wording" edit.
|
|
162
|
+
- `tests/interaction_prefs.mjs` stopped asserting a bare field **count** (which
|
|
163
|
+
every new field broke) and now asserts the field **set** against `FIELD_KEYS`.
|
|
164
|
+
- The chrome half stays visibly unfinished, which is the honest state. It is
|
|
165
|
+
filed with its evidence, not silently dropped.
|
|
166
|
+
- **One correction to the record:** task [#1002514](https://cloudbongos.com/builders#/task/1002514)
|
|
167
|
+
states that `contractFor()` "has ZERO consumers." That is **stale** — the
|
|
168
|
+
Conductor hook relays the interaction contract as advisory 8 (task 2010,
|
|
169
|
+
`.claude/hooks/conductor.js`). Its *no UI* half is still accurate.
|
|
170
|
+
|
|
171
|
+
## 6. Recorded objection: the grade panel's verdict, and this record's answer
|
|
172
|
+
|
|
173
|
+
The ship-time grader panel **failed** this task — one blocker, two majors — and the
|
|
174
|
+
task was confirmed over that verdict by the area owner rather than re-graded. The
|
|
175
|
+
disagreement is recorded here rather than left in a grade card, because it is a
|
|
176
|
+
disagreement about *scope*, and the next reader deserves both sides.
|
|
177
|
+
|
|
178
|
+
All three findings rest on the same move: the work was judged against the task
|
|
179
|
+
description's **literal asks**, which §1 and §3 above override deliberately and
|
|
180
|
+
with argument. That is a legitimate lens — a grader *should* ask "did you build
|
|
181
|
+
what was asked" — but the task's own text forecloses it: *"Deciding between (a),
|
|
182
|
+
(b) or both is the first task, not an implementation detail. Do not start by
|
|
183
|
+
trying to patch the terminal."*
|
|
184
|
+
|
|
185
|
+
**Finding 1 (blocker) — "no working-indicator / simplified rendering surface was
|
|
186
|
+
built; the literal 'show a working icon' ask is entirely unimplemented, deferred
|
|
187
|
+
to an unfiled follow-on spike."**
|
|
188
|
+
|
|
189
|
+
The first half is **true and intended**: §1 splits the ask and defers the chrome
|
|
190
|
+
half; §4 records the two candidate levers and the bias between them. The second
|
|
191
|
+
half is **factually wrong** — the spike is filed as
|
|
192
|
+
[#1003765](https://cloudbongos.com/builders#/task/1003765), the app-rendered
|
|
193
|
+
surface as [#1003766](https://cloudbongos.com/builders#/task/1003766) with a
|
|
194
|
+
dependency edge on the spike, and both are linked to `wa6-kernel-and-packs`. Both
|
|
195
|
+
were filed *before* the grade ran. "Unfiled" was the load-bearing word in a
|
|
196
|
+
blocker-severity finding, and it was not checked.
|
|
197
|
+
|
|
198
|
+
**Finding 2 (major) — "the ask to hide thinking/tool-call chrome is unaddressed at
|
|
199
|
+
the rendering layer; the ADR itself concedes this is unfinished."**
|
|
200
|
+
|
|
201
|
+
Accurate, and the concession is deliberate (§1, §4). Recorded as an open half with
|
|
202
|
+
its evidence and a named bias, not as a silent omission. Shipping a chrome
|
|
203
|
+
mechanism on this task would have meant building on `viewMode`'s unverified
|
|
204
|
+
committability and a research-preview renderer — §4's three open questions exist
|
|
205
|
+
precisely so that does not happen on a guess.
|
|
206
|
+
|
|
207
|
+
**Finding 3 (major) — "the description asked to recommend bypass (paired with
|
|
208
|
+
narrower blast radius); the shipped decision reverses this entirely rather than
|
|
209
|
+
delivering the paired mitigation the CAUTION section suggested."**
|
|
210
|
+
|
|
211
|
+
This is the substantive one, and the answer is that **the pairing was not
|
|
212
|
+
available**. §3.3: `permissions.defaultMode: "bypassPermissions"` set in a
|
|
213
|
+
project's `.claude/settings.json` or `.claude/settings.local.json` *does not take
|
|
214
|
+
effect* — the session starts in Manual. There is no channel by which a project
|
|
215
|
+
repo can ship a bypass default to be mitigated. And the "narrower blast radius"
|
|
216
|
+
the CAUTION asked to pair it with **already exists**, server-side and
|
|
217
|
+
independently of the prompt: rank checked per request ([ADR 0016](<redacted>.md)) plus the
|
|
218
|
+
sub-Metic protected-path globs. So there was no mitigation left to deliver and
|
|
219
|
+
nothing to attach it to. Rejecting was the honest reading of the CAUTION, not a
|
|
220
|
+
reversal of it.
|
|
221
|
+
|
|
222
|
+
**Why confirmed rather than re-graded.** `--regrade` is the documented way to
|
|
223
|
+
answer a grade with evidence, but it cannot carry this answer:
|
|
224
|
+
[#1003702](https://cloudbongos.com/builders#/task/1003702) records that
|
|
225
|
+
`ship.js --regrade` silently discards `--notes`, *"so an evidence-based grade
|
|
226
|
+
blocker can never be answered."* Re-running the panel would have re-litigated a
|
|
227
|
+
decision this record argues at length, with the rebuttal dropped on the floor. The
|
|
228
|
+
Archon override plus this section is the honest path while 1003702 is open.
|
|
229
|
+
|
|
230
|
+
**What would change this record's mind.** If the 1003765 spike finds `viewMode`
|
|
231
|
+
committable from a project file on the pinned client, Lever A becomes a real
|
|
232
|
+
shipped-default option and §4's bias should be revisited. Nothing in the grade
|
|
233
|
+
findings bears on that question.
|
package/docs/adr/README.md
CHANGED
|
@@ -362,3 +362,4 @@ This keeps the decision history honest and traceable.
|
|
|
362
362
|
| 0268 | [**The constitution comes from two roots, and a decision that did not take must say so** ([task 1003739](https://cloudbongos.com/builders#/task/1003739) · goal 1000111 — *Government*). On 2026-08-25 cloudbongos.com's board RATIFIED an amendment (rank:metic+ · consent · a 1440-minute sitting) and the constitution never changed — for two weeks `GET /government/constitution` answered the day-one monarchy with the passed amendment sitting in its own `history` array directly beneath the contradicting `board` block. Every sitting since was decided under a rule the board had voted to replace, and it silently un-shipped [ADR 0191](<redacted>.md) (majority) and would have un-shipped [ADR 0267](<redacted>.md) the same way. **Cause:** `modules/government/config.js` resolved BOTH its neutral starter and its instance pack from one `path.resolve(__dirname, '..', '..')`, under a comment saying the roots coincided *today* — true in a single checkout, false on a STANDALONE instance ([ADR 0108](<redacted>.md) §1), where the server runs `node_modules/@cloudbongos/core` with `WorkingDirectory=<instance repo>`. The neutral path stayed right (it really is core content); the INSTANCE path resolved inside the core package, so the host's own `config/government.json` was never read and `applyBoardAmendment` wrote the ratified amendment into `node_modules`, where the nightly core upgrade ([ADR 0161](<redacted>.md) cuts a release per merge) erased it within a day. **Nothing threw and nothing could have** — both paths exist, both are writable, and an absent instance pack is a legal state, so the read fell through to the neutral monarchy exactly as designed. `src/branding.js` had the pattern right three files away. **Fix:** neutral from `resolveCoreRoot()`, instance from `resolveInstanceRoot()`, reached through `src/module-api` — which had exposed NEITHER resolver, and that absence is precisely why the module re-derived the wrong one ([ADR 0083](<redacted>.md) forbids requiring a core internal). Resolved at load, not per call, because `resolveInstanceRoot()` falls back to `process.cwd()` and a lazy resolve would let a `chdir` move the constitution; `applyBoardAmendment` now mkdirs, since the write targets a directory the core does not own. Single-checkout behaviour is byte-identical, asserted rather than claimed. **The detector, which is the durable half:** `constitutionView` carries a `divergence` block comparing the newest PASSED amendment (sanitized as an apply would write it) field-by-field against what is in force, rendered above the hall's dials in words rather than config keys. It REPORTS and never HEALS — the instance pack is also the file a human editing the constitution touches, so a silent re-apply would revert a legitimate hand edit with no way to tell the two apart. A RETURNED amendment is never compared (that is what *returned* means). Does NOT put the 2026-08-25 amendment back in force — that is a board act. Sibling sweep RUN, not promised: one real hit filed as [task 1003745](https://cloudbongos.com/builders#/task/1003745) (quarantine.js's repo-containment refusal is blind to the instance repo).](<redacted>.md) | government / instance roots |
|
|
363
363
|
| 0269 | [**The CLI session store is host-keyed at a fixed anchor; the per-brand file stays the active pointer** ([task 1003741](https://cloudbongos.com/builders#/task/1003741) · goal 1000090 — *Working area 4, Bongos Core distribution*). The CLI resolved its session path from the BRANDING PACK, read out of whatever checkout the process stood in — one slot, that moved. Standalone (the public CLI's whole situation) no checkout means no brand, so every instance shared `~/.config/cloudbongos/gds-session.json` and signing into a second DESTROYED the first; in-repo it landed in `~/.config/<slug>/` where the standalone CLI could never find it (`npx … api GET /me` from a bare dir returned the cloudbongos builder while a valid hermeslines session sat on the same machine). Not theoretical — the owner's config dir carries a hand-made `gds-session.<redacted>.bak.json` and four more of the same shape. **Decision: one file per instance keyed by HOST at a FIXED anchor (`~/.config/<FALLBACK_DIR>/instances/<host>.json`), with the per-brand `gds-session.json` left exactly where it is as the ACTIVE pointer.** Four load-bearing parts. (1) The anchor must never read `configHome()`/`configDirName()`/the pack — a test asserts the function body names none, since routing it back through the brand silently reinstates the split. (2) The active pointer does NOT move: every existing reader (in-repo skills, the card hook, the dev box, `readSessionToken`) looks there, and the store is purely additive. (3) The OUTGOING session is archived BEFORE the incoming one lands — a session written before the store existed is not in it, so without this the first login after upgrading still loses it, the bug surviving its own fix. (4) A stored token is VERIFIED against `/api/gds/me` before being reinstated; unverified falls through to the real device flow, `--force` signs in as somebody else. `login` also names the other instances and how to switch, since sessions are now kept rather than overwritten. Files 0600 in a 0700 dir; an `api_base` naming no host is not stored rather than stored under a guess, so nothing can write outside the store. The dir is `instances/` NOT `sessions/` — `sessions` is a live module key and [ADR 0083](<redacted>.md) §Decision #4 forbids kernel machinery naming one; the fitness gate caught it. Rejected: moving the active pointer (breaks every reader for no visible gain); one file holding a map (changes a format hooks and the dev box parse today); keying under `configHome()` (the bug); restoring without verifying (hands over a dead session that looks live); a new `bongos switch` verb (login is the command already reached for, and the public CLI's surface is deliberately small).](<redacted>.md) | cli / auth / distribution |
|
|
364
364
|
| 0270 | [**The rank floor derives from facts, not from a prediction** ([task 1003663](https://cloudbongos.com/builders#/task/1003663) · goal 1000111 — *Working area 7, Government*). [ADR 0084](<redacted>.md) derives every task’s claim-time `requires_rank` floor from `touches[]` + `security_sensitive`; [ADR 0049](<redacted>.md) then retired PREDICTIVE `touches[]`, which is now backfilled from the real committed diff at SHIP — after the claim the floor exists to gate. So on every create vector that omits it the matcher saw an empty array, and because the floor is computed ONCE at creation and never recomputed, it returned `xenos` forever. Nothing threw: an empty `touches[]` is a legal state. **Measured before the fix: 88 tasks declared `needs_migration` and 68 of them (77%) sat at `xenos`** — including [task 1003372](https://cloudbongos.com/builders#/task/1003372), a READY task to hash the `builder_sessions` token store at rest, claimable by any newcomer; goal 1000111 alone held 20 such rows. Two mechanisms hid it: the authoring routes DISAGREE (`POST /goals/:id/tasks` REQUIRES `touches[]` as its scope-wall blast radius, so the floor derives there, while its sibling `POST /tasks` — the route `/goal-create` actually files seed tasks through — treats it as optional), and `goal-create/SKILL.md` told authors in ONE line to *omit `touches[]`* and *never hand-set `requires_rank`*: omit the only input, never set the output. **Decision:** the floor derives from facts a task already carries at create time, never from a prediction — `deriveRequiredRank` gains `opts` with two touches-free inputs, `needs_migration` (the direct successor to the `migrations/` entry `POST /tasks` used to fold into `touches[]`) and `scopeProtected` (the owning goal’s `scope_modules` wall, ALWAYS available since `goal_id` is required, read through the same `scopeIncludesProtected` primitive the pre-push hook, grader and main-audit use, so it cannot drift). Derived at the LOWEST create layer in `createTask` after `goalIdEffective` resolves — the ADR 0096 argument — so every vector inherits it and patching the two routes separately (which is how they came to disagree) is not repeated. `updateTaskRequiresRank` re-derives with the same four inputs, else an Archon setting a migration task to `xenos` would silently undo the create-time floor; raise-only throughout; `archon` still never auto-assigned; a strict `=== true` so a stray truthy value cannot wall off the open queue. One-time backfill `core_236` raises still-CLAIMABLE rows only (the floor is a claim-time gate; rewriting shipped rows changes no live decision) and never lowers. **Accepted cost:** a protected goal wall is blunt — any protected module in the wall floors the whole goal, so a `hall-ui` task in a mixed goal floors at Metic too; same semantics the ideas vector has enforced since task 1003066, fails safe, and the remedy is to narrow the wall. Zero claimable `newcomer_friendly` tasks exist today, and no carve-out was added for them on purpose. Rejected: re-requiring predictive `touches[]` (re-litigates ADR 0049); recomputing at ship (too late by construction); deriving from `module_key` (null on most tasks, including the reporter).](<redacted>.md) | permissions / task lifecycle |
|
|
365
|
+
| 0271 | [**The ideator surface is two halves: Bongos owns the prose, the harness owns the chrome** ([task 1003506](https://cloudbongos.com/builders#/task/1003506) · goal 1000095 — *Working area 6, Governor / Builder / Artist / Ideator experience*, criterion `wa6-kernel-and-packs`; owner idea 1000732, 2026-08-16). The ask — *no need for an ideator to see all the text and technical work, or the inner Bongos logic* — is two problems in one sentence. **The PROSE half ships here**, through three seams that already existed: a `machinery` field on `interaction-prefs` (per-PERSON, default `show`, riding the live Conductor injection rail — advisory 8, which means task [#1002514](https://cloudbongos.com/builders#/task/1002514)’s claim that `contractFor()` has zero consumers is STALE); the `ideator` directive in `discipline-modes.json` (per-ROLE, the file whose own docs say *to give a discipline its own experience later, add an entry here*); and a new surface section in `/ideate`, which the directive names as the only home of the experience. Default `show` means no existing builder changes. **The CHROME half is the harness’s and is deferred to a spike, not declared impossible** — this record was nearly written on the wrong assumption that config cannot reach it. Two real levers exist: **A**, shipped client defaults (`/focus` renders *only your last prompt, a one-line tool-call summary, and the final response*; `alwaysThinkingEnabled:false` stops thinking being generated; `showTurnDuration`/`spinnerTipsEnabled`/`statusLine` trim chrome) — but `/focus` is fullscreen-only (research preview), `viewMode`’s committability is UNVERIFIED, and thinking suppression is per-model; and **B**, an app-owned surface over `claude -p --output-format stream-json`, whose documented streaming-UI pattern (working indicator on a `tool_use` block, render on `result`) IS the ask, is model-independent, and is the recorded bias. **Bypass is REJECTED** despite the idea proposing it, on three grounds ending in a technical one: the least technical role is the worst audience for removing confirmations; the prompt was never the gate ([ADR 0016](<redacted>.md) — rank is checked server-side, and sub-Metic protected-path globs already narrow an ideator); and a project **cannot** ship it — `defaultMode: bypassPermissions` in `.claude/settings.json` silently does not take effect and starts the session in Manual. Rejected also: a hall ROOM on the Studio’s precedent (goal 1000074’s *a place, not a form*) — right for an artist, wrong here, because the ideator’s complaint is about the Claude session’s surface, which the hall does not render; and waiting on per-session declared role, whose dependency is softer than filed since `discipline` + the claim-time fork already carry the craft axis. When declaration lands the contract MOVES into the Ideator pack with nothing undone.](<redacted>.md) | roles / ideator experience / session surface |
|
|
@@ -1703,5 +1703,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1703
1703
|
landed since 1.19.625 with no explicit bump. run 34363350286. (task 1002620)
|
|
1704
1704
|
1.19.627 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1705
1705
|
landed since 1.19.626 with no explicit bump. run 34390069410. (task 1002620)
|
|
1706
|
+
1.19.628 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1707
|
+
landed since 1.19.627 with no explicit bump. run 34397967061. (task 1002620)
|
|
1708
|
+
1.19.629 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1709
|
+
landed since 1.19.628 with no explicit bump. run 34412159317. (task 1002620)
|
|
1706
1710
|
---------------------------------------------------------------------------
|
|
1707
1711
|
```
|
|
@@ -147,3 +147,4 @@ caught a real bug during task 1003548, so read its assertion before blaming the
|
|
|
147
147
|
|
|
148
148
|
- Several hall files (`people.css`, `people.html`, `style.css`, the privacy spec) and every `.claude/skills/*/SKILL.md` are CRLF; `scripts/gds/*.js` and the tests are LF. An exact-string patch must detect the file's EOL before joining lines (`text.includes('\r\n') ? '\r\n' : '\n'`), or the find matches nothing and the edit is a silent no-op (session log 2026-08-24).
|
|
149
149
|
- A `git diff --stat` that touches every line of a file you barely edited is an EOL flip — check with `git diff --stat --ignore-cr-at-eol`, and restore the original EOL before committing.
|
|
150
|
+
- **`copy-inventory.js --check` no longer reds on line endings alone** (task 1003619). It used to: the generator emits LF and the check byte-compared that against a CRLF worktree file, so `/ship-check` showed one permanent red line on every Windows checkout while the committed content was already correct — and a builder who "fixed" it committed a whole-file CRLF diff. `docs/copy-inventory.md` is now pinned `text eol=lf` in `.gitattributes` (its `.json` sibling always was, via `*.json`), and the check compares EOL-blind so an already-CRLF worktree passes without a re-clone. If it reports STALE now, the content really is stale — regenerate, don't reach for `--ignore-cr-at-eol`. Note `*.md` is deliberately NOT pinned repo-wide; only that one generated artifact is.
|
|
@@ -74,6 +74,34 @@ const FIELDS = {
|
|
|
74
74
|
},
|
|
75
75
|
},
|
|
76
76
|
},
|
|
77
|
+
// task 1003506 (ADR 0271) — the SURFACE half of the interaction profile. The
|
|
78
|
+
// three fields above tune how a reply is PITCHED (vocabulary, length); this
|
|
79
|
+
// one tunes how much of the WORK BEHIND IT is narrated. An ideator asked for
|
|
80
|
+
// "no need to see all the text and technical work being done, or even the
|
|
81
|
+
// inner Bongos logic" — that is not a vocabulary problem, so it needed its
|
|
82
|
+
// own field rather than another value on technical_level.
|
|
83
|
+
//
|
|
84
|
+
// SCOPE, stated honestly: this governs Claude's own PROSE, which is the only
|
|
85
|
+
// part of the surface a contract can reach. It cannot hide the harness's
|
|
86
|
+
// rendering of tool calls or thinking blocks — see ADR 0271 §4. Default is
|
|
87
|
+
// `show` (today's behavior) so existing builders are untouched; the ideator
|
|
88
|
+
// CRAFT gets `quiet` from its claim directive instead of from this default
|
|
89
|
+
// (scripts/gds/discipline-modes.json), because the surface follows the ROLE
|
|
90
|
+
// and this key follows the PERSON.
|
|
91
|
+
machinery: {
|
|
92
|
+
label: 'Machinery in replies',
|
|
93
|
+
blurb: 'Whether replies narrate the tools, file paths, and Bongos internals the work went through.',
|
|
94
|
+
values: {
|
|
95
|
+
show: {
|
|
96
|
+
label: 'Show the work (default)',
|
|
97
|
+
contract: '',
|
|
98
|
+
},
|
|
99
|
+
quiet: {
|
|
100
|
+
label: 'Hide the machinery',
|
|
101
|
+
contract: 'Machinery: quiet — report OUTCOMES, not mechanism. Say what changed and what it means for the reader; do not narrate tool calls, file paths, script names, API routes, task/claim bookkeeping, or Bongos internals unless they ask or something actually failed. When a step fails, say plainly what broke and what you need — a quiet surface never hides a problem.',
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
77
105
|
};
|
|
78
106
|
|
|
79
107
|
const FIELD_KEYS = Object.keys(FIELDS);
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.629",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.629",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.629",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
|
@@ -485,6 +485,32 @@ function renderReport(reg) {
|
|
|
485
485
|
return L.join('\n');
|
|
486
486
|
}
|
|
487
487
|
|
|
488
|
+
// Line-ending-blind view of a file's text, for the --check compare ONLY (task 1003619).
|
|
489
|
+
// Why: the generator emits LF, and --check string-compares that against the file on
|
|
490
|
+
// disk. A Windows checkout hands back CRLF, so the compare reported STALE forever on
|
|
491
|
+
// content that was byte-identical in the index — a permanently red /ship-check gate,
|
|
492
|
+
// which teaches builders to wave past the one check that catches a REAL stale artifact.
|
|
493
|
+
// .gitattributes now pins this pair to eol=lf, but that only governs FUTURE checkouts:
|
|
494
|
+
// a file already sitting CRLF in an existing worktree stays CRLF until renormalized,
|
|
495
|
+
// so the checker must not care either way. Freshness is a question about CONTENT.
|
|
496
|
+
// Not used on the write path — generated files are still written with LF endings.
|
|
497
|
+
function lf(s) {
|
|
498
|
+
return s.replace(/\r\n/g, '\n');
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Is the artifact on disk still what the generator would write? Freshness is a
|
|
502
|
+
// question about CONTENT, so CRLF-vs-LF is not a difference — see lf() above for
|
|
503
|
+
// why that mattered. A missing file (`have === null`) is stale by definition.
|
|
504
|
+
// Only \r\n is folded, NOT a lone \r: no tool in this repo emits classic-Mac
|
|
505
|
+
// endings, and treating a bare \r as a newline would let a genuinely corrupt file
|
|
506
|
+
// read as fresh. Pure + exported so the decision is unit-testable without writing
|
|
507
|
+
// to the committed artifacts (same shape as shouldRefuseSessionClaim in
|
|
508
|
+
// modules/lifecycle/routes/claims.js).
|
|
509
|
+
function matchesGenerated(have, want) {
|
|
510
|
+
if (have === null || have === undefined) return false;
|
|
511
|
+
return lf(have) === lf(want);
|
|
512
|
+
}
|
|
513
|
+
|
|
488
514
|
function write({ check = false } = {}) {
|
|
489
515
|
const reg = buildRegistry();
|
|
490
516
|
const json = `${JSON.stringify(reg, null, 2)}\n`;
|
|
@@ -495,7 +521,7 @@ function write({ check = false } = {}) {
|
|
|
495
521
|
const stale = [];
|
|
496
522
|
for (const [p, want] of [[jsonPath, json], [mdPath, md]]) {
|
|
497
523
|
const have = fs.existsSync(p) ? fs.readFileSync(p, 'utf8') : null;
|
|
498
|
-
if (have
|
|
524
|
+
if (!matchesGenerated(have, want)) stale.push(path.relative(REPO_ROOT, p));
|
|
499
525
|
}
|
|
500
526
|
return { reg, stale };
|
|
501
527
|
}
|
|
@@ -525,5 +551,5 @@ if (require.main === module) {
|
|
|
525
551
|
module.exports = {
|
|
526
552
|
SURFACES, COPY_ATTRS, BLIND_SPOTS, REGISTRY_REL, REPORT_REL,
|
|
527
553
|
cmp, stableId, looksLikeCopy, lineIndex, lineAt, stripEmbedded, fromHtml, fromJs, filesFor,
|
|
528
|
-
buildRegistry, renderReport, write,
|
|
554
|
+
buildRegistry, renderReport, write, lf, matchesGenerated,
|
|
529
555
|
};
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"ideator": {
|
|
10
10
|
"skill": "ideate",
|
|
11
11
|
"label": "Ideation session",
|
|
12
|
-
"directive": "This is an IDEATOR task. To work it, invoke the /ideate skill — that skill is the operating playbook for ideation, and it is the only place the experience is defined. It inverts the usual dynamic: you (the human ideator) are the creative driver and Claude is your sounding board / research arm / devil's advocate, not a content generator filing ideas on command. If no human is present (autonomous / bypass-permissions), /ideate runs a self-adversarial divergence-and-critique loop instead of dumping a fixed batch. Open /ideate now rather than working this task the default way."
|
|
12
|
+
"directive": "This is an IDEATOR task. To work it, invoke the /ideate skill — that skill is the operating playbook for ideation, and it is the only place the experience is defined. It inverts the usual dynamic: you (the human ideator) are the creative driver and Claude is your sounding board / research arm / devil's advocate, not a content generator filing ideas on command. If no human is present (autonomous / bypass-permissions), /ideate runs a self-adversarial divergence-and-critique loop instead of dumping a fixed batch. Open /ideate now rather than working this task the default way. THE SURFACE, not just the behavior (ADR 0271): an ideator session is QUIET about its own machinery. Report outcomes, not mechanism — do not narrate tool calls, file paths, script names, API routes or task/claim bookkeeping unless they ask or something actually failed. Speak in the language of the idea, not the plumbing. If a step breaks, say plainly what broke and what you need — quiet never means hiding a problem."
|
|
13
13
|
},
|
|
14
14
|
"artist": {
|
|
15
15
|
"skill": "paint",
|
package/src/module-api.js
CHANGED
|
@@ -55,7 +55,7 @@ const { buildInfo } = require('./build-info');
|
|
|
55
55
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
56
56
|
// the entry to that file. Look for a version's history there, not here.
|
|
57
57
|
// ---------------------------------------------------------------------------
|
|
58
|
-
const CORE_VERSION = '1.19.
|
|
58
|
+
const CORE_VERSION = '1.19.629'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
59
59
|
|
|
60
60
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
61
61
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
package/tests/copy_inventory.mjs
CHANGED
|
Binary file
|
|
@@ -176,9 +176,24 @@ function withBlockBody(src, body) {
|
|
|
176
176
|
// .gitattributes, one in REGENERABLE) — nothing checked that they still matched, so
|
|
177
177
|
// docs/copy-inventory.md + docs/copy-registry.json could be (and were) added to
|
|
178
178
|
// neither and merge like ordinary text. This pins the two lists as one set.
|
|
179
|
+
// A .gitattributes line may carry SEVERAL attributes for one path — git reads
|
|
180
|
+
// `docs/copy-inventory.md merge=otb-regen text eol=lf` exactly as it reads the
|
|
181
|
+
// bare `merge=otb-regen` form. The original matcher here was end-anchored
|
|
182
|
+
// (/^(\S+)\s+merge=otb-regen\s*$/), so adding any second attribute made this
|
|
183
|
+
// check stop SEEING the entry and report it as missing (task 1003619, where the
|
|
184
|
+
// .md gained an eol pin). Under-inclusive in the dangerous direction too: a
|
|
185
|
+
// regenerable path declared with extra attributes would slip the REGENERABLE
|
|
186
|
+
// cross-check entirely. Tokenize the attribute list instead of pattern-matching
|
|
187
|
+
// the whole line, so the set equality below is about PATHS, not line formatting.
|
|
179
188
|
{
|
|
180
189
|
const attrsText = fs.readFileSync(path.join(repo, '.gitattributes'), 'utf8');
|
|
181
|
-
const otbRegenPaths =
|
|
190
|
+
const otbRegenPaths = attrsText
|
|
191
|
+
.split(/\r?\n/)
|
|
192
|
+
.map((line) => line.trim())
|
|
193
|
+
.filter((line) => line && !line.startsWith('#'))
|
|
194
|
+
.map((line) => line.split(/\s+/))
|
|
195
|
+
.filter((tokens) => tokens.slice(1).includes('merge=otb-regen'))
|
|
196
|
+
.map((tokens) => tokens[0]);
|
|
182
197
|
assert.ok(otbRegenPaths.length > 0, 'expected at least one merge=otb-regen line in .gitattributes');
|
|
183
198
|
for (const p of otbRegenPaths) {
|
|
184
199
|
assert.ok(regen.REGENERABLE.has(p), `.gitattributes declares merge=otb-regen for ${p} but REGENERABLE (git-merge-regen.js) does not know it`);
|
|
@@ -65,8 +65,50 @@ test('applyPatch merges and null clears', () => {
|
|
|
65
65
|
|
|
66
66
|
test('describeForApi exposes catalog + effective + contract + inject', () => {
|
|
67
67
|
const d = ip.describeForApi({ interaction: { verbosity: 'detailed' } });
|
|
68
|
-
assert.ok(Array.isArray(d.fields)
|
|
68
|
+
assert.ok(Array.isArray(d.fields));
|
|
69
|
+
assert.deepEqual(d.fields.map((f) => f.field), ip.FIELD_KEYS, "the catalog must expose every field, in order");
|
|
69
70
|
assert.equal(d.effective.verbosity, 'detailed');
|
|
70
71
|
assert.equal(d.inject, true);
|
|
71
72
|
assert.match(d.contract, /detailed/);
|
|
72
73
|
});
|
|
74
|
+
|
|
75
|
+
// ---- the machinery field (task 1003506 / ADR 0270) ----
|
|
76
|
+
// The SURFACE half of the profile: how much of the work behind a reply gets
|
|
77
|
+
// narrated. Default `show` keeps every existing builder on today's behavior.
|
|
78
|
+
|
|
79
|
+
test('machinery defaults to show and injects nothing', () => {
|
|
80
|
+
assert.equal(ip.defaultValue('machinery'), 'show');
|
|
81
|
+
assert.equal(ip.effectiveProfile({}).machinery, 'show');
|
|
82
|
+
assert.equal(ip.contractFor({ interaction: { machinery: 'show' } }), '', 'the default must never spend session context');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('machinery=quiet injects an outcomes-not-mechanism contract', () => {
|
|
86
|
+
const prefs = { interaction: { machinery: 'quiet' } };
|
|
87
|
+
assert.equal(ip.effectiveProfile(prefs).machinery, 'quiet');
|
|
88
|
+
assert.ok(ip.shouldInject(prefs));
|
|
89
|
+
const c = ip.contractFor(prefs);
|
|
90
|
+
assert.match(c, /Machinery: quiet/);
|
|
91
|
+
assert.match(c, /OUTCOMES, not mechanism/);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('quiet still requires reporting a real failure (it is not a gag)', () => {
|
|
95
|
+
// Load-bearing: a surface that hides a broken step is worse than a noisy one,
|
|
96
|
+
// so the contract itself must carry the exception.
|
|
97
|
+
const c = ip.contractFor({ interaction: { machinery: 'quiet' } });
|
|
98
|
+
assert.match(c, /fails/, 'the contract must name the failure exception');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('machinery validates like every other field', () => {
|
|
102
|
+
assert.equal(ip.validatePatch({ machinery: 'quiet' }).ok, true);
|
|
103
|
+
const bad = ip.validatePatch({ machinery: 'silent' });
|
|
104
|
+
assert.equal(bad.ok, false);
|
|
105
|
+
assert.deepEqual(bad.errors[0].allowed, ['show', 'quiet']);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('machinery composes with the pitch fields rather than replacing them', () => {
|
|
109
|
+
// The three original fields tune HOW a reply is pitched; machinery tunes how
|
|
110
|
+
// much of the work behind it is shown. Both must appear together.
|
|
111
|
+
const c = ip.contractFor({ interaction: { technical_level: 'engineer', machinery: 'quiet' } });
|
|
112
|
+
assert.match(c, /engineer/);
|
|
113
|
+
assert.match(c, /Machinery: quiet/);
|
|
114
|
+
});
|