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.
@@ -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
 
@@ -184,7 +184,9 @@
184
184
  "project": "TOGa Commerce",
185
185
  "framework": "2.0",
186
186
  "role": "app",
187
- "dependsOn": ["api2"]
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: almost certainly a fresh CI bump (non-fast-forward) loop and retry
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: almost certainly a fresh CI bump (non-fast-forward) — retry
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}`); process.exitCode = 1;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.479",
3
+ "version": "1.0.481",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",