@atlassian/chippy 0.0.1-beta.1 → 0.0.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +142 -54
  2. package/bin/chippy.mjs +53 -95
  3. package/bin/pack.mjs +20 -0
  4. package/bin/skill-dir.mjs +15 -0
  5. package/lib/avalanche.js +54 -23
  6. package/lib/avalanche.js.map +2 -2
  7. package/lib/bootstrap.js +20 -1
  8. package/lib/bootstrap.js.map +3 -3
  9. package/lib/extension/avalanche.es.js +173 -34
  10. package/lib/extension/avalanche.es.js.map +1 -1
  11. package/lib/extension/chippy-page-bridge.js +164 -44
  12. package/lib/mcp-skill/README.md +2 -4
  13. package/lib/mcp-skill/SKILL.md +21 -16
  14. package/lib/mcp-skill/extension/armed-reload-startup.js +83 -0
  15. package/lib/mcp-skill/extension/avalanche.es.js +173 -34
  16. package/lib/mcp-skill/extension/avalanche.es.js.map +1 -1
  17. package/lib/mcp-skill/extension/avalanche.iife.js +173 -34
  18. package/lib/mcp-skill/extension/avalanche.iife.js.map +1 -1
  19. package/lib/mcp-skill/extension/background.js +2 -0
  20. package/lib/mcp-skill/extension/chippy-page-bridge.js +164 -44
  21. package/lib/mcp-skill/extension/content-script.js +23 -0
  22. package/lib/mcp-skill/extension/devtools-hook-shim.js +18 -1
  23. package/lib/mcp-skill/extension/early-bootstrap.js +3 -1
  24. package/lib/mcp-skill/extension/manifest.json +1 -1
  25. package/lib/mcp-skill/extension/panel-settings.js +2 -20
  26. package/lib/mcp-skill/extension/startup-handoff.js +30 -0
  27. package/lib/mcp-skill/references/README.md +2 -2
  28. package/lib/mcp-skill/references/avalanche-detector.md +3 -3
  29. package/lib/mcp-skill/references/browser-runtime-api.md +4 -4
  30. package/lib/mcp-skill/references/command-output-formats.md +12 -2
  31. package/lib/mcp-skill/references/command-surface.md +6 -6
  32. package/lib/mcp-skill/references/mode-a-injection.md +43 -9
  33. package/lib/mcp-skill/references/playwright-cli-setup.md +5 -5
  34. package/lib/mcp-skill/references/playwright-probes.md +1 -1
  35. package/lib/mcp-skill/references/recipes.txt +1 -1
  36. package/lib/mcp-skill/references/stability-render.md +4 -4
  37. package/lib/mcp-skill/references/test-mode-playbook.md +2 -1
  38. package/lib/mcp-skill/references/troubleshooting.md +6 -1
  39. package/lib/mcp-skill/scripts/chippy-helper-server.js +116 -58
  40. package/lib/mcp-skill/scripts/chippy.sh +41 -7
  41. package/lib/mcp-skill/scripts/helper-access.js +20 -6
  42. package/lib/mcp-skill/scripts/helper-proxy.js +87 -0
  43. package/lib/mcp-skill/scripts/page-socket-trust.js +81 -0
  44. package/lib/plugin.js.map +2 -2
  45. package/lib/storybook/connection-fallback.jsx +1 -1
  46. package/lib/storybook/manager-owner.js +42 -0
  47. package/lib/storybook/manager.jsx +22 -0
  48. package/lib/storybook/preview.js +53 -18
  49. package/lib/storybook/setup.js +15 -4
  50. package/lib/types/chippy-bridge/fiber-source.d.ts +14 -3
  51. package/lib/types/chippy-bridge/react-renderer.d.ts +30 -5
  52. package/lib/types/chippy-bridge/recorded-source.d.ts +44 -0
  53. package/lib/types/debug-render/core.d.ts +6 -0
  54. package/package.json +3 -1
@@ -34,7 +34,7 @@ Important behavior:
34
34
  - extension-owned pages initially render one small bottom-corner Chippy launcher; expanding it reveals compact `P1 — drag to source` / `P2 — drag to source` rows that serve as each probe's dock and detail surface
35
35
  - dragging a probe does not collapse the panel; an undocked probe uses document coordinates and stays anchored to its target while the page scrolls
36
36
  - the extension toolbar popup contains only Enable and Disable for the current origin; helper and proxy configuration stay in the in-page panel
37
- - extension-owned panels expose the helper URL and optional forward-port settings behind the gear; the isolated content script mounts the form in a closed shadow root, with no iframe and no page access to extension storage
37
+ - extension-owned panels expose the helper URL and plugin settings behind the gear; the isolated content script mounts the form in a closed shadow root, with no iframe and no page access to extension storage
38
38
  - Storybook host-panel mode keeps a standalone dock in the preview frame because probe details live in the manager frame
39
39
  - docked probes are inactive
40
40
  - while dragging:
@@ -66,7 +66,7 @@ The helper must be a real local process reachable by the user's browser. The age
66
66
 
67
67
  `contextUiEnabled` defaults to `true`. It controls only context-provider cards in the active probe UI, including Storybook marker cards; generic probe resolution and snapshots continue when it is false.
68
68
 
69
- Source reads are cross-version. React <=18 locations come from `_debugSource`. React 19 locations start at `_debugStack`, skip dependency frames, and resolve the served module through its inline or external Source Map v3 payload. Snapshot publication is immediate and may be followed by a `<reason>-source-resolved` snapshot when the authored position differs; `describe-component` and `explain-component` await the mapped position.
69
+ Source reads are cross-version. React <=18 locations come from `_debugSource`; when the module the page loaded for that file carries a source map, its recorded literal is mapped back to correct lines shifted by an earlier transform. React 19 locations start at `_debugStack`, skip dependency frames, and resolve the served module through its inline or external Source Map v3 payload. Snapshot publication is immediate and may be followed by a `<reason>-source-resolved` snapshot when the authored position differs; `find-fibers`, `describe-component`, and `explain-component` await the mapped position.
70
70
 
71
71
  `integration.kind` is `extension`, `import`, or `storybook`. `who` names the host that established Chippy and `source` points to its real setup source file. Import integrations report `reinjectsOnReload: false`; a full reload/navigation requires the user or automation to run the import again.
72
72
 
@@ -83,10 +83,10 @@ The Avalanche panel installs and refreshes capture state while selected. It grou
83
83
  When either panel is `failed` or `reconnecting`, it may offer:
84
84
 
85
85
  ```bash
86
- npx @atlassian/chippy install
86
+ npx skills add atlassian-labs/chippy --skill chippy -g
87
87
  ```
88
88
 
89
- That command installs Chippy when it is absent. It does not start or verify the helper; the agent still follows [troubleshooting](troubleshooting.md) and runs the helper checks.
89
+ That command installs the Chippy skill with the skills CLI when it is absent. It does not start or verify the helper; the agent still follows [troubleshooting](troubleshooting.md) and runs the helper checks.
90
90
 
91
91
  ## `window.__CHIPPY.resolveProbes` / `window.__CHIPPY.resolveProbesCompact`
92
92
 
@@ -53,10 +53,11 @@ Fields: `ok` (bool), `now` (ISO 8601 timestamp).
53
53
 
54
54
  ## `inject-url`
55
55
 
56
- Prints a lifecycle warning to stderr:
56
+ Prints a lifecycle warning and the second required asset to stderr:
57
57
 
58
58
  ```text
59
59
  Import integration is document-scoped: a full reload/navigation removes Chippy and requires the user or automation to inject it again.
60
+ The classic inject.js installs Chippy only; load http://127.0.0.1:7823/__chippy/avalanche.iife.js after it for bridge and Avalanche commands, or import http://127.0.0.1:7823/__chippy/inject.mjs instead.
60
61
  ```
61
62
 
62
63
  Then prints the classic-script URL to stdout, one line:
@@ -153,7 +154,7 @@ Records are separated by one blank line. `score` is useful only for ordering res
153
154
 
154
155
  `buildMode`: `"dev" | "production" | "unknown"`
155
156
 
156
- `dev` source evidence is either React <=18 `_debugSource` or React 19 `_debugStack`. For React 19, the first snapshot may carry the served-module URL and generated position; the bridge resolves the module's inline or external Source Map v3 payload in the page realm and republishes the authored `fileName`, `lineNumber`, and `columnNumber` with reason `<original-reason>-source-resolved`. Explicit component descriptions await that resolution. If the module or map is unavailable, the served position remains the fallback.
157
+ `dev` source evidence is either React <=18 `_debugSource` or React 19 `_debugStack`. For React 19, the first snapshot may carry the served-module URL and generated position; the bridge resolves the module's inline or external Source Map v3 payload in the page realm and republishes the authored `fileName`, `lineNumber`, and `columnNumber` with reason `<original-reason>-source-resolved`. For React <=18, the same resolution corrects a `_debugSource` line that a dev-server transform shifted; `fileName` stays the recorded path. `find-fibers` and explicit component descriptions await that resolution. If the module or map is unavailable, the recorded or served position remains the fallback.
157
158
 
158
159
  **`integration`** — identifies who established Chippy on this page:
159
160
  ```json
@@ -1034,6 +1035,7 @@ Proxy active:
1034
1035
  "proxy": {
1035
1036
  "active": true,
1036
1037
  "targetPort": 3000,
1038
+ "targetHost": "localhost:3000",
1037
1039
  "originalUrl": "http://localhost:3000/issues/ABC-123",
1038
1040
  "injectTarget": { "pathname": "/issues/ABC-123", "search": "" },
1039
1041
  "startedAt": "2026-04-16T08:00:00.000Z"
@@ -1043,6 +1045,8 @@ Proxy active:
1043
1045
 
1044
1046
  `injectTarget` — the path+search the proxy will inject the Chippy bundle into. Derived from `originalUrl`. `null` if no `originalUrl` was given.
1045
1047
 
1048
+ `targetHost` — where requests are forwarded. The proxy forwards only to this machine: it uses the loopback host of `originalUrl` (`localhost`, `127.0.0.1`, `[::1]`), or `localhost` when none is given, and reaches servers listening on either IPv4 or IPv6. Into the injected document it adds the DevTools hook shim and the Avalanche runtime before page scripts, and the page bridge at the end of the body.
1049
+
1046
1050
  ---
1047
1051
 
1048
1052
  ## `proxy-start <targetPort> [originalUrl]`
@@ -1054,6 +1058,7 @@ Success:
1054
1058
  "proxy": {
1055
1059
  "active": true,
1056
1060
  "targetPort": 3000,
1061
+ "targetHost": "localhost:3000",
1057
1062
  "originalUrl": "http://localhost:3000/issues/ABC-123",
1058
1063
  "injectTarget": { "pathname": "/issues/ABC-123", "search": "" },
1059
1064
  "startedAt": "2026-04-16T08:00:00.000Z"
@@ -1066,6 +1071,11 @@ Bad port:
1066
1071
  { "ok": false, "error": "Invalid target port: abc" }
1067
1072
  ```
1068
1073
 
1074
+ Target off this machine:
1075
+ ```json
1076
+ { "ok": false, "error": "Proxy targets must be on this machine; originalUrl host example.com is not a loopback name" }
1077
+ ```
1078
+
1069
1079
  ---
1070
1080
 
1071
1081
  ## `proxy-stop`
@@ -21,7 +21,7 @@ Use recipe search when the operation or recovery route is not already obvious. I
21
21
  | Command | Purpose |
22
22
  |---|---|
23
23
  | `start`, `stop`, `health` | Own the local helper lifecycle and readiness check. `start` backgrounds itself and binds `127.0.0.1`; `start --host 0.0.0.0` binds wide only when the user asks for network access. |
24
- | `inject-url` | Print the classic `inject.js` URL to stdout and the document-scoped reload warning to stderr. Prefer the ESM import recipe for new injection workflows. |
24
+ | `inject-url` | Print the classic `inject.js` URL to stdout, and the document-scoped reload warning plus the second required asset to stderr. `inject.js` alone installs Chippy without the bridge; load `avalanche.iife.js` after it, or prefer the single ESM import for new injection workflows. |
25
25
  | `pages [slice]` | List currently connected pages. This is connection truth. |
26
26
  | `recent-pages` | List recently disconnected page summaries for reload/navigation diagnosis. |
27
27
  | `page <clientId> [slice]` | Read one connected page with `minimal`, `standard`, or `full` detail. |
@@ -91,9 +91,9 @@ Load [active-stimulus](active-stimulus.md) before an active command. A `fiber:*`
91
91
 
92
92
  | Mode | Use it for | Boundary |
93
93
  |---|---|---|
94
- | `sources` | Default source and commit attribution | No per-component `renderedByCommit` evidence |
95
- | `full` | Per-component render, prop-change, and trasher evidence | Higher collection cost |
96
- | `stats` | Aggregate capture statistics | No source or rendered detail needed |
94
+ | `sources` | Default source and commit attribution; rendered components with prop changes and `isSelfSource` | `trasherCounts` stay zero |
95
+ | `full` | Everything in `sources` plus amplifier (`trasherCounts`) evidence | Higher collection cost |
96
+ | `stats` | Aggregate capture statistics; rendered components with prop changes | No source tracking: `isSelfSource` stays false |
97
97
  | `amplifiers` | Amplifier-focused capture | Specialized analysis; load Avalanche reference first |
98
98
 
99
99
  `avalanche-arm` and `reload-with-avalanche` also accept `captureFibers=true|false`. Keep it `true` when debug-hook or fiber-backed analysis may follow.
@@ -102,7 +102,7 @@ Load [active-stimulus](active-stimulus.md) before an active command. A `fiber:*`
102
102
 
103
103
  `avalanche-commit-list` live `by` values are `collateral`, `sources`, `restarts`, `performedWork`, `effects`, `memos`, `callbacks`, `contextUpdates`, `unmountedComponents`, and `unmountedSubtrees`.
104
104
 
105
- `avalanche-commit-peek` live `include` values are `stats`, `sources`, `timing`, `metrics`, `unmounts`, `actions`, and `rendered`; `atl` is a deprecated alias of `metrics`. `rendered` requires `detectionMode=full`.
105
+ `avalanche-commit-peek` live `include` values are `stats`, `sources`, `timing`, `metrics`, `unmounts`, `actions`, and `rendered`; `atl` is a deprecated alias of `metrics`. `rendered` works in every mode; `trasherCounts` in it need `full` or `amplifiers`.
106
106
 
107
107
  Load [avalanche-detector](avalanche-detector.md) for schemas and evidence-reading rules.
108
108
 
@@ -112,7 +112,7 @@ Load [avalanche-detector](avalanche-detector.md) for schemas and evidence-readin
112
112
  |---|---|
113
113
  | `eval-snippets` | List packaged snippets. |
114
114
  | `eval-page` | Execute one packaged or local snippet in the connected page realm with `--key value` arguments. |
115
- | `page-reload`, `page-wait-reload` | Request a reload and wait for the helper's reconnect transition. For a Storybook integration, `page-reload` emits `FORCE_REMOUNT`, remounting only the current SUT without navigating the preview iframe. |
115
+ | `page-reload`, `page-wait-reload` | Request a reload and wait for the helper's reconnect transition. For a Storybook integration, `page-reload` emits `FORCE_REMOUNT`, remounting only the current SUT without navigating the preview iframe; it fails with HTTP 409 when no rendered story matches the selection. |
116
116
  | `handoff-to-playwright` | Transfer a connected page URL and selected probes into a Playwright session configuration. |
117
117
  | `proxy-state`, `proxy-start`, `proxy-stop` | Inspect or control the helper's local proxy. |
118
118
 
@@ -12,7 +12,7 @@ If the user is using their own browser with the extension, this contract does NO
12
12
 
13
13
  ## Required bootstrap and install order
14
14
 
15
- Mode A has two supported runtime-loading variants. Both end with the same explicit detector install.
15
+ Mode A has two runtime-loading variants that end with the same explicit detector install. To record the initial load, use [pre-page start-up](#pre-page-start-up) instead; it installs the detector before React on every document.
16
16
 
17
17
  1. **Install the DevTools hook shim before navigation.** Use `page.addInitScript({ content: DEVTOOLS_HOOK_SHIM })` before `page.goto(...)`.
18
18
  React-DOM checks `__REACT_DEVTOOLS_GLOBAL_HOOK__` at module-evaluation time. Installing the shim after navigation = no `currentDispatcherRef` = `replayComponentHooks` collapses to fiber-only fallback. The shim file is shipped with the Chippy skill at `extension/devtools-hook-shim.js`; the helper serves it at `/__chippy/devtools-hook-shim.js` (load that contents into the init script).
@@ -21,16 +21,48 @@ Mode A has two supported runtime-loading variants. Both end with the same explic
21
21
  - **Ad-hoc / playwright-cli:** navigate first, then import `http://127.0.0.1:7823/__chippy/inject.mjs` (preferred), or add `inject.js` followed by `avalanche.iife.js`. The classic `inject.js` is intentionally split; bridge commands return `bridge module not loaded` until the IIFE is present.
22
22
  3. **Wait for the page client in `chippy.sh pages`.** A loaded global is not connection proof.
23
23
  4. **Run `chippy.sh avalanche-install <clientId>`.** Loading either runtime does not auto-install the detector in Mode A.
24
- Auto-install is gated on `__CHIPPY_STARTUP_CONFIG.avalanche.autoStart`, which is written by the extension's MAIN-world script in Mode B. Neither Mode A loader writes that startup configuration, so both must trigger install explicitly.
24
+ Auto-install is gated on `__CHIPPY_STARTUP_CONFIG.avalanche.autoStart`, which is written by the extension's MAIN-world script in Mode B. Neither loader above writes that startup configuration, so both must trigger install explicitly.
25
+
26
+ ## Pre-page start-up
27
+
28
+ Use this variant when the agent must record the initial page load without the extension. It writes the startup configuration that the extension writes in Mode B, then adds the early scripts and the page bridge as init scripts. Every document, including each reload, installs Avalanche before React DOM evaluates.
29
+
30
+ Register the init scripts before `page.goto(...)`, in this order. The files ship in this skill's `extension/` folder:
31
+
32
+ ```js
33
+ const read = (file) => fs.readFileSync(path.join(skillDir, 'extension', file), 'utf8');
34
+
35
+ await page.addInitScript(() => {
36
+ globalThis.__CHIPPY_STARTUP_CONFIG = {
37
+ enabled: true,
38
+ helperBaseUrl: 'http://127.0.0.1:7823',
39
+ avalanche: { autoStart: true },
40
+ };
41
+ });
42
+ await page.addInitScript({ content: read('devtools-hook-shim.js') });
43
+ await page.addInitScript({ content: read('avalanche.iife.js') });
44
+ await page.addInitScript({ content: read('early-bootstrap.js') });
45
+ await page.addInitScript({
46
+ content: ['inspect-react.js', 'panel-dom.js', 'layout-lab.js', 'resolve-probes.js', 'chippy-page-bridge.js']
47
+ .map(read)
48
+ .join('\n'),
49
+ });
50
+ await page.goto(url);
51
+ ```
52
+
53
+ - Skip the explicit `avalanche-install`; the detector is already installed and recording. `avalanche.options` accepts `detectionMode` and `captureFibers`, and `avalanche.max` sets the history size.
54
+ - To capture a load, call `page.reload()` and let the page settle. Do not call `avalanche-reset` between the reload and the read; it clears the boot commits.
55
+ - Read the capture with `chippy.sh avalanche-status <clientId>` and `avalanche-export` once the reloaded page appears in `chippy.sh pages`. If the client has not reconnected yet, read `window.__AVALANCHE__.exportLast()` in the page.
25
56
 
26
57
  ## On reload (`page.reload(...)`)
27
58
 
28
59
  - Init scripts survive reload. The packaged loader restores its runtimes automatically; the ad-hoc variant must load them again.
29
- - Both variants receive a new client and detector instance. Re-resolve the client, then repeat the explicit install.
60
+ - The client id is kept in the tab's `sessionStorage`, so the reloaded page normally reconnects under the same id (`page-wait-reload` reports `reloaded: true`). The connection and the detector are new: repeat the explicit install.
61
+ - If the runtimes are not loaded again, the page never reconnects: `page-wait-reload` reports `reloaded: false`, and `reload-with-avalanche` exits with an error instead of reading status from a client that no longer exists.
30
62
 
31
- ## Boot-time commit loss — known limitation
63
+ ## Boot-time commit loss in the packaged and ad-hoc variants
32
64
 
33
- The ad-hoc `addScriptTag` variant loads the runtimes after page code. The packaged init-script variant loads them before page code, but its detector still remains uninstalled until the helper observes the new client and sends the explicit install.
65
+ This limit does not apply to [pre-page start-up](#pre-page-start-up). The ad-hoc `addScriptTag` variant loads the runtimes after page code. The packaged init-script variant loads them before page code, but its detector still remains uninstalled until the helper observes the new client and sends the explicit install.
34
66
 
35
67
  Any React commit that fires between page mount and your `avalanche-install` call is **invisible to Avalanche**. This includes:
36
68
  - initial mount
@@ -38,7 +70,7 @@ Any React commit that fires between page mount and your `avalanche-install` call
38
70
  - Suspense fallback → content swaps
39
71
  - route transitions on first paint
40
72
 
41
- **Workaround:** stories under test must expose a manual trigger (e.g. `window.__triggerSwap()`) that you call AFTER `avalanche-install`. Document this trigger in the test fixture so the test reads end-to-end.
73
+ **Workaround:** use [pre-page start-up](#pre-page-start-up), or trigger the transition from the page after `avalanche-install`.
42
74
 
43
75
  ## Failure signatures
44
76
 
@@ -54,15 +86,17 @@ Any React commit that fires between page mount and your `avalanche-install` call
54
86
 
55
87
  - `chippy.sh avalanche-status <clientId>` → expect `installed: true`, `commitCount > 0`, and `integrationMode` matching the host (`adapter:atl` for a patched Atlassian host, `devtools` otherwise; earlier releases report `atl`), never `none` after a captured commit.
56
88
  - In `avalanche-export`, compare `meta.commits` vs `meta.memoizedUpdatersSupport.commitsWithData` — they should match. If `commitsWithData > meta.commits`, early commits were missed.
57
- - For end-to-end behavior, build a CONTROL test (trigger after install — must capture) AND a GAP test (trigger before install — documents the limitation) per fixture.
89
+ - To tell the boot-time gap from a wiring fault, repeat the interaction after install. If that capture is also empty, the injection is broken; if only the pre-install interaction is missing, use [pre-page start-up](#pre-page-start-up).
58
90
 
59
91
  ## Mode B reference (for contrast)
60
92
 
61
93
  In Mode B, the extension does the following automatically and silently for every navigation:
62
94
  1. `webNavigation.onCommitted` fires
63
95
  2. Background fetches `armedAvalanche` config from helper
64
- 3. `chrome.scripting.executeScript({ injectImmediately: true, world: 'MAIN' })` runs the early scripts (`devtools-hook-shim.js`, `avalanche.iife.js`, `early-bootstrap.js`) before any page script evaluates
96
+ 3. `chrome.scripting.executeScript({ injectImmediately: true, world: 'MAIN' })` runs the early scripts (`devtools-hook-shim.js`, `avalanche.iife.js`, `early-bootstrap.js`). Because steps 2–3 wait on network lookups, a fast page can evaluate React first.
65
97
  4. iife.ts reads `__CHIPPY_STARTUP_CONFIG.avalanche.autoStart` and self-installs
66
98
  5. `chrome.tabs.onUpdated` (`status === 'complete'`) injects late bundle scripts (bridge + probes)
67
99
 
68
- The supported Mode A loaders do not provide the extension's pre-page `autoStart` configuration. They install only after client connection; that post-connection detector start is the structural reason for the boot-time gap.
100
+ For a helper reload of an armed page (`page-reload` after `avalanche-arm`, as in `reload-with-avalanche`), the page bridge first stores the arm in `sessionStorage` and the extension registers the early scripts plus `startup-handoff.js` as a MAIN-world `document_start` content script for that one navigation. The next document installs Avalanche before any page script runs, so boot commits are captured. The reload ack's `earlyStartup` is `registered` when this path was prepared.
101
+
102
+ The packaged and ad-hoc Mode A loaders do not provide the extension's pre-page `autoStart` configuration. They install only after client connection; that post-connection detector start is the structural reason for their boot-time gap. Pre-page start-up writes the configuration itself and has no such gap.
@@ -6,7 +6,7 @@ Use this when there is no Chippy browser extension on the page (`playwright-cli`
6
6
 
7
7
  Chippy assets are served by the local Chippy helper:
8
8
 
9
- - `http://127.0.0.1:7823/__chippy/inject.mjs` — **preferred ESM bootstrap**. Import side-effect installs `window.__CHIPPY`, registers the page with the helper, auto-loads `window.__AVALANCHE__` from the ESM-safe `avalanche.es.js` asset, activates probes immediately, and installs a small in-page control panel. The module default export is `window.__CHIPPY`; named export `ready` resolves to `{ chippy, avalanche, connState, integration, activated, probesActive }` immediately after bootstrap.
9
+ - `http://127.0.0.1:7823/__chippy/inject.mjs` — **preferred ESM bootstrap**. Import side-effect installs `window.__CHIPPY`, registers the page with the helper, auto-loads `window.__AVALANCHE__` from the ESM-safe `avalanche.es.js` asset, and activates probes immediately. It renders no in-page control panel. The module default export is `window.__CHIPPY`; named export `ready` resolves to `{ chippy, avalanche, connState, integration, activated, probesActive }` immediately after bootstrap.
10
10
  - `http://127.0.0.1:7823/__chippy/inject.js` — **classic-script bootstrap**. Installs `window.__CHIPPY` only.
11
11
  - `http://127.0.0.1:7823/__chippy/avalanche.iife.js` — **Chippy runtime for bridge / Avalanche-backed commands**. Installs `window.__AVALANCHE__` when the classic bootstrap is used.
12
12
 
@@ -18,9 +18,9 @@ The same ESM bootstrap is also valid for a real user-controlled browser tab when
18
18
  import('http://127.0.0.1:7823/__chippy/inject.mjs')
19
19
  ```
20
20
 
21
- In that mode the import is intended to be extension-equivalent for the current page: it activates probes immediately and installs the in-page Chippy controls.
21
+ In that mode the import activates probes for the current page without the extension's in-page controls.
22
22
 
23
- The equivalence lasts only for the current document. A full reload or navigation removes the imported runtime because no extension or Storybook host remains to inject it. The in-page import controls display this warning. The user—or Playwright—must run the import command again after every reload/navigation.
23
+ The import lasts only for the current document. A full reload or navigation removes the imported runtime because no extension, proxy, or Storybook host remains to inject it. The user—or Playwright—must run the import command again after every reload/navigation. To keep Chippy across reloads without the extension, use the packaged init-script loader in [mode-a-injection](mode-a-injection.md) or the helper's local proxy.
24
24
 
25
25
  ## Steps
26
26
 
@@ -36,7 +36,7 @@ The equivalence lasts only for the current document. A full reload or navigation
36
36
  3. Prefer the ESM bootstrap when module loading is available:
37
37
 
38
38
  ```bash
39
- yarn exec playwright-cli -s=<session> run-code "async page => {
39
+ playwright-cli -s=<session> run-code "async page => {
40
40
  await page.addScriptTag({
41
41
  content: `
42
42
  const script = document.createElement('script');
@@ -57,7 +57,7 @@ The equivalence lasts only for the current document. A full reload or navigation
57
57
  Fallback: classic script injection when ESM import is not available:
58
58
 
59
59
  ```bash
60
- yarn exec playwright-cli -s=<session> run-code "async page => {
60
+ playwright-cli -s=<session> run-code "async page => {
61
61
  await page.addScriptTag({ url: 'http://127.0.0.1:7823/__chippy/inject.js' });
62
62
  await page.waitForTimeout(500);
63
63
  await page.addScriptTag({ url: 'http://127.0.0.1:7823/__chippy/avalanche.iife.js' });
@@ -123,7 +123,7 @@ overridden with `CHIPPY_HELPER_BASE`.
123
123
 
124
124
  The packaged `launchSession(...)` path installs the Chippy bridge and Avalanche runtime via `addInitScript`, so both runtimes exist before page scripts on every reload. The detector still starts uninstalled in each new page realm. `reloadWithAvalanche` therefore reloads, waits for the new client, calls the Avalanche `install` action explicitly, then settles and reads evidence. It does not use helper origin-arming.
125
125
 
126
- Commits that occur before that post-reload install are not captured. Use this helper for post-install triggers and settled-state checks, not to claim complete initial-load coverage.
126
+ Commits that occur before that post-reload install are not captured. Use this helper for post-install triggers and settled-state checks, not to claim complete initial-load coverage. To record the initial load, use [pre-page start-up](mode-a-injection.md#pre-page-start-up).
127
127
 
128
128
  Flow:
129
129
  1. `page.reload()` via Playwright
@@ -21,7 +21,7 @@ id=show-page-status | q=How do I show a short status message inside the inspecte
21
21
  id=hide-page-status | q=How do I remove Chippy's page-visible status message? | tags=hide status clear message page | a=Run `chippy.sh hide-status <clientId>`. | done=The page-visible Chippy status is absent. | guard=This does not clear probes or capture state. | refs=references/command-surface.md#runtime-and-connection
22
22
  id=identify-integration-owner | q=How do I tell whether Chippy came from the extension, Storybook, or a console import? | tags=integration identity owner extension storybook import reload behavior who source | a=Read `integration.kind`, `who`, `source`, `reinjectsOnReload`, and `reloadAction` from `chippy.sh pages`. | done=The injection owner and reload contract are explicit. | guard=Do not infer ownership from globals or the page URL. | refs=SKILL.md#connection-and-bootstrap
23
23
  id=inject-console-module | q=How do I load Chippy into a user-controlled page without the extension? | tags=inject import esm console module user controlled page | a=Run `import('http://127.0.0.1:7823/__chippy/inject.mjs')` in the browser console, then resolve the page through `chippy.sh pages`. | done=The page reports `integration.kind: import` and the bridge is ready. | guard=The import is document-scoped and must be repeated after full navigation or reload. | refs=SKILL.md#connection-and-bootstrap
24
- id=print-classic-inject-url | q=How do I obtain the classic Chippy injection URL? | tags=inject url classic inject.js fallback | a=Run `chippy.sh inject-url`; stdout contains the URL and stderr explains the reload boundary. | done=A valid helper-served `inject.js` URL is returned. | guard=Prefer the ESM import for new console workflows. | refs=references/command-surface.md#runtime-and-connection
24
+ id=print-classic-inject-url | q=How do I obtain the classic Chippy injection URL? | tags=inject url classic inject.js fallback | a=Run `chippy.sh inject-url`; stdout contains the URL and stderr explains the reload boundary and the second asset. | done=A valid helper-served `inject.js` URL is returned. | guard=`inject.js` alone has no bridge; load `avalanche.iife.js` after it. Prefer the ESM import for new console workflows. | refs=references/command-surface.md#runtime-and-connection
25
25
  id=toggle-jira-enrichment | q=How do I show or hide Jira ownership and boundary enrichment in probe cards? | tags=jira context ui ownership error boundary enrichment on off cards | a=Run `chippy.sh context-ui <clientId> on` or `chippy.sh context-ui <clientId> off`. | done=The connected page renders or hides the enrichment consistently. | guard=This changes Chippy UI only and does not change Jira product behavior. | refs=references/command-surface.md#runtime-and-connection,references/jira-taxonomy.md
26
26
 
27
27
  # Probe, source, and component inspection
@@ -380,7 +380,7 @@ The effect attaches a subscription and dispatches state. It depends on `handler`
380
380
  - If the subscription is the only side effect, move attach/detach to a `useLayoutEffect` keyed on the stable subscription target rather than on a callback identity.
381
381
 
382
382
  **Pointer for the agent:**
383
- - File: `src/stability/StabilityChain.jsx`, `Leaf` component, `useEffect(..., [handler])`.
383
+ - Location: the `Leaf` component's `useEffect(..., [handler])`.
384
384
  - Question to answer before editing: *what does this effect semantically depend on — the function identity, or the data the function reads?*
385
385
  - After the fix, re-run stability-render. Expected: `commitCount` drops to 1; `attach/detach` no longer grows per nudge.
386
386
 
@@ -389,7 +389,7 @@ The effect attaches a subscription and dispatches state. It depends on `handler`
389
389
  After Step 1 the effect is fine, but the callback still flips identity every render. That is harmless on its own — but the moment a memoised child uses it as a prop, the cascade comes back. Fix this *before* memoising any children, not *after*.
390
390
 
391
391
  **Pointer for the agent:**
392
- - File: `src/stability/StabilityChain.jsx`, `Leaf`, `useCallback(handler, [opts])`.
392
+ - Location: `Leaf`'s `useCallback(handler, [opts])`.
393
393
  - Same dep narrowing logic as Step 1: depend on `opts.mode`, not on `opts`. The callback's body only uses `opts.mode`, so the wider dep is overcautious and breaks memoisation.
394
394
  - After the fix, re-run stability-render. Expected: callback identity stable across nudges; `Leaf`'s render count for the nudge stays at 1.
395
395
 
@@ -398,13 +398,13 @@ After Step 1 the effect is fine, but the callback still flips identity every ren
398
398
  `Parent` builds `opts = { mode }` inline. Step 1 + Step 2 made downstream consumers immune to that, but the next consumer added to this subtree will get the same trap. Wrap the object once.
399
399
 
400
400
  **Pointer for the agent:**
401
- - File: `src/stability/StabilityChain.jsx`, `StabilityChain`, `const opts = { mode };`.
401
+ - Location: the parent's `const opts = { mode };`.
402
402
  - Wrap with `useMemo(() => ({ mode }), [mode])` — or, if `mode` is the only field, pass `mode` directly and drop the object.
403
403
  - After the fix, re-run stability-render. Expected: same numbers as after Step 2. This step is *prevention*, not *repair* — measurable improvement is "no regression when a new memoised consumer is added".
404
404
 
405
405
  ### What "deep explanation" means here
406
406
 
407
- When Chippy reports findings on this fixture, every pointer must answer three questions:
407
+ When Chippy reports findings on this chain, every pointer must answer three questions:
408
408
 
409
409
  1. **What flips?** Name the identity that changes. (`opts` object reference; `handler` function reference; `useEffect` cleanup/attach pair.)
410
410
  2. **Why does that flip propagate?** Name the dep array or prop boundary it crosses. (Dep array of `useCallback`; dep array of `useEffect`; prop `opts` from Parent to Leaf.)
@@ -132,7 +132,8 @@ Examples:
132
132
  ./scripts/chippy.sh pages
133
133
  # Select the page whose URL, title, integration identity, and recency match the target.
134
134
  CLIENT_ID=<confirmed-clientId>
135
- ./scripts/chippy.sh reload-with-avalanche "$CLIENT_ID" http://localhost:9000 0 15000 5000
135
+ ORIGIN=<that page's origin>
136
+ ./scripts/chippy.sh reload-with-avalanche "$CLIENT_ID" "$ORIGIN" 0 15000 5000
136
137
  ./scripts/chippy.sh avalanche-status "$CLIENT_ID"
137
138
  ./scripts/chippy.sh avalanche-hot-sources "$CLIENT_ID" 10
138
139
  ./scripts/chippy.sh avalanche-commit-list "$CLIENT_ID" 10 collateral
@@ -3,7 +3,7 @@
3
3
  ## Helper is down
4
4
  - `/__chippy/health` fails
5
5
  - no pages in `/__chippy/api/pages`
6
- - the Storybook Chippy and Avalanche panels conditionally offer `npx @atlassian/chippy install` while their connection state is `failed` or `reconnecting`
6
+ - the Storybook Chippy and Avalanche panels conditionally offer `npx skills add atlassian-labs/chippy --skill chippy -g` while their connection state is `failed` or `reconnecting`
7
7
  - Chippy must run outside the agent sandbox; the agent owns this, not the user
8
8
  - sandbox failure mode: the helper can look alive inside the agent environment, but the browser cannot reach its isolated Node server/port
9
9
  - fix: agent runs `./scripts/chippy.sh start` using outside-sandbox / escalated execution, then verifies with `./scripts/chippy.sh health` and `./scripts/chippy.sh pages` (also outside-sandbox)
@@ -47,6 +47,11 @@
47
47
  - the served URL and generated line remain as a bounded fallback; do not present them as a mapped repository position
48
48
  - check that the page can fetch its own module URL and that the dev server emits an inline or external Source Map v3 payload
49
49
 
50
+ ## React <=18 source line is offset from the file
51
+ - `_debugSource` records the line the JSX transform saw; a transform that ran earlier and added lines shifts it
52
+ - Chippy corrects the line when it can fetch the module the page loaded for that file and the module's source map maps the recorded literal back into the same file
53
+ - if the line still looks offset, check that the page loaded the module from its own origin and that the dev server emits a source map
54
+
50
55
  ## Multiple pages on same URL
51
56
  - use `clientId`, title, `latestSnapshotAt`, and `lastMessageAt` to identify the right page
52
57
  - usually prefer the most recently updated page when one candidate is clearly newer than the rest