toga-ai 1.0.479 → 1.0.481
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/knowledge/INDEX.md +1 -0
- package/knowledge/registry.json +10 -1
- package/knowledge/standalone/apps/claude/INDEX.md +6 -0
- package/knowledge/standalone/apps/claude/workflows/harness-distribution.md +57 -0
- package/knowledge/standalone/apps/claude/workflows/knowledge-publish-pipeline.md +72 -0
- package/knowledge.js +37 -6
- package/package.json +1 -1
package/knowledge/INDEX.md
CHANGED
|
@@ -39,6 +39,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
39
39
|
- **togatech** (TOGA Technology Website) — 4 doc(s) → [standalone/apps/togatech/INDEX.md](standalone/apps/togatech/INDEX.md)
|
|
40
40
|
- **websocket** (WebSocket Server) — 2 doc(s) → [standalone/apps/websocket/INDEX.md](standalone/apps/websocket/INDEX.md)
|
|
41
41
|
- **forward** (Forwarder) — 3 doc(s) → [standalone/apps/forward/INDEX.md](standalone/apps/forward/INDEX.md)
|
|
42
|
+
- **claude** (Claude Harness) — 2 doc(s) → [standalone/apps/claude/INDEX.md](standalone/apps/claude/INDEX.md)
|
|
42
43
|
|
|
43
44
|
## Clients
|
|
44
45
|
|
package/knowledge/registry.json
CHANGED
|
@@ -184,7 +184,9 @@
|
|
|
184
184
|
"project": "TOGa Commerce",
|
|
185
185
|
"framework": "2.0",
|
|
186
186
|
"role": "app",
|
|
187
|
-
"dependsOn": [
|
|
187
|
+
"dependsOn": [
|
|
188
|
+
"api2"
|
|
189
|
+
]
|
|
188
190
|
},
|
|
189
191
|
{
|
|
190
192
|
"repo": "toga",
|
|
@@ -227,5 +229,12 @@
|
|
|
227
229
|
"api2",
|
|
228
230
|
"worker2"
|
|
229
231
|
]
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"repo": "claude",
|
|
235
|
+
"project": "Claude Harness",
|
|
236
|
+
"framework": "standalone",
|
|
237
|
+
"role": "app",
|
|
238
|
+
"dependsOn": []
|
|
230
239
|
}
|
|
231
240
|
]
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# claude (Claude Harness) — standalone knowledge
|
|
2
|
+
|
|
3
|
+
| Doc | Summary | Files |
|
|
4
|
+
|-----|---------|-------|
|
|
5
|
+
| [Harness Distribution — How a knowledge.js Fix Reaches Teammates](workflows/harness-distribution.md) | There are **two copies** of `knowledge.js` on a developer's machine, and the skills use only one of them. | claude/scripts/install.js, claude/knowledge.js |
|
|
6
|
+
| [Knowledge Base Publish / Push Pipeline](workflows/knowledge-publish-pipeline.md) | How `/capture` and `/session-save` get knowledge docs from your working tree into `agilantsolutions/claude` on `_main`. | claude/knowledge.js |
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Harness Distribution — How a knowledge.js Fix Reaches Teammates
|
|
3
|
+
framework: "standalone"
|
|
4
|
+
repo: claude
|
|
5
|
+
project: Claude Harness
|
|
6
|
+
client: shared
|
|
7
|
+
type: workflow
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-07-30
|
|
10
|
+
owners: ["jcardinal"]
|
|
11
|
+
files:
|
|
12
|
+
- claude/scripts/install.js
|
|
13
|
+
- claude/knowledge.js
|
|
14
|
+
related:
|
|
15
|
+
- standalone/apps/claude/workflows/knowledge-publish-pipeline.md
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Summary
|
|
19
|
+
There are **two copies** of `knowledge.js` on a developer's machine, and the skills use only
|
|
20
|
+
one of them. Publishing a new npm version does **not** fix a teammate's `/capture` — the git
|
|
21
|
+
clone is what has to be updated. `npx toga-ai` is the mechanism that does it.
|
|
22
|
+
|
|
23
|
+
## How it works
|
|
24
|
+
- `/capture`, `/kickoff`, and `/session-save` all invoke
|
|
25
|
+
`node "<TEAM_REPO>/knowledge.js"` — that is the **git clone's** copy at
|
|
26
|
+
`~/toga-tech/knowledge.js`.
|
|
27
|
+
- The npm bundle installs a *separate* copy into the project's `.claude/knowledge.js`. The
|
|
28
|
+
skills do not run it, so a new published npm version alone changes nothing about captures.
|
|
29
|
+
- `npx toga-ai` runs `tryGitPull()` (`scripts/install.js`, ~lines 182-210, called at ~line
|
|
30
|
+
548), which does `git pull --ff-only` inside `~/toga-tech`. **That pull is what actually
|
|
31
|
+
delivers a fixed `knowledge.js`.**
|
|
32
|
+
- Equivalent manual route: `git -C ~/toga-tech pull`.
|
|
33
|
+
|
|
34
|
+
Verify a teammate has a given fix:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
git -C ~/toga-tech log --oneline -1 knowledge.js
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
For the 2026-07-30 publish/push fix, that must show `47fbdc4` or newer.
|
|
41
|
+
|
|
42
|
+
## Gotchas / known issues
|
|
43
|
+
- **`npx toga-ai` can silently fail to update the knowledge repo** (open issue, not yet
|
|
44
|
+
fixed). On a non-zero `git pull --ff-only` exit, `tryGitPull()` returns
|
|
45
|
+
`{ pulled: false, newDocs: 0, message: null }` — the git stderr is discarded and **nothing
|
|
46
|
+
is printed to the developer**. A teammate whose pull is refused (e.g. local edits to a
|
|
47
|
+
tracked file that the incoming commits also touch — `.claude/settings.json` is the usual
|
|
48
|
+
culprit) will believe they are up to date while still running old tooling.
|
|
49
|
+
Suggested fix, not applied: surface the git stderr in the `pulled: false` path so a refused
|
|
50
|
+
pull is visible. Same defect class as the swallowed-stderr bug in the publish pipeline.
|
|
51
|
+
- Until that is fixed, if a harness fix "didn't take", check the pull manually rather than
|
|
52
|
+
trusting `npx toga-ai`'s output.
|
|
53
|
+
|
|
54
|
+
## Change history
|
|
55
|
+
- 2026-07-30 — Documented that the skills run the git clone's `knowledge.js` (not the npm
|
|
56
|
+
bundle copy), so `npx toga-ai` / `git -C ~/toga-tech pull` is required to distribute a
|
|
57
|
+
harness fix; recorded the silent `tryGitPull()` failure as a known issue. (jcardinal)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Knowledge Base Publish / Push Pipeline
|
|
3
|
+
framework: "standalone"
|
|
4
|
+
repo: claude
|
|
5
|
+
project: Claude Harness
|
|
6
|
+
client: shared
|
|
7
|
+
type: workflow
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-07-30
|
|
10
|
+
owners: ["jcardinal"]
|
|
11
|
+
files:
|
|
12
|
+
- claude/knowledge.js
|
|
13
|
+
related:
|
|
14
|
+
- standalone/apps/claude/workflows/harness-distribution.md
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Summary
|
|
18
|
+
How `/capture` and `/session-save` get knowledge docs from your working tree into
|
|
19
|
+
`agilantsolutions/claude` on `_main`. Both go through `knowledge.js` — `cmdPublish()` for
|
|
20
|
+
`/capture`, `pushStaged()` for `/session-save`. Both run the same three-step git sequence,
|
|
21
|
+
and both had the same defect until 2026-07-30 (`47fbdc4`): they reported `PUSH_FAILED` on
|
|
22
|
+
every run while a manual `git push` always worked.
|
|
23
|
+
|
|
24
|
+
## How it works
|
|
25
|
+
The publish sequence, run inside the team repo clone (`~/toga-tech`):
|
|
26
|
+
|
|
27
|
+
1. `node knowledge.js validate` — aborts the publish on any `ERROR:` line.
|
|
28
|
+
2. `git add` the touched paths under `knowledge/` and commit.
|
|
29
|
+
3. `git fetch origin <branch>`
|
|
30
|
+
4. `git rebase --autostash origin/<branch>`
|
|
31
|
+
5. `git push origin HEAD:<branch>`
|
|
32
|
+
|
|
33
|
+
Steps 3–5 are wrapped in a 3-attempt retry with a 2s/4s backoff between attempts
|
|
34
|
+
(`sleepSync()`, implemented with `Atomics.wait`). On final failure the tool prints both the
|
|
35
|
+
manual-push command **and** the underlying git error:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
PUBLISH: PUSH_FAILED after 3 attempts — run manually: git -C "<TEAM>" push origin HEAD:_main
|
|
39
|
+
PUBLISH: last git error — <git stderr, first 5 lines>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`/session-save` prints the same pair under a `SESSION:` prefix.
|
|
43
|
+
|
|
44
|
+
Direct commits to `_main` in this repo are intentional and are the documented exception to
|
|
45
|
+
the team's branch-protection rule. Force-pushing is never automatic here either.
|
|
46
|
+
|
|
47
|
+
## Gotchas / known issues
|
|
48
|
+
- **`git rebase` refuses to run while ANY tracked file has unstaged changes** — even a file
|
|
49
|
+
with nothing to do with `knowledge/`. It exits with
|
|
50
|
+
`error: cannot rebase: You have unstaged changes.` That means the rebase failed *before*
|
|
51
|
+
the push was ever attempted, which is why the manual `git push` always succeeded (push
|
|
52
|
+
does not require a clean working tree). `--autostash` is therefore **required, not a
|
|
53
|
+
nicety**: it stashes the unrelated edits, rebases, and restores them. Do not remove it.
|
|
54
|
+
- **This was never a CI race.** The retry loop's original comment claimed it was retrying a
|
|
55
|
+
"CI version-bump race". That diagnosis was wrong, and three instant retries just repeated
|
|
56
|
+
the same deterministic failure three times.
|
|
57
|
+
- **`.claude/settings.json` is tracked in this repo and is edited by the harness itself**, so
|
|
58
|
+
local drift there is expected and recurring. It was the dirty file that made the failure
|
|
59
|
+
100% reproducible on at least one machine. `--autostash` makes publish tolerate it, but the
|
|
60
|
+
drift still wants a periodic `git diff .claude/settings.json` decision — commit it or revert
|
|
61
|
+
it. It is also the file most likely to make `npx toga-ai`'s `git pull --ff-only` refuse (see
|
|
62
|
+
the harness distribution doc).
|
|
63
|
+
- **A `catch` that discards `stderr` disguises a deterministic bug as a flaky one.** This bug
|
|
64
|
+
went undiagnosed for months purely because `catch (e)` threw git's stderr away while git was
|
|
65
|
+
naming the exact cause on every single run. Any retry wrapper around a shell command in this
|
|
66
|
+
repo must surface `e.stderr || e.stdout` on final failure.
|
|
67
|
+
|
|
68
|
+
## Change history
|
|
69
|
+
- 2026-07-30 — Fixed permanent `PUSH_FAILED`: `git rebase` → `git rebase --autostash`, surface
|
|
70
|
+
git stderr as a `last git error` line, and add 2s/4s backoff (`sleepSync()`) between retries.
|
|
71
|
+
Applied identically in `cmdPublish()` and `pushStaged()`, which had the same defect
|
|
72
|
+
(`47fbdc4`). (jcardinal)
|
package/knowledge.js
CHANGED
|
@@ -48,6 +48,17 @@ function fail(msg) {
|
|
|
48
48
|
process.exitCode = 1;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/* Blocking sleep, for backing off between push retries. This script is a
|
|
52
|
+
* short-lived synchronous CLI, so there is no event loop to yield to and an
|
|
53
|
+
* async sleep would mean threading promises through every git call. */
|
|
54
|
+
function sleepSync(ms) {
|
|
55
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* stderr of the last failed push attempt, so reportSession() can say WHY
|
|
59
|
+
* instead of only that it failed. */
|
|
60
|
+
let lastPushError = '';
|
|
61
|
+
|
|
51
62
|
function parseArgs(argv) {
|
|
52
63
|
const out = {};
|
|
53
64
|
for (const arg of argv) {
|
|
@@ -433,15 +444,23 @@ function cmdPublish(args) {
|
|
|
433
444
|
const msg = String(args.msg || 'knowledge: capture session updates').replace(/["\\]/g, "'").slice(0, 200);
|
|
434
445
|
run(`git commit -m "${msg}"`);
|
|
435
446
|
|
|
436
|
-
// 6. rebase-before-push, retrying the CI version-bump race up to 3x
|
|
447
|
+
// 6. rebase-before-push, retrying the CI version-bump race up to 3x.
|
|
448
|
+
// --autostash is REQUIRED, not a nicety: plain `git rebase` aborts with
|
|
449
|
+
// "cannot rebase: You have unstaged changes" if ANY tracked file is dirty,
|
|
450
|
+
// even one unrelated to knowledge/ (.claude/settings.json is the usual
|
|
451
|
+
// culprit — the harness edits it). That failure is not a conflict and not a
|
|
452
|
+
// race, so it fails identically on all 3 attempts and every capture ends in
|
|
453
|
+
// a manual push. --autostash stashes the unrelated edits, rebases, restores.
|
|
454
|
+
let lastError = '';
|
|
437
455
|
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
438
456
|
try {
|
|
439
457
|
run('git fetch origin');
|
|
440
|
-
run(`git rebase origin/${branch}`);
|
|
458
|
+
run(`git rebase --autostash origin/${branch}`);
|
|
441
459
|
run(`git push origin HEAD:${branch}`);
|
|
442
460
|
console.log(`PUBLISH: PUSHED to ${branch} — CI publishes a new npm version; teammates get it on \`npx toga-ai\``);
|
|
443
461
|
return;
|
|
444
462
|
} catch (e) {
|
|
463
|
+
lastError = String((e && (e.stderr || e.stdout)) || (e && e.message) || '').trim();
|
|
445
464
|
let status = '';
|
|
446
465
|
try { status = run('git status --porcelain'); } catch { /* ignore */ }
|
|
447
466
|
if (/^(UU|AA|DD|AU|UA|DU|UD) /m.test(status)) {
|
|
@@ -449,10 +468,15 @@ function cmdPublish(args) {
|
|
|
449
468
|
console.log(`PUBLISH: CONFLICT — rebase onto origin/${branch} hit a real conflict; resolve manually, then re-run publish`);
|
|
450
469
|
process.exitCode = 1; return;
|
|
451
470
|
}
|
|
452
|
-
// else:
|
|
471
|
+
// else: most likely a fresh CI bump (non-fast-forward). Back off before
|
|
472
|
+
// retrying — an instant retry loses the same race three times over.
|
|
473
|
+
if (attempt < 3) sleepSync(2000 * attempt);
|
|
453
474
|
}
|
|
454
475
|
}
|
|
476
|
+
// Always surface WHY. Swallowing stderr here is what disguised the dirty-tree
|
|
477
|
+
// bug above as a flaky CI race for months.
|
|
455
478
|
console.log(`PUBLISH: PUSH_FAILED after 3 attempts — run manually: git -C "${TEAM}" push origin HEAD:${branch}`);
|
|
479
|
+
if (lastError) console.log('PUBLISH: last git error — ' + lastError.split('\n').slice(0, 5).join(' | '));
|
|
456
480
|
process.exitCode = 1;
|
|
457
481
|
}
|
|
458
482
|
|
|
@@ -766,20 +790,25 @@ function pushStaged(opts) {
|
|
|
766
790
|
}
|
|
767
791
|
const msg = String((opts && opts.msg) || 'sessions: update').replace(/["\\]/g, "'").slice(0, 200);
|
|
768
792
|
gitRun(`git commit -m "${msg}"`);
|
|
793
|
+
// --autostash is REQUIRED: plain `git rebase` refuses to run while ANY tracked
|
|
794
|
+
// file is dirty, even one unrelated to what we staged, which fails identically
|
|
795
|
+
// on every attempt and forces a manual push. See cmdPublish() for the detail.
|
|
769
796
|
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
770
797
|
try {
|
|
771
798
|
gitRun('git fetch origin');
|
|
772
|
-
gitRun(`git rebase origin/${branch}`);
|
|
799
|
+
gitRun(`git rebase --autostash origin/${branch}`);
|
|
773
800
|
gitRun(`git push origin HEAD:${branch}`);
|
|
774
801
|
return 'PUSHED';
|
|
775
802
|
} catch (e) {
|
|
803
|
+
lastPushError = String((e && (e.stderr || e.stdout)) || (e && e.message) || '').trim();
|
|
776
804
|
let status = '';
|
|
777
805
|
try { status = gitRun('git status --porcelain'); } catch { /* ignore */ }
|
|
778
806
|
if (/^(UU|AA|DD|AU|UA|DU|UD) /m.test(status)) {
|
|
779
807
|
try { gitRun('git rebase --abort'); } catch { /* ignore */ }
|
|
780
808
|
return 'CONFLICT';
|
|
781
809
|
}
|
|
782
|
-
// else:
|
|
810
|
+
// else: most likely a fresh CI bump (non-fast-forward) — back off, retry
|
|
811
|
+
if (attempt < 3) sleepSync(2000 * attempt);
|
|
783
812
|
}
|
|
784
813
|
}
|
|
785
814
|
return 'PUSH_FAILED';
|
|
@@ -792,7 +821,9 @@ function reportSession(status, branch) {
|
|
|
792
821
|
if (status === 'NO_CHANGES') { console.log('SESSION: NO_CHANGES — nothing new to push'); return; }
|
|
793
822
|
if (status === 'CONFLICT') { console.log(`SESSION: CONFLICT — rebase onto origin/${br} hit a real conflict; resolve manually, then retry`); process.exitCode = 1; return; }
|
|
794
823
|
if (status && status.startsWith('ABORT_OUTSIDE')) { console.log('SESSION: ABORT_OUTSIDE_SESSIONS — refusing to commit non-session files: ' + status.slice('ABORT_OUTSIDE:'.length)); process.exitCode = 1; return; }
|
|
795
|
-
console.log(`SESSION: PUSH_FAILED after 3 attempts — run manually: git -C "${__dirname}" push origin HEAD:${br}`);
|
|
824
|
+
console.log(`SESSION: PUSH_FAILED after 3 attempts — run manually: git -C "${__dirname}" push origin HEAD:${br}`);
|
|
825
|
+
if (lastPushError) console.log('SESSION: last git error — ' + lastPushError.split('\n').slice(0, 5).join(' | '));
|
|
826
|
+
process.exitCode = 1;
|
|
796
827
|
}
|
|
797
828
|
|
|
798
829
|
function fetchQuiet() {
|
package/package.json
CHANGED