rhachet-roles-bhrowser 0.0.0

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 (118) hide show
  1. package/LICENSE +21 -0
  2. package/dist/contract/sdk/index.d.ts +3 -0
  3. package/dist/contract/sdk/index.js +10 -0
  4. package/dist/contract/sdk/index.js.map +1 -0
  5. package/dist/domain.roles/getRoleRegistry.d.ts +6 -0
  6. package/dist/domain.roles/getRoleRegistry.js +17 -0
  7. package/dist/domain.roles/getRoleRegistry.js.map +1 -0
  8. package/dist/domain.roles/inspector/boot.yml +4 -0
  9. package/dist/domain.roles/inspector/getInspectorRole.d.ts +6 -0
  10. package/dist/domain.roles/inspector/getInspectorRole.js +34 -0
  11. package/dist/domain.roles/inspector/getInspectorRole.js.map +1 -0
  12. package/dist/domain.roles/inspector/readme.md +7 -0
  13. package/dist/domain.roles/playwright/boot.yml +37 -0
  14. package/dist/domain.roles/playwright/briefs/auth/howto.cross-session-auth.md +134 -0
  15. package/dist/domain.roles/playwright/briefs/debug/howto.capture-state-on-error.md +253 -0
  16. package/dist/domain.roles/playwright/briefs/debug/howto.debug-movie-frames.md +182 -0
  17. package/dist/domain.roles/playwright/briefs/diagnosis/howto.browser-diagnosis.md +152 -0
  18. package/dist/domain.roles/playwright/briefs/diagnosis/rule.require.grep-html-before-selector-guess.md +82 -0
  19. package/dist/domain.roles/playwright/briefs/diagnosis/rule.require.snapshot-before-assume.md +73 -0
  20. package/dist/domain.roles/playwright/briefs/diagnosis/rule.require.snapshot-before-debug.md +81 -0
  21. package/dist/domain.roles/playwright/briefs/diagnosis/rule.require.snapshot-latest-tab.md +84 -0
  22. package/dist/domain.roles/playwright/briefs/reliability/ref.cdp-reconnection-patterns.md +168 -0
  23. package/dist/domain.roles/playwright/briefs/reliability/rule.forbid.coordinate-clicks.md +53 -0
  24. package/dist/domain.roles/playwright/briefs/reliability/rule.require.bounded-timeouts-and-bisection.md +184 -0
  25. package/dist/domain.roles/playwright/briefs/rule.im_a.bhrowser_lizard.md +68 -0
  26. package/dist/domain.roles/playwright/briefs/skills/howto.browser-action-playbooks.md +265 -0
  27. package/dist/domain.roles/playwright/briefs/skills/howto.browser-byhand-work.md +151 -0
  28. package/dist/domain.roles/playwright/briefs/skills/rule.require.playbooks-over-adhoc.md +90 -0
  29. package/dist/domain.roles/playwright/briefs/spa/rule.require.spa-navigation-fail-fast.md +109 -0
  30. package/dist/domain.roles/playwright/briefs/spa/rule.require.wait-for-content-not-shell.md +128 -0
  31. package/dist/domain.roles/playwright/briefs/spa/rule.require.wait-for-react-render.md +127 -0
  32. package/dist/domain.roles/playwright/briefs/spa/rule.require.wait-for-target-element.md +123 -0
  33. package/dist/domain.roles/playwright/briefs/stealth/ref.antibot-escalation.md +120 -0
  34. package/dist/domain.roles/playwright/briefs/test/howto.test-via-browser.md +285 -0
  35. package/dist/domain.roles/playwright/briefs/test/rule.forbid.afterall-state-mutation.md +106 -0
  36. package/dist/domain.roles/playwright/briefs/test/rule.forbid.test-goto.md +77 -0
  37. package/dist/domain.roles/playwright/briefs/test/rule.require.fast-tests.md +130 -0
  38. package/dist/domain.roles/playwright/briefs/verification/rule.forbid.unverified-actions.md +95 -0
  39. package/dist/domain.roles/playwright/briefs/verification/rule.require.action-verification.md +138 -0
  40. package/dist/domain.roles/playwright/getPlaywrightRole.d.ts +6 -0
  41. package/dist/domain.roles/playwright/getPlaywrightRole.js +34 -0
  42. package/dist/domain.roles/playwright/getPlaywrightRole.js.map +1 -0
  43. package/dist/domain.roles/playwright/readme.md +7 -0
  44. package/dist/domain.roles/playwright/skills/.test/infra/browser.ts +272 -0
  45. package/dist/domain.roles/playwright/skills/browser.action.d.ts +23 -0
  46. package/dist/domain.roles/playwright/skills/browser.action.js +124 -0
  47. package/dist/domain.roles/playwright/skills/browser.action.js.map +1 -0
  48. package/dist/domain.roles/playwright/skills/browser.action.sh +120 -0
  49. package/dist/domain.roles/playwright/skills/browser.action.ts +148 -0
  50. package/dist/domain.roles/playwright/skills/browser.describe.d.ts +15 -0
  51. package/dist/domain.roles/playwright/skills/browser.describe.js +191 -0
  52. package/dist/domain.roles/playwright/skills/browser.describe.js.map +1 -0
  53. package/dist/domain.roles/playwright/skills/browser.describe.sh +45 -0
  54. package/dist/domain.roles/playwright/skills/browser.describe.ts +255 -0
  55. package/dist/domain.roles/playwright/skills/browser.lib.sh +725 -0
  56. package/dist/domain.roles/playwright/skills/browser.session.d.ts +1 -0
  57. package/dist/domain.roles/playwright/skills/browser.session.js +253 -0
  58. package/dist/domain.roles/playwright/skills/browser.session.js.map +1 -0
  59. package/dist/domain.roles/playwright/skills/browser.session.sh +194 -0
  60. package/dist/domain.roles/playwright/skills/browser.session.ts +310 -0
  61. package/dist/domain.roles/playwright/skills/browser.snapshot.console.d.ts +33 -0
  62. package/dist/domain.roles/playwright/skills/browser.snapshot.console.js +163 -0
  63. package/dist/domain.roles/playwright/skills/browser.snapshot.console.js.map +1 -0
  64. package/dist/domain.roles/playwright/skills/browser.snapshot.console.sh +57 -0
  65. package/dist/domain.roles/playwright/skills/browser.snapshot.console.ts +212 -0
  66. package/dist/domain.roles/playwright/skills/browser.snapshot.html.d.ts +17 -0
  67. package/dist/domain.roles/playwright/skills/browser.snapshot.html.js +133 -0
  68. package/dist/domain.roles/playwright/skills/browser.snapshot.html.js.map +1 -0
  69. package/dist/domain.roles/playwright/skills/browser.snapshot.html.sh +53 -0
  70. package/dist/domain.roles/playwright/skills/browser.snapshot.html.ts +152 -0
  71. package/dist/domain.roles/playwright/skills/browser.snapshot.meta.d.ts +28 -0
  72. package/dist/domain.roles/playwright/skills/browser.snapshot.meta.js +109 -0
  73. package/dist/domain.roles/playwright/skills/browser.snapshot.meta.js.map +1 -0
  74. package/dist/domain.roles/playwright/skills/browser.snapshot.meta.sh +53 -0
  75. package/dist/domain.roles/playwright/skills/browser.snapshot.meta.ts +136 -0
  76. package/dist/domain.roles/playwright/skills/browser.snapshot.network.d.ts +28 -0
  77. package/dist/domain.roles/playwright/skills/browser.snapshot.network.js +160 -0
  78. package/dist/domain.roles/playwright/skills/browser.snapshot.network.js.map +1 -0
  79. package/dist/domain.roles/playwright/skills/browser.snapshot.network.sh +57 -0
  80. package/dist/domain.roles/playwright/skills/browser.snapshot.network.ts +188 -0
  81. package/dist/domain.roles/playwright/skills/browser.snapshot.screen.d.ts +17 -0
  82. package/dist/domain.roles/playwright/skills/browser.snapshot.screen.js +138 -0
  83. package/dist/domain.roles/playwright/skills/browser.snapshot.screen.js.map +1 -0
  84. package/dist/domain.roles/playwright/skills/browser.snapshot.screen.sh +65 -0
  85. package/dist/domain.roles/playwright/skills/browser.snapshot.screen.ts +187 -0
  86. package/dist/domain.roles/playwright/skills/browser.snapshot.sh +323 -0
  87. package/dist/domain.roles/playwright/skills/browser.snapshot.storage.d.ts +21 -0
  88. package/dist/domain.roles/playwright/skills/browser.snapshot.storage.js +154 -0
  89. package/dist/domain.roles/playwright/skills/browser.snapshot.storage.js.map +1 -0
  90. package/dist/domain.roles/playwright/skills/browser.snapshot.storage.sh +53 -0
  91. package/dist/domain.roles/playwright/skills/browser.snapshot.storage.ts +172 -0
  92. package/dist/domain.roles/playwright/skills/browser.start.sh +266 -0
  93. package/dist/domain.roles/playwright/skills/browser.stop.sh +89 -0
  94. package/dist/domain.roles/playwright/skills/lib/shared.d.ts +219 -0
  95. package/dist/domain.roles/playwright/skills/lib/shared.js +416 -0
  96. package/dist/domain.roles/playwright/skills/lib/shared.js.map +1 -0
  97. package/dist/domain.roles/playwright/skills/lib/shared.ts +480 -0
  98. package/dist/domain.roles/readme.md +8 -0
  99. package/dist/domain.roles/scraper/boot.yml +8 -0
  100. package/dist/domain.roles/scraper/briefs/cache/howto.cache-browser-scrapes.md +257 -0
  101. package/dist/domain.roles/scraper/briefs/cache/howto.setup-cache-infrastructure.md +62 -0
  102. package/dist/domain.roles/scraper/briefs/cache/ref.remote-state-query-cache.md +43 -0
  103. package/dist/domain.roles/scraper/briefs/cache/rule.require.remote-state-cache.md +119 -0
  104. package/dist/domain.roles/scraper/getScraperRole.d.ts +6 -0
  105. package/dist/domain.roles/scraper/getScraperRole.js +34 -0
  106. package/dist/domain.roles/scraper/getScraperRole.js.map +1 -0
  107. package/dist/domain.roles/scraper/readme.md +7 -0
  108. package/dist/domain.roles/scraper/skills/.test/infra/cache.ts +184 -0
  109. package/dist/domain.roles/scraper/skills/cache.expire.sh +159 -0
  110. package/dist/domain.roles/scraper/skills/cache.extend.sh +416 -0
  111. package/dist/index.d.ts +1 -0
  112. package/dist/index.js +18 -0
  113. package/dist/index.js.map +1 -0
  114. package/license.md +21 -0
  115. package/package.json +116 -0
  116. package/readme.[seed].md +2 -0
  117. package/readme.md +65 -0
  118. package/rhachet.repo.yml +17 -0
@@ -0,0 +1,182 @@
1
+ # howto.debug-movie-frames
2
+
3
+ ## .what
4
+
5
+ capture sequential snapshots at each step to create a "movie" of automation flow.
6
+
7
+ ## .why
8
+
9
+ single snapshots show one moment. movie frames show:
10
+ - state before each action
11
+ - state after each action
12
+ - where exactly flow diverged
13
+ - what changed between steps
14
+
15
+ essential for debug of multi-step flows.
16
+
17
+ ## .pattern
18
+
19
+ ```typescript
20
+ /**
21
+ * .what = get movie identifier with timestamp fallback if null
22
+ * .why = isolates nullable coalesce + date logic from orchestrator
23
+ */
24
+ const asMovieId = (input: { movieId: string | null }): string =>
25
+ input.movieId ?? String(Date.now());
26
+
27
+ /**
28
+ * .what = capture movie frames at each step of automation flow
29
+ * .why = enables frame-by-frame debug of multi-step workflows
30
+ */
31
+ export const action = async (
32
+ // movieId nullable: caller may not have identifier; defaults to timestamp
33
+ input: { email: string; password: string; movieId: string | null },
34
+ context: { page: Page; session: string },
35
+ ) => {
36
+ // determine snapshot directory via named transformer
37
+ const movieId = asMovieId({ movieId: input.movieId });
38
+ const snapshotDir = `.cache/browser.${context.session}/movie.${movieId}`;
39
+
40
+ const frame = async (input: { name: string }) => {
41
+ // capture state at this moment
42
+ await context.page.screenshot({ path: `${snapshotDir}/${input.name}.png` });
43
+ const html = await context.page.content();
44
+ await fs.writeFile(`${snapshotDir}/${input.name}.html`, html);
45
+ const url = context.page.url();
46
+ await fs.writeFile(`${snapshotDir}/${input.name}.url`, url);
47
+ };
48
+
49
+ // frame 0: initial state
50
+ await frame({ name: '00-initial' });
51
+
52
+ // action 1
53
+ await context.page.fill('#email', input.email);
54
+ await frame({ name: '01-email-filled' });
55
+
56
+ // action 2
57
+ await context.page.fill('#password', input.password);
58
+ await frame({ name: '02-password-filled' });
59
+
60
+ // action 3
61
+ await context.page.click('#submit');
62
+ await frame({ name: '03-submit-clicked' });
63
+
64
+ // wait for result
65
+ await context.page.waitForURL('**/dashboard');
66
+ await frame({ name: '04-dashboard-loaded' });
67
+ };
68
+ ```
69
+
70
+ ## .output structure
71
+
72
+ ```
73
+ .cache/browser.$SESSION/movie.$TIMESTAMP/
74
+ ├── 00-initial.png
75
+ ├── 00-initial.html
76
+ ├── 00-initial.url
77
+ ├── 01-email-filled.png
78
+ ├── 01-email-filled.html
79
+ ├── 01-email-filled.url
80
+ ├── 02-password-filled.png
81
+ ├── 02-password-filled.html
82
+ ├── 02-password-filled.url
83
+ ├── 03-submit-clicked.png
84
+ ├── 03-submit-clicked.html
85
+ ├── 03-submit-clicked.url
86
+ ├── 04-dashboard-loaded.png
87
+ ├── 04-dashboard-loaded.html
88
+ └── 04-dashboard-loaded.url
89
+ ```
90
+
91
+ ## .debug workflow
92
+
93
+ ```
94
+ flow failed at step 3
95
+
96
+ ├─> open 02-password-filled.png
97
+ │ └─ "password field filled correctly"
98
+
99
+ ├─> open 03-submit-clicked.png
100
+ │ └─ "error: submit button was disabled!"
101
+
102
+ └─> root cause: validation error not visible in 02
103
+ └─ check 02-password-filled.html for error message
104
+ ```
105
+
106
+ ## .frame helper
107
+
108
+ ```typescript
109
+ /**
110
+ * .what = format frame number as zero-padded prefix
111
+ * .why = ensures consistent two-digit frame numbers for sort order
112
+ */
113
+ const asFramePrefix = (input: { numFrame: number }): string =>
114
+ String(input.numFrame).padStart(2, '0');
115
+
116
+ /**
117
+ * .what = persist single frame snapshot with explicit sequence number
118
+ * .why = pure function for frame capture; caller manages sequence
119
+ */
120
+ const setFrameSnapshot = async (
121
+ input: { label: string; numFrame: number },
122
+ context: { page: Page; dir: string },
123
+ ): Promise<string> => {
124
+ // format frame name with sequence prefix
125
+ const prefix = asFramePrefix({ numFrame: input.numFrame });
126
+ const name = `${prefix}-${input.label}`;
127
+
128
+ // capture screenshot
129
+ await context.page.screenshot({ path: `${context.dir}/${name}.png`, fullPage: true });
130
+
131
+ // capture html content
132
+ await fs.writeFile(`${context.dir}/${name}.html`, await context.page.content());
133
+
134
+ // capture metadata
135
+ await fs.writeFile(`${context.dir}/${name}.meta.json`, JSON.stringify({
136
+ url: context.page.url(),
137
+ title: await context.page.title(),
138
+ time: new Date().toJSON(),
139
+ }, null, 2));
140
+
141
+ return name;
142
+ };
143
+
144
+ // usage in playbook
145
+ /**
146
+ * .what = example playbook with frame capture at each step
147
+ * .why = demonstrates movie frame debug pattern in practice
148
+ */
149
+ export const action = async (
150
+ // movieId nullable: caller may not have identifier; defaults to timestamp
151
+ input: { movieId: string | null },
152
+ context: { page: Page },
153
+ ) => {
154
+ // frame capture context via named transformer (immutable per frame)
155
+ const movieId = asMovieId({ movieId: input.movieId });
156
+ const frameContext = { page: context.page, dir: `.cache/movie.${movieId}` };
157
+
158
+ // capture frames with explicit sequence numbers
159
+ await setFrameSnapshot({ label: 'initial', numFrame: 0 }, frameContext);
160
+ await context.page.click('#start');
161
+ await setFrameSnapshot({ label: 'started', numFrame: 1 }, frameContext);
162
+ // ...
163
+ };
164
+ ```
165
+
166
+ ## .comparison tools
167
+
168
+ ```bash
169
+ # view frames in sequence
170
+ ls -la .cache/browser.$SESSION/movie.*/
171
+
172
+ # compare two frames
173
+ diff 02-before.html 03-after.html
174
+
175
+ # image diff (requires imagemagick)
176
+ compare 02-before.png 03-after.png diff.png
177
+ ```
178
+
179
+ ## .see also
180
+
181
+ - `howto.capture-state-on-error.md` — error state capture
182
+ - `howto.browser-diagnosis.md` — diagnosis workflow
@@ -0,0 +1,152 @@
1
+ # howto.browser-diagnosis
2
+
3
+ ## .what
4
+
5
+ systematic approach to diagnose browser automation issues.
6
+
7
+ ## .workflow
8
+
9
+ ```
10
+ 1. snapshot
11
+ └─> freeze state before it changes
12
+
13
+ 2. observe
14
+ ├─> screenshot: what's visible?
15
+ ├─> html: what's in dom?
16
+ ├─> console: any errors?
17
+ └─> network: requests failed?
18
+
19
+ 3. hypothesize
20
+ └─> form theory based on evidence
21
+
22
+ 4. test
23
+ └─> minimal action to test theory
24
+
25
+ 5. iterate
26
+ └─> new snapshot, new observation
27
+ ```
28
+
29
+ ## .step 1: snapshot
30
+
31
+ ```bash
32
+ # capture all artifacts
33
+ browser.snapshot --session $SESSION --focused
34
+
35
+ # output location
36
+ .cache/browser.$SESSION/snapshot.$TIMESTAMP/
37
+ ├── snapshot.meta.json
38
+ ├── snapshot.png
39
+ ├── snapshot.html
40
+ ├── snapshot.storage.json
41
+ ├── snapshot.console.json
42
+ └── snapshot.network.json
43
+ ```
44
+
45
+ ## .step 2: observe
46
+
47
+ ### screenshot analysis
48
+
49
+ ```bash
50
+ # view screenshot
51
+ open .cache/browser.$SESSION/snapshot.*/snapshot.png
52
+
53
+ # check:
54
+ # - is the expected page visible?
55
+ # - any overlays that block content?
56
+ # - load indicators present?
57
+ # - error messages displayed?
58
+ ```
59
+
60
+ ### html analysis
61
+
62
+ ```bash
63
+ # search for element
64
+ grep -i "submit" snapshot.html
65
+
66
+ # check element visibility
67
+ grep -B5 -A5 "submit" snapshot.html
68
+ # look for: style="display:none", class="hidden", etc.
69
+ ```
70
+
71
+ ### console analysis
72
+
73
+ ```bash
74
+ # check for errors
75
+ cat snapshot.console.json | jq '.[] | select(.type == "error")'
76
+
77
+ # common issues:
78
+ # - uncaught TypeError (JS error)
79
+ # - CORS errors (blocked requests)
80
+ # - 404 (absent resources)
81
+ ```
82
+
83
+ ### network analysis
84
+
85
+ ```bash
86
+ # check for failed requests
87
+ cat snapshot.network.json | jq '.[] | select(.status >= 400)'
88
+
89
+ # check for awaited requests
90
+ cat snapshot.network.json | jq '.[] | select(.status == null)'
91
+ ```
92
+
93
+ ## .step 3: common diagnoses
94
+
95
+ | symptom | likely cause | verification |
96
+ |---------|--------------|--------------|
97
+ | element not found | not in dom | grep html |
98
+ | element not visible | CSS hidden | grep for display/visibility |
99
+ | timeout | still loads | check network awaited |
100
+ | wrong page | navigation issue | check screenshot url |
101
+ | stale element | dom changed | re-snapshot |
102
+ | click blocked | overlay present | screenshot shows modal |
103
+
104
+ ## .step 4: targeted action
105
+
106
+ ```bash
107
+ # test hypothesis with minimal playbook
108
+ browser.action --session $SESSION --play test-hypothesis.play.ts
109
+ ```
110
+
111
+ ```typescript
112
+ // test-hypothesis.play.ts
113
+ /**
114
+ * .what = test hypothesis about element visibility
115
+ * .why = validates theory with minimal action before complex fix
116
+ */
117
+ export const action = async (
118
+ input: Record<string, never>,
119
+ context: { page: Page; log: LogMethods },
120
+ ) => {
121
+ // check element state
122
+ const element = await context.page.$('#my-element');
123
+ context.log.info('element exists:', { exists: !!element });
124
+ context.log.info('element visible:', { visible: await element?.isVisible() });
125
+ };
126
+ ```
127
+
128
+ ## .step 5: iterate
129
+
130
+ ```bash
131
+ # new snapshot after action
132
+ browser.snapshot --session $SESSION --focused
133
+
134
+ # compare to previous
135
+ diff snapshot.before.html snapshot.after.html
136
+ ```
137
+
138
+ ## .diagnosis checklist
139
+
140
+ - [ ] snapshot captured before debug?
141
+ - [ ] screenshot reviewed?
142
+ - [ ] html searched for target element?
143
+ - [ ] console checked for errors?
144
+ - [ ] network checked for failures?
145
+ - [ ] hypothesis formed from evidence?
146
+ - [ ] minimal test action executed?
147
+
148
+ ## .see also
149
+
150
+ - `rule.require.snapshot-before-debug.md` — always snapshot first
151
+ - `rule.require.grep-html-before-selector-guess.md` — search before select
152
+ - `howto.debug-movie-frames.md` — capture action sequences
@@ -0,0 +1,82 @@
1
+ # rule.require.grep-html-before-selector-guess
2
+
3
+ ## .what
4
+
5
+ search the html snapshot before you guess selectors.
6
+
7
+ ## .why
8
+
9
+ guessed selectors fail because:
10
+ - class names differ from what you expect
11
+ - IDs are dynamically generated
12
+ - structure changed since you last saw it
13
+ - framework adds wrapper elements
14
+
15
+ the html snapshot is the source of truth.
16
+
17
+ ## .pattern
18
+
19
+ ```bash
20
+ # 1. capture snapshot
21
+ browser.snapshot --session $SESSION --focused
22
+
23
+ # 2. read the html
24
+ cat .cache/browser.$SESSION/snapshot.*/snapshot.html
25
+
26
+ # 3. search for your target
27
+ grep -i "submit" .cache/browser.$SESSION/snapshot.*/snapshot.html
28
+ grep -i "button" .cache/browser.$SESSION/snapshot.*/snapshot.html
29
+ grep -i "login" .cache/browser.$SESSION/snapshot.*/snapshot.html
30
+ ```
31
+
32
+ ## .selector discovery
33
+
34
+ | look for | grep pattern |
35
+ |----------|--------------|
36
+ | button text | `grep -i "sign in"` |
37
+ | input placeholder | `grep -i "placeholder"` |
38
+ | form action | `grep -i "action="` |
39
+ | data attributes | `grep "data-test"` |
40
+ | aria labels | `grep "aria-label"` |
41
+
42
+ ## .examples
43
+
44
+ ### good: search first
45
+
46
+ ```bash
47
+ # find the login button
48
+ grep -i "sign in\|login\|submit" snapshot.html
49
+ # output: <button data-test="login-btn" class="xyz123">Sign In</button>
50
+
51
+ # use the stable selector
52
+ page.click('[data-test="login-btn"]')
53
+ ```
54
+
55
+ ### bad: guess selector
56
+
57
+ ```bash
58
+ # 🚫 guess based on common patterns
59
+ page.click('#login') # not found
60
+ page.click('.login-button') # wrong class
61
+ page.click('button[type="submit"]') # multiple matches
62
+ ```
63
+
64
+ ## .selector preference order
65
+
66
+ | priority | selector type | why |
67
+ |----------|---------------|-----|
68
+ | 1 | `data-test="..."` | stable, for test |
69
+ | 2 | `aria-label="..."` | semantic, stable |
70
+ | 3 | `[role="..."]` | semantic |
71
+ | 4 | unique text | `text="Sign In"` |
72
+ | 5 | id | may be dynamic |
73
+ | 6 | class | often minified/dynamic |
74
+
75
+ ## .enforcement
76
+
77
+ guessed selector without search of html = blocker
78
+
79
+ ## .see also
80
+
81
+ - `rule.require.snapshot-before-assume.md` — snapshot first
82
+ - `browser.snapshot html` — capture html
@@ -0,0 +1,73 @@
1
+ # rule.require.snapshot-before-assume
2
+
3
+ ## .what
4
+
5
+ take a snapshot before you assume page state. never guess what the browser shows.
6
+
7
+ ## .why
8
+
9
+ assumptions about page state cause:
10
+ - wrong selectors (element not visible)
11
+ - wrong time (content not loaded)
12
+ - wrong context (different page than expected)
13
+
14
+ snapshots provide evidence for decisions.
15
+
16
+ ## .pattern
17
+
18
+ ```bash
19
+ # before any assumption about state
20
+ browser.snapshot --session $SESSION --focused
21
+
22
+ # now you can see:
23
+ # - screenshot: what's visible
24
+ # - html: what's in dom
25
+ # - console: any errors
26
+ # - network: queued requests
27
+ ```
28
+
29
+ ## .antipattern
30
+
31
+ ```bash
32
+ # 🚫 guess selector without view of page
33
+ browser.action --session $SESSION --play click-submit.play.ts
34
+ # fails: "element not found"
35
+ # you: "but it should be there!"
36
+ # reality: page shows login form, not submit button
37
+ ```
38
+
39
+ ## .examples
40
+
41
+ ### good: snapshot first
42
+
43
+ ```bash
44
+ # see what's actually on page
45
+ browser.snapshot --session $SESSION --focused
46
+ # output: screenshot shows load spinner
47
+
48
+ # now you know: wait for content
49
+ browser.action --session $SESSION --play wait-for-content.play.ts
50
+ ```
51
+
52
+ ### bad: assume then debug
53
+
54
+ ```bash
55
+ # assume page is ready
56
+ browser.action --session $SESSION --play submit-form.play.ts
57
+ # error: timeout awaited for #submit-button
58
+
59
+ # now you snapshot to debug
60
+ browser.snapshot --session $SESSION --focused
61
+ # output: page shows captcha challenge
62
+
63
+ # wasted time on wrong assumption
64
+ ```
65
+
66
+ ## .enforcement
67
+
68
+ assumed page state without prior snapshot = blocker
69
+
70
+ ## .see also
71
+
72
+ - `rule.require.snapshot-before-debug.md` — snapshot before debug
73
+ - `rule.require.grep-html-before-selector-guess.md` — search before select
@@ -0,0 +1,81 @@
1
+ # rule.require.snapshot-before-debug
2
+
3
+ ## .what
4
+
5
+ before you debug any browser issue, capture a full snapshot.
6
+
7
+ ## .why
8
+
9
+ debug without evidence is a guess:
10
+ - symptoms change between observations
11
+ - state mutates while you investigate
12
+ - memory of what you saw is unreliable
13
+
14
+ a snapshot freezes state for analysis.
15
+
16
+ ## .pattern
17
+
18
+ ```bash
19
+ # action failed
20
+ browser.action --session $SESSION --play failed-action.play.ts
21
+ # error: could not find element
22
+
23
+ # FIRST: capture state
24
+ browser.snapshot --session $SESSION --focused
25
+
26
+ # NOW: analyze frozen state
27
+ # - screenshot: what user sees
28
+ # - html: what dom contains
29
+ # - console: error messages
30
+ # - network: failed requests
31
+ ```
32
+
33
+ ## .what snapshots capture
34
+
35
+ | artifact | reveals |
36
+ |----------|---------|
37
+ | screenshot | visual state, overlays, modals |
38
+ | html | dom structure, hidden elements |
39
+ | console | js errors, warnings, logs |
40
+ | network | failed requests, awaited loads |
41
+ | storage | cookies, localStorage state |
42
+ | meta | url, title, tab info |
43
+
44
+ ## .diagnosis workflow
45
+
46
+ ```
47
+ error occurs
48
+
49
+ └─> snapshot immediately
50
+
51
+ ├─> check screenshot
52
+ │ └─ visible state matches expectation?
53
+
54
+ ├─> check html
55
+ │ └─ element exists in dom?
56
+
57
+ ├─> check console
58
+ │ └─ js errors that prevent render?
59
+
60
+ └─> check network
61
+ └─ request failed or awaited?
62
+ ```
63
+
64
+ ## .antipattern
65
+
66
+ ```bash
67
+ # 🚫 debug without snapshot
68
+ # "let me check if the element exists..."
69
+ browser.action --session $SESSION --play check-element.play.ts
70
+ # "hmm, now it says different error..."
71
+ # state has changed, you chase ghosts
72
+ ```
73
+
74
+ ## .enforcement
75
+
76
+ debug browser issue without prior snapshot = blocker
77
+
78
+ ## .see also
79
+
80
+ - `rule.require.snapshot-before-assume.md` — snapshot before assume
81
+ - `howto.browser-diagnosis.md` — full diagnosis workflow
@@ -0,0 +1,84 @@
1
+ # rule.require.snapshot-latest-tab
2
+
3
+ ## .what
4
+
5
+ when you diagnose issues, snapshot the latest (most recently opened) tab first.
6
+
7
+ ## .why
8
+
9
+ - navigation often opens new tabs
10
+ - popups and oauth flows spawn tabs
11
+ - the action that failed likely happened in the newest tab
12
+ - `--focused` gets the human-focused tab, but automation may be in another
13
+
14
+ ## .pattern
15
+
16
+ ```bash
17
+ # first: see all tabs
18
+ browser.describe --session $SESSION
19
+ # output shows: tab 0 (original), tab 1 (oauth popup), tab 2 (result)
20
+
21
+ # snapshot latest tab (highest index)
22
+ browser.snapshot --session $SESSION --tab 2 --url 'expected-url.com'
23
+
24
+ # if that's not the issue, work backwards
25
+ browser.snapshot --session $SESSION --tab 1 --url 'oauth-provider.com'
26
+ ```
27
+
28
+ ## .tab order
29
+
30
+ | index | typical content |
31
+ |-------|-----------------|
32
+ | 0 | original/start page |
33
+ | 1 | first popup/redirect |
34
+ | -1 | most recent (alias for highest) |
35
+
36
+ ## .common scenarios
37
+
38
+ ### oauth flow
39
+
40
+ ```
41
+ user clicks "login with google"
42
+
43
+ ├─ tab 0: original site (awaited)
44
+ └─ tab 1: google oauth (active)
45
+
46
+ └─ tab 2: callback page (final result)
47
+ ```
48
+
49
+ snapshot tab 2 first — that's where the result is.
50
+
51
+ ### file download
52
+
53
+ ```
54
+ user clicks "download"
55
+
56
+ ├─ tab 0: original page
57
+ └─ tab 1: download initiated (may close)
58
+ ```
59
+
60
+ ### popup blocked
61
+
62
+ ```
63
+ action opens popup
64
+
65
+ └─ tab 0: popup blocked notification
66
+ (no new tab created)
67
+ ```
68
+
69
+ ## .antipattern
70
+
71
+ ```bash
72
+ # 🚫 always snapshot tab 0
73
+ browser.snapshot --session $SESSION --tab 0
74
+ # shows original page, but error is in popup tab
75
+ ```
76
+
77
+ ## .enforcement
78
+
79
+ diagnose navigation issue without check of latest tab = blocker
80
+
81
+ ## .see also
82
+
83
+ - `browser.describe.sh` — list all tabs
84
+ - `rule.require.snapshot-before-debug.md` — snapshot first