@bongos/core 1.19.689 → 1.19.690
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 +18 -13
- package/docs/module-api-changelog.md +2 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/conductor-dispatch.js +15 -4
- package/src/bongos/claim-attribution.js +49 -0
- package/src/module-api.js +1 -1
- package/tests/conductor.mjs +103 -3
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.690",
|
|
6
|
+
"core_contract": "1.19.690",
|
|
7
|
+
"source_commit": "60679343d4642880b9d09e266fdf8847948e63c4",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-
|
|
9
|
+
"built_at": "2026-09-13T00:15:19.171Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 477,
|
|
13
13
|
"agent_docs_stubbed": 24,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 2126,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2627,
|
|
20
|
+
"tree_sha256": "62df4f01b358cfb184df394a6f373977f6638d2b4ce8f58df8e78f66259bfb95",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -2802,7 +2802,7 @@
|
|
|
2802
2802
|
{
|
|
2803
2803
|
"path": "docs/module-api-changelog.md",
|
|
2804
2804
|
"mode": "0000644",
|
|
2805
|
-
"sha256": "
|
|
2805
|
+
"sha256": "a8b10d6556ee013925b76fab635bbf14e1299216be69b54959b07de8cf47b8da"
|
|
2806
2806
|
},
|
|
2807
2807
|
{
|
|
2808
2808
|
"path": "docs/modules-contract.md",
|
|
@@ -7782,12 +7782,12 @@
|
|
|
7782
7782
|
{
|
|
7783
7783
|
"path": "package-lock.json",
|
|
7784
7784
|
"mode": "0000644",
|
|
7785
|
-
"sha256": "
|
|
7785
|
+
"sha256": "260ff5666981dd27b2112c4e0ed8618761d10fe9c505630e3b75eaac3d588f73"
|
|
7786
7786
|
},
|
|
7787
7787
|
{
|
|
7788
7788
|
"path": "package.json",
|
|
7789
7789
|
"mode": "0000644",
|
|
7790
|
-
"sha256": "
|
|
7790
|
+
"sha256": "6164b80832d080ed4ec27d8e4678ffbbab1344cf876af1b46702e5e4e2988924"
|
|
7791
7791
|
},
|
|
7792
7792
|
{
|
|
7793
7793
|
"path": "public-docs/index.html",
|
|
@@ -8077,7 +8077,7 @@
|
|
|
8077
8077
|
{
|
|
8078
8078
|
"path": "scripts/gds/conductor-dispatch.js",
|
|
8079
8079
|
"mode": "0000644",
|
|
8080
|
-
"sha256": "
|
|
8080
|
+
"sha256": "b0e5767d0a30e57130169b41f1dd0add6cfc54237bca6d731219d5aff5d38168"
|
|
8081
8081
|
},
|
|
8082
8082
|
{
|
|
8083
8083
|
"path": "scripts/gds/context-pack.js",
|
|
@@ -9289,6 +9289,11 @@
|
|
|
9289
9289
|
"mode": "0000644",
|
|
9290
9290
|
"sha256": "7290d5d15b4c8283ebb17e28b17739b9dc5de4da3e916e9491248ba8acc24619"
|
|
9291
9291
|
},
|
|
9292
|
+
{
|
|
9293
|
+
"path": "src/bongos/claim-attribution.js",
|
|
9294
|
+
"mode": "0000644",
|
|
9295
|
+
"sha256": "1c3c7bc5d3c72de526334866a966a2fc7c5c8a486e19d65fefd50431f982d77b"
|
|
9296
|
+
},
|
|
9292
9297
|
{
|
|
9293
9298
|
"path": "src/bongos/db-kernel.js",
|
|
9294
9299
|
"mode": "0000644",
|
|
@@ -9547,7 +9552,7 @@
|
|
|
9547
9552
|
{
|
|
9548
9553
|
"path": "src/module-api.js",
|
|
9549
9554
|
"mode": "0000644",
|
|
9550
|
-
"sha256": "
|
|
9555
|
+
"sha256": "3db8b41741ace6b9070683824e39c1fa8e7c2724bef4585e91734ffd43afa382"
|
|
9551
9556
|
},
|
|
9552
9557
|
{
|
|
9553
9558
|
"path": "src/module-loader/catalog.js",
|
|
@@ -10207,7 +10212,7 @@
|
|
|
10207
10212
|
{
|
|
10208
10213
|
"path": "tests/conductor.mjs",
|
|
10209
10214
|
"mode": "0000644",
|
|
10210
|
-
"sha256": "
|
|
10215
|
+
"sha256": "e0d75747731cf45280dca2d36260a8b7799923dc1a2ffeb775cd37a0eb1f837c"
|
|
10211
10216
|
},
|
|
10212
10217
|
{
|
|
10213
10218
|
"path": "tests/confirm_attribution.mjs",
|
|
@@ -1837,5 +1837,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1837
1837
|
landed since 1.19.687 with no explicit bump. run 34725902871. (task 1002620)
|
|
1838
1838
|
1.19.689 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1839
1839
|
landed since 1.19.688 with no explicit bump. run 34726470555. (task 1002620)
|
|
1840
|
+
1.19.690 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1841
|
+
landed since 1.19.689 with no explicit bump. run 34727464797. (task 1002620)
|
|
1840
1842
|
---------------------------------------------------------------------------
|
|
1841
1843
|
```
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.690",
|
|
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.690",
|
|
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.690",
|
|
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",
|
|
@@ -65,6 +65,9 @@ const { isSensitive } = require('../../src/bongos/sensitive-surfaces');
|
|
|
65
65
|
// re-hand-rolled this coverage logic — the exact drift this PR fixed for the
|
|
66
66
|
// sensitive-surface set. Route through the one matcher instead.)
|
|
67
67
|
const { matchOne } = require('../../src/bongos/path-match');
|
|
68
|
+
// The worktree→claim rule, shared with .claude/hooks/conductor.js — the third
|
|
69
|
+
// member of this file's "one definition, both conductors" set (task 1003837).
|
|
70
|
+
const { claimForWorktree } = require('../../src/bongos/claim-attribution');
|
|
68
71
|
|
|
69
72
|
const GRADER_DIFF_CHAR_CAP = 250_000;
|
|
70
73
|
const GRADER_TIMEOUT_MS = 240_000;
|
|
@@ -209,10 +212,11 @@ function diffStatsSince(base, untracked) {
|
|
|
209
212
|
//
|
|
210
213
|
// Claims from before the column existed carry a null worktree_name and are
|
|
211
214
|
// simply not matched here — the heuristic still covers them.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
215
|
+
//
|
|
216
|
+
// The rule itself moved to src/bongos/claim-attribution.js in task 1003837, so
|
|
217
|
+
// the conductor HOOK can answer the same question the same way without pulling
|
|
218
|
+
// this module (and cli-lib, and the grader) into a per-prompt hook. Re-exported
|
|
219
|
+
// below, so every existing importer and test is unchanged.
|
|
216
220
|
|
|
217
221
|
// Pick the active claim whose touches[] best overlap the changed files — the
|
|
218
222
|
// claim the current diff most plausibly belongs to. With multiple (possibly
|
|
@@ -296,6 +300,13 @@ async function resolveTaskContext(changedFiles) {
|
|
|
296
300
|
// one answered is reported to the reader rather than hidden.
|
|
297
301
|
const attribution = attributeClaim(claims, changedFiles, path.basename(process.cwd()));
|
|
298
302
|
const c = attribution.claim;
|
|
303
|
+
// attributeClaim's documented { claim: null, signal: 'none' } return —
|
|
304
|
+
// not dead defensive code, and not a crash path: the throw is caught by
|
|
305
|
+
// this function's own catch below, which is where the no-claim context
|
|
306
|
+
// is synthesized. Unreachable only because the caller happens to check
|
|
307
|
+
// claims.length first; it states the seam's contract rather than relying
|
|
308
|
+
// on that. (Task 1003834's grade panel read it as a dead branch and
|
|
309
|
+
// filed it as a nit; kept, with this sentence, per task 1003837.)
|
|
299
310
|
if (!c) throw new Error('no claim attributed');
|
|
300
311
|
// Pull the full description for richer context (best-effort). Coerce
|
|
301
312
|
// task_id to an integer before interpolating it into the URL path —
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// src/bongos/claim-attribution.js — which of a builder's open claims does THIS
|
|
3
|
+
// working tree belong to? (task 1003837)
|
|
4
|
+
//
|
|
5
|
+
// WHY. Two things answer that question — `scripts/gds/conductor-dispatch.js`,
|
|
6
|
+
// which briefs a reviewer on the claim a diff belongs to, and
|
|
7
|
+
// `.claude/hooks/conductor.js`, which measures "changed files outside your
|
|
8
|
+
// claim's declared touches[]" on every prompt. They must not answer it
|
|
9
|
+
// differently, and the hook cannot simply require the dispatcher: the dispatcher
|
|
10
|
+
// pulls in cli-lib, the grader and the autonomy gate at module scope, and the
|
|
11
|
+
// hook's cost posture is explicit that it runs on EVERY prompt and must never
|
|
12
|
+
// crash one. So the rule lives here, in a dependency-free core module both can
|
|
13
|
+
// resolve — the same shape `sensitive-surfaces.js` and `path-match.js` already
|
|
14
|
+
// take for this exact pair, after the Phase 4 dogfood flagged a hand-copied
|
|
15
|
+
// pattern set as a drift risk.
|
|
16
|
+
//
|
|
17
|
+
// The worktree is a FACT, not a guess. `claim.js` records the worktree it was
|
|
18
|
+
// run in, and the project default is one claim per worktree (task 1642), so a
|
|
19
|
+
// name match attributes the tree with certainty. That is worth keeping separate
|
|
20
|
+
// from the dispatcher's touches[]-overlap heuristic, which is a guess and is
|
|
21
|
+
// labelled as one.
|
|
22
|
+
|
|
23
|
+
const path = require('node:path');
|
|
24
|
+
|
|
25
|
+
// The claim bound to `worktreeName`, or null. Exact match: the worktree name is
|
|
26
|
+
// a recorded identifier, not a pattern, so a near-miss is a different tree.
|
|
27
|
+
function claimForWorktree(claims, worktreeName) {
|
|
28
|
+
if (!Array.isArray(claims) || !worktreeName) return null;
|
|
29
|
+
return claims.find((c) => c && c.worktree_name && c.worktree_name === worktreeName) || null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// The worktree name for a directory — its basename, which is what `claim.js`
|
|
33
|
+
// stores. Returns null for an empty/garbage input so callers get "unknown"
|
|
34
|
+
// rather than a name that cannot match anything.
|
|
35
|
+
//
|
|
36
|
+
// Backslashes are normalized BEFORE the basename, then `path.posix` does the
|
|
37
|
+
// split, so the answer does not depend on which platform is asking. Plain
|
|
38
|
+
// `path.basename` is the platform's: on POSIX it splits only on `/`, so a
|
|
39
|
+
// Windows path arrives back whole — `C:\…\task-1003837` would BE the worktree
|
|
40
|
+
// name, matching nothing. This repo keeps finding that same defect (task
|
|
41
|
+
// 1003821 was two CI-invisible instances of it), and `scripts/gds/worktree.js`
|
|
42
|
+
// already normalizes backslashes first for exactly this reason.
|
|
43
|
+
function worktreeNameFor(dir) {
|
|
44
|
+
const d = String(dir || '').trim().replace(/\\/g, '/').replace(/\/+$/, '');
|
|
45
|
+
if (!d) return null;
|
|
46
|
+
return path.posix.basename(d) || null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { claimForWorktree, worktreeNameFor };
|
package/src/module-api.js
CHANGED
|
@@ -71,7 +71,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
71
71
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
72
72
|
// the entry to that file. Look for a version's history there, not here.
|
|
73
73
|
// ---------------------------------------------------------------------------
|
|
74
|
-
const CORE_VERSION = '1.19.
|
|
74
|
+
const CORE_VERSION = '1.19.690'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
75
75
|
|
|
76
76
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
77
77
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
package/tests/conductor.mjs
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { strict as assert } from 'node:assert';
|
|
12
12
|
import { createRequire } from 'node:module';
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
13
14
|
import { requireHostModule } from './helpers.mjs';
|
|
14
15
|
|
|
15
16
|
const require = createRequire(import.meta.url);
|
|
@@ -129,11 +130,15 @@ test('wandering: lowest priority — emitted after methodology/status advisories
|
|
|
129
130
|
assert.equal(a[a.length - 1].id, 'wandering', 'wandering is emitted last');
|
|
130
131
|
});
|
|
131
132
|
|
|
133
|
+
// The nudge says "your claim's", singular, so it is measured against the claim
|
|
134
|
+
// bound to THIS worktree (task 1003837). These three carry a worktree_name and a
|
|
135
|
+
// matching `worktree` for that reason; the four below pin what changed.
|
|
132
136
|
test('outside-touches fires for files outside the claim touches[]', () => {
|
|
133
137
|
const a = conductor.evaluateTriggers({
|
|
134
138
|
changedFiles: ['src/bongos/grader.js', 'server.js'],
|
|
135
139
|
prompt: 'edits',
|
|
136
|
-
activeClaims: [{ task_id: 1, title: 't', touches: ['src/bongos/grader.js'] }],
|
|
140
|
+
activeClaims: [{ task_id: 1, title: 't', touches: ['src/bongos/grader.js'], worktree_name: 'task-1' }],
|
|
141
|
+
worktree: 'task-1',
|
|
137
142
|
});
|
|
138
143
|
const outside = a.find((x) => x.id === 'outside-touches');
|
|
139
144
|
assert.ok(outside, 'expected outside-touches advisory');
|
|
@@ -144,7 +149,8 @@ test('outside-touches does NOT fire when all changes are covered', () => {
|
|
|
144
149
|
const a = conductor.evaluateTriggers({
|
|
145
150
|
changedFiles: ['src/bongos/grader.js', 'migrations/053_x.sql'],
|
|
146
151
|
prompt: 'edits',
|
|
147
|
-
activeClaims: [{ task_id: 1, title: 't', touches: ['src/bongos/grader.js', 'migrations/'] }],
|
|
152
|
+
activeClaims: [{ task_id: 1, title: 't', touches: ['src/bongos/grader.js', 'migrations/'], worktree_name: 'task-1' }],
|
|
153
|
+
worktree: 'task-1',
|
|
148
154
|
});
|
|
149
155
|
assert.ok(!ids(a).includes('outside-touches'));
|
|
150
156
|
});
|
|
@@ -153,7 +159,71 @@ test('outside-touches does NOT fire when the claim has empty touches[] (fresh ta
|
|
|
153
159
|
const a = conductor.evaluateTriggers({
|
|
154
160
|
changedFiles: ['src/bongos/grader.js'],
|
|
155
161
|
prompt: 'edits',
|
|
156
|
-
activeClaims: [{ task_id: 1, title: 't', touches: [] }],
|
|
162
|
+
activeClaims: [{ task_id: 1, title: 't', touches: [], worktree_name: 'task-1' }],
|
|
163
|
+
worktree: 'task-1',
|
|
164
|
+
});
|
|
165
|
+
assert.ok(!ids(a).includes('outside-touches'));
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// THE BUG (task 1003837). The pool measured the line partly against somebody
|
|
169
|
+
// else's declared paths. The shape is the one observed on 2026-09-10: the claim
|
|
170
|
+
// you are in declares three exact files, a concurrent claim declares four broad
|
|
171
|
+
// directories, and those directories cover the very files the nudge should have
|
|
172
|
+
// named. Under the pool this fired with ONE file; it must name both.
|
|
173
|
+
test('outside-touches ignores a CONCURRENT claim\'s touches[] — the under-report', () => {
|
|
174
|
+
const a = conductor.evaluateTriggers({
|
|
175
|
+
changedFiles: ['modules/hall-ui/public/studio.js', 'scripts/gds/ship.js', 'docs/adr/0280-x.md'],
|
|
176
|
+
prompt: 'edits',
|
|
177
|
+
activeClaims: [
|
|
178
|
+
{ task_id: 1, title: 'mine', touches: ['modules/hall-ui/public/studio.js'], worktree_name: 'task-1' },
|
|
179
|
+
{ task_id: 2, title: 'theirs', touches: ['scripts/gds/', 'src/bongos/', 'modules/', 'clients/'], worktree_name: 'task-2' },
|
|
180
|
+
],
|
|
181
|
+
worktree: 'task-1',
|
|
182
|
+
});
|
|
183
|
+
const outside = a.find((x) => x.id === 'outside-touches');
|
|
184
|
+
assert.ok(outside, 'expected outside-touches advisory');
|
|
185
|
+
// studio.js is covered by MY claim; the other two are not covered by anything
|
|
186
|
+
// of mine. Under the pool, `modules/` and `scripts/gds/` swallowed them and
|
|
187
|
+
// the line read "1 changed file(s)".
|
|
188
|
+
assert.match(outside.text, /^2 changed file\(s\)/, 'both uncovered files must be counted, not one');
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test('outside-touches counts only what MY claim fails to cover', () => {
|
|
192
|
+
const a = conductor.evaluateTriggers({
|
|
193
|
+
changedFiles: ['modules/hall-ui/public/studio.js', 'scripts/gds/ship.js', 'docs/adr/0280-x.md'],
|
|
194
|
+
prompt: 'edits',
|
|
195
|
+
activeClaims: [
|
|
196
|
+
{ task_id: 1, title: 'mine', touches: ['modules/hall-ui/public/studio.js'], worktree_name: 'task-1' },
|
|
197
|
+
{ task_id: 2, title: 'theirs', touches: ['scripts/gds/', 'modules/'], worktree_name: 'task-2' },
|
|
198
|
+
],
|
|
199
|
+
worktree: 'task-1',
|
|
200
|
+
});
|
|
201
|
+
const outside = a.find((x) => x.id === 'outside-touches');
|
|
202
|
+
assert.match(outside.text, /scripts\/gds\/ship\.js/, "the other claim's directory must not cover my diff");
|
|
203
|
+
assert.match(outside.text, /docs\/adr\/0280-x\.md/);
|
|
204
|
+
assert.doesNotMatch(outside.text, /studio\.js/, 'my own declared file is still covered');
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// The owner's call, recorded in the task: an unbound tree is SILENT rather than
|
|
208
|
+
// falling back to the pool. There is no "your claim" to measure against, and a
|
|
209
|
+
// number derived from another claim's touches[] is the defect, not a degraded
|
|
210
|
+
// version of it. Trigger 1 still covers having no claim at all.
|
|
211
|
+
test('outside-touches is SILENT when no claim is bound to this worktree', () => {
|
|
212
|
+
const claims = [{ task_id: 2, title: 'theirs', touches: ['scripts/gds/'], worktree_name: 'task-2' }];
|
|
213
|
+
for (const worktree of ['cloud-bongos', null, undefined, '']) {
|
|
214
|
+
const a = conductor.evaluateTriggers({
|
|
215
|
+
changedFiles: ['server.js', 'scripts/gds/ship.js'], prompt: 'edits', activeClaims: claims, worktree,
|
|
216
|
+
});
|
|
217
|
+
assert.ok(!ids(a).includes('outside-touches'), `unbound tree (${String(worktree)}) must not borrow another claim's touches[]`);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test('outside-touches is SILENT for a claim predating the worktree_name column', () => {
|
|
222
|
+
const a = conductor.evaluateTriggers({
|
|
223
|
+
changedFiles: ['server.js'],
|
|
224
|
+
prompt: 'edits',
|
|
225
|
+
activeClaims: [{ task_id: 1, title: 'old', touches: ['src/bongos/'], worktree_name: null }],
|
|
226
|
+
worktree: 'task-1',
|
|
157
227
|
});
|
|
158
228
|
assert.ok(!ids(a).includes('outside-touches'));
|
|
159
229
|
});
|
|
@@ -478,6 +548,36 @@ test('attribution: claimForWorktree ignores claims with no recorded worktree', (
|
|
|
478
548
|
assert.equal(dispatch.claimForWorktree(null, 'task-x'), null);
|
|
479
549
|
});
|
|
480
550
|
|
|
551
|
+
// ONE DEFINITION, BOTH CONDUCTORS (task 1003837). The dispatcher and the hook
|
|
552
|
+
// answer "which claim is this tree?" and must not answer it differently — the
|
|
553
|
+
// drift this file's own header warns about, after the Phase 4 dogfood found a
|
|
554
|
+
// hand-copied pattern set. The dispatcher re-exports the shared rule, so identity
|
|
555
|
+
// is the assertion: a second copy would pass a behavioural test and fail this one.
|
|
556
|
+
test('attribution: the hook and the dispatcher share ONE claimForWorktree', () => {
|
|
557
|
+
const shared = require('../src/bongos/claim-attribution.js');
|
|
558
|
+
assert.equal(dispatch.claimForWorktree, shared.claimForWorktree, 'the dispatcher must re-export the shared rule, not re-derive it');
|
|
559
|
+
// …and the hook resolves that same module rather than inlining a find().
|
|
560
|
+
const hookSrc = readFileSync(new URL('../.claude/hooks/conductor.js', import.meta.url), 'utf8');
|
|
561
|
+
assert.match(hookSrc, /requireCore\('src\/bongos\/claim-attribution'\)/, 'the hook resolves the shared module');
|
|
562
|
+
assert.doesNotMatch(hookSrc, /flatMap\(\(c\) => \(Array\.isArray\(c\.touches\)/, 'the touches POOL must not come back');
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
// PLATFORM-INDEPENDENT on purpose. These assertions must hold identically on the
|
|
566
|
+
// Windows box a builder runs on and the POSIX box CI runs on — `path.basename`
|
|
567
|
+
// splits only on `/` under POSIX, so a Windows path would come back whole and
|
|
568
|
+
// match no claim, and this test would red on CI while passing locally.
|
|
569
|
+
test('attribution: worktreeNameFor is the basename claim.js records, on either platform', () => {
|
|
570
|
+
const { worktreeNameFor } = require('../src/bongos/claim-attribution.js');
|
|
571
|
+
assert.equal(worktreeNameFor('/repo/.claude/worktrees/task-1003837'), 'task-1003837');
|
|
572
|
+
assert.equal(worktreeNameFor('C:\\repo\\.claude\\worktrees\\task-1003837'), 'task-1003837', 'a Windows path must resolve the same way under POSIX');
|
|
573
|
+
assert.equal(worktreeNameFor('C:/repo/.claude/worktrees/task-1003837'), 'task-1003837');
|
|
574
|
+
assert.equal(worktreeNameFor('/repo/.claude/worktrees/task-1003837/'), 'task-1003837', 'a trailing separator must not erase the name');
|
|
575
|
+
assert.equal(worktreeNameFor('C:\\repo\\worktrees\\task-1003837\\'), 'task-1003837');
|
|
576
|
+
assert.equal(worktreeNameFor(null), null);
|
|
577
|
+
assert.equal(worktreeNameFor(''), null);
|
|
578
|
+
assert.equal(worktreeNameFor(' '), null);
|
|
579
|
+
});
|
|
580
|
+
|
|
481
581
|
test('isCovered now delegates to the canonical matcher (still correct)', () => {
|
|
482
582
|
// Regression guard for the dedup: isCovered is a thin wrapper over
|
|
483
583
|
// path-match.matchOne (relocated from the deleted touches-scan in task 879),
|