@hiver/skills 1.0.1 → 1.0.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.
- package/collections/extension/agents/build-feature.md +41 -0
- package/collections/extension/agents/build-milestone.md +62 -0
- package/collections/extension/skills/build-backbone-component/SKILL.md +100 -0
- package/collections/extension/skills/build-backbone-component/references/backbone-scaffolds.md +162 -0
- package/collections/extension/skills/build-backbone-component/references/react-mount-path.md +93 -0
- package/collections/extension/skills/build-component/SKILL.md +33 -0
- package/collections/extension/skills/build-component/palette/README.md +3 -0
- package/collections/extension/skills/build-component/palette/colors.md +87 -0
- package/collections/extension/skills/build-component/references/best-practices.md +8 -0
- package/collections/extension/skills/build-component/references/component-organization.md +21 -0
- package/collections/extension/skills/build-component/references/figma-integration.md +5 -0
- package/collections/extension/skills/build-component/references/icons.md +8 -0
- package/collections/extension/skills/build-component/references/layout-and-structure.md +8 -0
- package/collections/extension/skills/build-component/references/state-management.md +25 -0
- package/collections/extension/skills/build-component/references/ui-kit-and-styling.md +13 -0
- package/collections/extension/skills/build-component/typography/README.md +3 -0
- package/collections/extension/skills/build-component/typography/variants.md +79 -0
- package/collections/extension/skills/ci-cd/SKILL.md +67 -0
- package/collections/extension/skills/ci-cd/references/bundle-and-troubleshooting.md +50 -0
- package/collections/extension/skills/ci-cd/references/file-registry.md +28 -0
- package/collections/extension/skills/connect-api/SKILL.md +23 -0
- package/collections/extension/skills/connect-api/references/api-call-structure.md +84 -0
- package/collections/extension/skills/connect-api/references/best-practices.md +10 -0
- package/collections/extension/skills/connect-api/references/data-fetchers.md +52 -0
- package/collections/extension/skills/connect-api/references/error-handling.md +34 -0
- package/collections/extension/skills/connect-api/references/hook-organization.md +11 -0
- package/collections/extension/skills/connect-api/references/query-keys.md +21 -0
- package/collections/extension/skills/connect-api/references/react-query-usage.md +83 -0
- package/collections/extension/skills/connect-api/references/url-placeholders.md +17 -0
- package/collections/extension/skills/modify-extension/SKILL.md +110 -0
- package/collections/extension/skills/modify-extension/references/architecture.md +55 -0
- package/collections/extension/skills/modify-extension/references/implementation-patterns.md +180 -0
- package/collections/extension/skills/scaffold-react-feature/SKILL.md +141 -0
- package/collections/extension/skills/write-test-cases/SKILL.md +93 -0
- package/package.json +1 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: modify-extension
|
|
3
|
+
description: Full command over Chrome extension top-level code in hiver-extension-v3/. Use when adding new features, modifying the script injection pipeline, fixing load sequence bugs, adding service worker commands, new iframes, new context menus, or any other change scoped to the extension layer.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Build Extension V3
|
|
7
|
+
|
|
8
|
+
**Scope:** All code inside `hiver-extension-v3/` — manifest, service worker, content scripts, iframes, context menus, env config, and `src/` files.
|
|
9
|
+
|
|
10
|
+
**Out of scope:** The React app bundles (`gx_combined.js`, `gx_react_combined.js`, `index.js`) that get injected into Gmail. Those are built from a separate part of the codebase. This skill only owns the extension layer that loads and injects those bundles.
|
|
11
|
+
|
|
12
|
+
**Never touch `chrome_extension_dev/`** — it is auto-generated from `hiver-extension-v3/` during local dev builds. All changes go into `hiver-extension-v3/` only.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Step 1 — Understand the Task
|
|
17
|
+
|
|
18
|
+
Read the task and classify it:
|
|
19
|
+
|
|
20
|
+
| Task type | Primary files involved |
|
|
21
|
+
|---|---|
|
|
22
|
+
| New service worker message command | `service_worker.js` |
|
|
23
|
+
| New context menu item | `ContextMenu.js`, `service_worker.js` |
|
|
24
|
+
| New iframe | `content_scripts/iframes.js`, `manifest.json`, new `src/iframe_*.html` |
|
|
25
|
+
| New content script | `manifest.json`, new file in `content_scripts/` |
|
|
26
|
+
| Modify script injection pipeline | `service_worker.js`, `grexit.js` |
|
|
27
|
+
| Fix load sequence bug | `grexit.js`, `content_scripts/gmail.js`, `content_scripts/utils.js` |
|
|
28
|
+
| New manifest permission or resource | `manifest.json` |
|
|
29
|
+
| Env-specific behavior | `env.js`, `service_worker.js` |
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Step 2 — Write Plain English Test Cases (TDD)
|
|
34
|
+
|
|
35
|
+
Before writing any code, write explicit test cases:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
TEST: [short name]
|
|
39
|
+
GIVEN: [precondition / starting state]
|
|
40
|
+
WHEN: [event or action that occurs]
|
|
41
|
+
THEN: [expected outcome]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then trace through the existing code and confirm each test currently fails. Implement. Then trace through updated code and confirm each test now passes.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Step 3 — Read the Relevant Files
|
|
49
|
+
|
|
50
|
+
Always read the files you will touch before editing.
|
|
51
|
+
|
|
52
|
+
## Reference files
|
|
53
|
+
|
|
54
|
+
**Load lazily — only read a reference file when you are about to perform that specific task. Do NOT read all files upfront.**
|
|
55
|
+
|
|
56
|
+
| Topic | File | When to load |
|
|
57
|
+
|-------|------|--------------|
|
|
58
|
+
| Architecture (file roles, injection pipeline, env behavior) | [references/architecture.md](references/architecture.md) | When you need to understand file relationships or the boot sequence |
|
|
59
|
+
| Implementation patterns (service worker, context menu, iframe, content script, manifest, env-gating, logging) | [references/implementation-patterns.md](references/implementation-patterns.md) | When writing code for a specific task type |
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Step 4 — Check if Chrome APIs or Patterns Are Unfamiliar
|
|
64
|
+
|
|
65
|
+
If the task requires a Chrome extension API that does not already exist in the codebase, **do not guess**. Use WebSearch:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
search: "chrome extension manifest v3 <topic> site:developer.chrome.com"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Always prefer the Chrome Developers documentation over general web search results. Reconcile with how the existing codebase handles similar things.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Step 5 — Implement
|
|
76
|
+
|
|
77
|
+
Load the relevant reference file for the task type and follow the pattern.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Step 6 — Verify Test Cases
|
|
82
|
+
|
|
83
|
+
After implementing, trace through each test case from Step 2:
|
|
84
|
+
- Identify the exact code path that satisfies the `THEN` condition
|
|
85
|
+
- If any test still fails, fix before closing the task
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Hard Rules — Never Violate
|
|
90
|
+
|
|
91
|
+
1. **Script load order in manifest is fixed** — `hiver-logger.js` → (performanceLogger, utils, gmail, iframes) → `grexit.js`. Wrong order = silent runtime failure.
|
|
92
|
+
2. **No import/export in content scripts** — Only `service_worker.js` and files it imports are ES modules.
|
|
93
|
+
3. **Always return true for async message handlers** — If `sendResponse` is called inside `.then()` or after `await`, the `case` block must `return true`.
|
|
94
|
+
4. **New DOM manipulation needs trusted types** — Route through `src/trusted-type-policy.js`.
|
|
95
|
+
5. **Use reloadUntil for critical boot failures** — Add entry to `reloadKeys` with unique key and `maxCount: 5`.
|
|
96
|
+
6. **Log all significant pipeline stages** — New boot steps must call `postMessageToLogger` on both success and failure.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Checklist
|
|
101
|
+
|
|
102
|
+
- [ ] Relevant files read before editing
|
|
103
|
+
- [ ] Plain English test cases written before implementation
|
|
104
|
+
- [ ] Implementation follows the correct pattern for the task type
|
|
105
|
+
- [ ] Script load order in `manifest.json` is correct (if manifest was changed)
|
|
106
|
+
- [ ] `return true` present for any new async message handler
|
|
107
|
+
- [ ] New files under `src/` are covered by `web_accessible_resources`
|
|
108
|
+
- [ ] New chrome API usage has corresponding permission in manifest
|
|
109
|
+
- [ ] Env-gating done correctly (not using `import` in content scripts)
|
|
110
|
+
- [ ] Each test case traced through updated code and confirmed passing
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Extension Architecture Reference
|
|
2
|
+
|
|
3
|
+
## File Roles and Module Types
|
|
4
|
+
|
|
5
|
+
| File | Role | Module type |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| `manifest.json` | Permissions, content script order, web_accessible_resources | — |
|
|
8
|
+
| `service_worker.js` | Message hub, script/CSS injection, update lifecycle | **ES module** |
|
|
9
|
+
| `grexit.js` | Gmail boot orchestrator — runs the full load sequence | **NOT a module** (uses globals) |
|
|
10
|
+
| `hiver-logger.js` | Constants + `HiverLogger` class — must load before `grexit.js` | **NOT a module** |
|
|
11
|
+
| `customClasses.js` | `CustomFetchError` | ES module (imported by `service_worker.js`) |
|
|
12
|
+
| `ContextMenu.js` | Context menu creation + click handling | ES module (imported by `service_worker.js`) |
|
|
13
|
+
| `env.js` | Production env: `HIVER_V2_HOST`, `HIVER_SELECTORS_API_URL`, `APP_ENV = 'production'` | ES module |
|
|
14
|
+
| `env.dev.js` | Dev env overrides (same exports, different values) | ES module |
|
|
15
|
+
| `content_scripts/utils.js` | `autoRetry`, `waitUntil`, `reloadUntil`, `getDebugEnabled` | Global |
|
|
16
|
+
| `content_scripts/gmail.js` | `waitForGmailToLoadNew`, `waitForGmailInitialization` | Global |
|
|
17
|
+
| `content_scripts/iframes.js` | `injectExtensionIframes` — injects 7 iframes | Global |
|
|
18
|
+
| `content_scripts/flowTracer.js` | Flow tracing (runs in MAIN world at document_start) | Global |
|
|
19
|
+
| `content_scripts/performanceLogger.js` | Performance logging helpers | Global |
|
|
20
|
+
| `src/selectors.js` | Resolves Gmail DOM selectors from `localStorage('selectors')` | Injected via `chrome.scripting` |
|
|
21
|
+
| `src/trusted-type-policy.js` | CSP Trusted Types policy setup | Injected via `chrome.scripting` |
|
|
22
|
+
| `src/iframe_*.html` | Individual iframe pages (editor, chat, analytics, gainsight, video, loggly) | Standalone |
|
|
23
|
+
| `src/iframe_*_handler.js` | Handlers loaded inside each iframe | Global (in iframe context) |
|
|
24
|
+
|
|
25
|
+
## Script Injection Pipeline
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
grexit.js boots in Gmail tab
|
|
29
|
+
│
|
|
30
|
+
├─ [1] injectTrustedTypePolicy()
|
|
31
|
+
│ → sends "injectTrustedTypePolicy" → service_worker executes src/trusted-type-policy.js
|
|
32
|
+
│
|
|
33
|
+
├─ [2] autoRetry(setupExtensionMetaDataGlobals)
|
|
34
|
+
│ → sends "setupExtensionMetaDataGlobals" → service_worker injects version/id globals
|
|
35
|
+
│
|
|
36
|
+
├─ [3] initializeHiverExtension() [IIFE]
|
|
37
|
+
│ → sends "fetchSelectorsAndApplicationVersions"
|
|
38
|
+
│ → service_worker fetches in parallel:
|
|
39
|
+
│ gmailSelectors API → stored in localStorage('selectors')
|
|
40
|
+
│ runtimeBundleVersion API → extensionUiVersion, channelsUiVersion, featureFlags
|
|
41
|
+
│
|
|
42
|
+
├─ [4] → sends "injectHiverSelectorScript" → service_worker executes src/selectors.js
|
|
43
|
+
│
|
|
44
|
+
├─ [5] → sends "injectAppScripts" → service_worker executes:
|
|
45
|
+
│ gx_combined.js + gx_react_combined.js + index.js
|
|
46
|
+
│
|
|
47
|
+
└─ [6] injectExtensionIframes() → 7 hidden iframes injected into Gmail DOM
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Environment Behavior
|
|
51
|
+
|
|
52
|
+
| `APP_ENV` | Script paths | CSS injection | Update checks |
|
|
53
|
+
|---|---|---|---|
|
|
54
|
+
| `'production'` | Fetched from `runtimeBundleVersion` API | No | Yes |
|
|
55
|
+
| anything else | `build/latest/*.js` (local) | Yes (`insertAppCSSOnBuddyEnvironment`) | No |
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Extension Implementation Patterns
|
|
2
|
+
|
|
3
|
+
## Adding a new service worker message command
|
|
4
|
+
|
|
5
|
+
Add a case inside the `onMessage` switch in `service_worker.js`:
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
// async command — must return true
|
|
9
|
+
case 'myNewCommand_1':
|
|
10
|
+
myAsyncOperation(request.someArg).then(sendResponse);
|
|
11
|
+
return true; // required — keeps the message port open until sendResponse fires
|
|
12
|
+
|
|
13
|
+
// sync command
|
|
14
|
+
case 'myNewCommand_2':
|
|
15
|
+
sendResponse(someValue);
|
|
16
|
+
break;
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Send from a content script:
|
|
20
|
+
```js
|
|
21
|
+
chrome.runtime.sendMessage({ command: 'myNewCommand_1', someArg: value }, ({ success, data, error }) => {
|
|
22
|
+
if (!success) { /* handle error */ }
|
|
23
|
+
// use data
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Adding a new context menu item
|
|
30
|
+
|
|
31
|
+
In `ContextMenu.js`, add inside `createContextMenu()`:
|
|
32
|
+
```js
|
|
33
|
+
chrome.contextMenus.create({
|
|
34
|
+
id: 'myNewItem',
|
|
35
|
+
title: 'My New Action',
|
|
36
|
+
contexts: ['page'],
|
|
37
|
+
documentUrlPatterns: ['*://mail.google.com/*']
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Add a handler function:
|
|
42
|
+
```js
|
|
43
|
+
const handleMyNewItem = async (tabId) => {
|
|
44
|
+
try {
|
|
45
|
+
chrome.tabs.sendMessage(tabId, {
|
|
46
|
+
type: 'CONTEXT_MENU_WORKER_EVENT',
|
|
47
|
+
payload: { type: 'MY_NEW_ACTION' }
|
|
48
|
+
});
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.log('== FAILED TO HANDLE MY_NEW_ACTION ==', err);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Wire it in `handleContextMenuClick`:
|
|
56
|
+
```js
|
|
57
|
+
if (info.menuItemId === 'myNewItem') {
|
|
58
|
+
await handleMyNewItem(tab.id);
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Adding a new iframe
|
|
65
|
+
|
|
66
|
+
In `content_scripts/iframes.js`, add inside `injectExtensionIframes()` following the existing pattern:
|
|
67
|
+
```js
|
|
68
|
+
const iframe_myfeature_id = 'Hiver_iframe_myfeature';
|
|
69
|
+
let iframe_myfeature = doc.getElementById(iframe_myfeature_id);
|
|
70
|
+
if (!iframe_myfeature) {
|
|
71
|
+
injectIframe({
|
|
72
|
+
id: iframe_myfeature_id,
|
|
73
|
+
src: chrome.runtime.getURL('src/iframe_myfeature.html'),
|
|
74
|
+
style: 'display:none;border:none;',
|
|
75
|
+
body
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Then create `src/iframe_myfeature.html`. The existing `"src/*"` wildcard in `web_accessible_resources` already covers it — verify it does before adding a new entry.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Adding a new content script (shared globals)
|
|
85
|
+
|
|
86
|
+
Create the file in `content_scripts/`. Add it to `manifest.json` at the **correct position** in the `js` array:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
"js": [
|
|
90
|
+
"hiver-logger.js",
|
|
91
|
+
"src/trusted-type-policy.js",
|
|
92
|
+
"content_scripts/performanceLogger.js",
|
|
93
|
+
"content_scripts/utils.js",
|
|
94
|
+
"content_scripts/gmail.js",
|
|
95
|
+
"content_scripts/iframes.js",
|
|
96
|
+
"content_scripts/myNewScript.js",
|
|
97
|
+
"grexit.js"
|
|
98
|
+
]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Rules:
|
|
102
|
+
- If your script defines globals that other scripts consume → place it **before** those scripts
|
|
103
|
+
- If your script uses globals from `hiver-logger.js` → place it **after** `hiver-logger.js`
|
|
104
|
+
- `grexit.js` is always **last**
|
|
105
|
+
- Do **not** use `import`/`export` — content scripts are not ES modules
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Adding a file injected via chrome.scripting
|
|
110
|
+
|
|
111
|
+
Create the file in `src/`. Inject it from `service_worker.js`:
|
|
112
|
+
```js
|
|
113
|
+
function executeScripts(tabId, files, executionWorld = 'MAIN') {
|
|
114
|
+
return chrome.scripting.executeScript({ target: { tabId }, files, world: executionWorld });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// usage
|
|
118
|
+
executeScripts(tabId, ['src/myfile.js']); // runs in MAIN world by default
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Check `web_accessible_resources` in `manifest.json`. The existing wildcard `"src/*"` covers any new file under `src/`.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Adding a new manifest permission
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
"permissions": ["scripting", "contextMenus", "myNewPermission"]
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
For host permissions:
|
|
132
|
+
```json
|
|
133
|
+
"host_permissions": ["*://mail.google.com/*", "*://mynewhost.com/*"]
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Env-gating new behavior
|
|
139
|
+
|
|
140
|
+
In `service_worker.js` (ES module — `APP_ENV` is importable):
|
|
141
|
+
```js
|
|
142
|
+
import { APP_ENV } from './env.js';
|
|
143
|
+
|
|
144
|
+
if (APP_ENV !== 'production') {
|
|
145
|
+
// dev-only behavior
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
In content scripts (`APP_ENV` is not importable). Pass it through a service worker message response or inject it as a global via `executeFunction`:
|
|
150
|
+
```js
|
|
151
|
+
function setMyEnvGlobal(appEnv) {
|
|
152
|
+
window.MY_APP_ENV = appEnv;
|
|
153
|
+
}
|
|
154
|
+
// in service_worker.js handler:
|
|
155
|
+
executeFunction({ tabId, functionToExecute: setMyEnvGlobal, args: [APP_ENV] });
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Adding a new log stage in the boot sequence
|
|
161
|
+
|
|
162
|
+
```js
|
|
163
|
+
const currentStage = 'myNewStage';
|
|
164
|
+
const { duration, stageDuration } = getStageDetails(currentStage);
|
|
165
|
+
postMessageToLogger({
|
|
166
|
+
message: 'Description of what happened',
|
|
167
|
+
stage: currentStage,
|
|
168
|
+
duration,
|
|
169
|
+
stageDuration,
|
|
170
|
+
level: LOG_LEVELS.DEBUG, // or LOG_LEVELS.ERROR
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
For failures, add a `LOG_MESSAGES` constant in `hiver-logger.js`:
|
|
175
|
+
```js
|
|
176
|
+
const LOG_MESSAGES = {
|
|
177
|
+
// existing...
|
|
178
|
+
MY_NEW_FAILURE: 'My new thing failed to initialize',
|
|
179
|
+
};
|
|
180
|
+
```
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: scaffold-react-feature
|
|
3
|
+
description: Generate standard feature folder structure, wiring, and feature flag boilerplate for new features. Internal skill — used by build-milestone agent, not invoked directly by users.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Scaffold Feature
|
|
7
|
+
|
|
8
|
+
**Trigger:** Only when building a **new feature or new component**. Do NOT use for bug fixes or issue fixes.
|
|
9
|
+
|
|
10
|
+
**Used by:** `build-milestone` agent internally. Not invoked directly by users.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Step 1 — Determine Mount Type
|
|
15
|
+
|
|
16
|
+
Ask (or infer from agent context / milestone doc):
|
|
17
|
+
|
|
18
|
+
> **Is this feature mounted via Backbone or pure React?**
|
|
19
|
+
|
|
20
|
+
- **Backbone mount** → the feature is rendered inside a Backbone view using `ReactBackboneModules`
|
|
21
|
+
- **React-only** → the feature is lazy-imported and rendered directly in `react-extn/src/index.js`
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Step 2 — Create Folder Structure
|
|
26
|
+
|
|
27
|
+
Create the following under `react-extn/src/<FeatureName>/`:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
<FeatureName>/
|
|
31
|
+
index.js ← main React component export
|
|
32
|
+
hooks/ ← custom hooks (data, state, business logic)
|
|
33
|
+
constants/
|
|
34
|
+
queries.js ← React Query query/mutation keys
|
|
35
|
+
index.js ← other constants (strings, enums, config)
|
|
36
|
+
components/ ← sub-components
|
|
37
|
+
utils.js ← pure utility functions
|
|
38
|
+
mounter.js ← ONLY if Backbone-mounted (see Step 3a)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Step 3a — Backbone Mount Wiring
|
|
44
|
+
|
|
45
|
+
### Create `mounter.js`
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
import ReactDOM from 'react-dom/client';
|
|
49
|
+
import { HiverProvider } from '@hiver/hiver-ui-kit';
|
|
50
|
+
import { Provider } from 'react-redux';
|
|
51
|
+
import HiverQueryClientProvider from '../reactQueryClient.js';
|
|
52
|
+
import MyFeature from './index.js';
|
|
53
|
+
|
|
54
|
+
export const mountMyFeature = (store) => (container, props) => {
|
|
55
|
+
const root = ReactDOM.createRoot(container);
|
|
56
|
+
root.render(
|
|
57
|
+
<HiverProvider custom>
|
|
58
|
+
<HiverQueryClientProvider>
|
|
59
|
+
<Provider store={store}>
|
|
60
|
+
<MyFeature {...props} />
|
|
61
|
+
</Provider>
|
|
62
|
+
</HiverQueryClientProvider>
|
|
63
|
+
</HiverProvider>
|
|
64
|
+
);
|
|
65
|
+
return root.unmount.bind(root);
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Replace `MyFeature` / `mountMyFeature` with the actual feature name.
|
|
70
|
+
|
|
71
|
+
### Register in `ReactBackboneModules/index.js`
|
|
72
|
+
|
|
73
|
+
1. Add the import at the top:
|
|
74
|
+
```js
|
|
75
|
+
import { mountMyFeature } from '../MyFeature/mounter';
|
|
76
|
+
```
|
|
77
|
+
2. Inside `appendReactBackboneModules()`, add:
|
|
78
|
+
```js
|
|
79
|
+
myFeature: createModule(mountMyFeature(store)),
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Use camelCase for the module key (e.g., `myFeature`).
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Step 3b — React-only Wiring
|
|
87
|
+
|
|
88
|
+
### Add lazy import in the file where the component is consumed
|
|
89
|
+
|
|
90
|
+
Do **not** default to `react-extn/src/index.js`. Add the lazy import in whichever file actually renders this component (e.g., a parent page, a layout component, or a route file).
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
const LazyMyFeature = React.lazy(() => import('./MyFeature' /* webpackChunkName: "my_feature" */))
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- Use `PascalCase` for the variable name prefixed with `Lazy`
|
|
97
|
+
- Use `snake_case` for `webpackChunkName`
|
|
98
|
+
- Render `<LazyMyFeature />` at the correct place in that file, wrapped in `<React.Suspense>` if not already present
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Step 4 — Feature Flag Boilerplate (if needed)
|
|
103
|
+
|
|
104
|
+
If the milestone requires a feature flag:
|
|
105
|
+
|
|
106
|
+
### Naming convention
|
|
107
|
+
Use the `rls-` prefix: e.g., `rls-my-feature-name`
|
|
108
|
+
|
|
109
|
+
### Add key to `react-extn/src/utils/constants.js`
|
|
110
|
+
|
|
111
|
+
Find the `featureKeys` object and add:
|
|
112
|
+
```js
|
|
113
|
+
myFeatureName: 'rls-my-feature-name',
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Add checker function to `react-extn/src/utils/features.js`
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
export const isMyFeatureEnabled = (availableFeatures) =>
|
|
120
|
+
isFeatureActive(availableFeatures, featureKeys.myFeatureName);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Gate the feature render
|
|
124
|
+
|
|
125
|
+
Use `availableFeatures` from Redux and the checker function to conditionally render:
|
|
126
|
+
```js
|
|
127
|
+
const availableFeatures = useSelector(state => state.availableFeatures);
|
|
128
|
+
if (!isMyFeatureEnabled(availableFeatures)) return null;
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Checklist (confirm before handing off to build-component)
|
|
134
|
+
|
|
135
|
+
- [ ] Folder structure created under `react-extn/src/<FeatureName>/`
|
|
136
|
+
- [ ] Mount type determined: Backbone / React-only
|
|
137
|
+
- [ ] `mounter.js` created and registered in `ReactBackboneModules/index.js` (Backbone only)
|
|
138
|
+
- [ ] Lazy import added in the correct consumer file (React-only, not necessarily the root `index.js`)
|
|
139
|
+
- [ ] Feature flag key added to `constants.js` (if needed)
|
|
140
|
+
- [ ] Feature flag checker added to `features.js` (if needed)
|
|
141
|
+
- [ ] Feature is gated behind the flag in the component (if needed)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: write-test-cases
|
|
3
|
+
description: Writes structured behavioural test cases for a milestone into a progress-sheet markdown file. Invoked by build-milestone before any implementation begins (Normal Mode) or once per feedback round (Fix Mode). Never writes source code.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Write Test Cases
|
|
7
|
+
|
|
8
|
+
Generates a complete set of structured behavioural test cases for a milestone and saves them as a progress sheet that `build-milestone` uses to drive its TDD loop.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
- Invoked by `build-milestone` at the start of **Normal Mode** — before any implementation begins
|
|
13
|
+
- Invoked by `build-milestone` at the start of **Fix Mode** — once per feedback round, scoped to the feedback
|
|
14
|
+
|
|
15
|
+
## Inputs
|
|
16
|
+
|
|
17
|
+
- Path to the milestone document (always required)
|
|
18
|
+
- Mode: `normal` or `feedback`
|
|
19
|
+
- If `feedback` mode: path to the feedback file
|
|
20
|
+
|
|
21
|
+
## Instructions
|
|
22
|
+
|
|
23
|
+
### Normal Mode
|
|
24
|
+
|
|
25
|
+
1. Read the milestone document thoroughly. Extract:
|
|
26
|
+
- Goal
|
|
27
|
+
- Acceptance criteria (2–3 product-level "done" conditions)
|
|
28
|
+
- Architectural approach and high-level areas affected
|
|
29
|
+
|
|
30
|
+
2. Explore the codebase **scoped strictly to the "High-level areas affected" section of the milestone doc**. Read only the files and directories explicitly named there. Do NOT fan out to unrelated modules. Limit reads to what is minimally needed to understand current state — what exists, what doesn't, what will change.
|
|
31
|
+
|
|
32
|
+
3. If the milestone doc contains Figma design links, load them using the Figma MCP tool. Analyze the designs to identify UI-specific behaviours and edge cases. Load only the specific nodes linked — do not traverse the full Figma file.
|
|
33
|
+
|
|
34
|
+
4. Derive test cases from the acceptance criteria and your codebase exploration. For each observable behaviour the milestone introduces or changes, write one test case. Aim for full coverage across:
|
|
35
|
+
- Happy path (primary user flows)
|
|
36
|
+
- Edge cases (boundary conditions, empty states, large data)
|
|
37
|
+
- Error states (API failures, invalid input, permission issues)
|
|
38
|
+
- Regressions (existing behaviours that must not break)
|
|
39
|
+
|
|
40
|
+
5. Write ALL test cases to `milestone-tests/<milestone-name>-tests.md` — create the file if it doesn't exist. All test cases start with `⬜ Pending` status.
|
|
41
|
+
|
|
42
|
+
6. Use this format for every test case:
|
|
43
|
+
|
|
44
|
+
```markdown
|
|
45
|
+
## TC-001 | Category: happy-path | ⬜ Pending
|
|
46
|
+
Given: <system or state precondition>
|
|
47
|
+
When: <user action or system trigger>
|
|
48
|
+
Then: <observable outcome — what the user sees or what changes in state>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
7. After writing all test cases, exit. Do not write any source code.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
### Feedback Mode
|
|
56
|
+
|
|
57
|
+
1. Read the milestone document to understand the original scope.
|
|
58
|
+
|
|
59
|
+
2. Read the feedback file. Identify the **active** (non-stale) feedback entry.
|
|
60
|
+
|
|
61
|
+
3. Explore the codebase to understand the current state of the implementation for this milestone.
|
|
62
|
+
|
|
63
|
+
4. Derive test cases scoped **only** to what the feedback describes. Write as many as needed to fully cover the feedback — happy path, edge cases, and regressions relevant to the fix.
|
|
64
|
+
|
|
65
|
+
5. Append the new test cases to the existing `milestone-tests/<milestone-name>-tests.md` under a new section:
|
|
66
|
+
|
|
67
|
+
```markdown
|
|
68
|
+
## Feedback Round N
|
|
69
|
+
|
|
70
|
+
### FTC-001 | Category: regression | ⬜ Pending
|
|
71
|
+
Given: <precondition>
|
|
72
|
+
When: <action>
|
|
73
|
+
Then: <expected outcome>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Where `N` is incremented from the last existing Feedback Round number in the file.
|
|
77
|
+
|
|
78
|
+
6. After appending, exit. Do not write any source code.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Output
|
|
83
|
+
|
|
84
|
+
A `milestone-tests/<milestone-name>-tests.md` file (created or appended) with all new test cases set to `⬜ Pending`.
|
|
85
|
+
|
|
86
|
+
The `milestone-tests/` directory lives alongside the `milestones/` directory under the same parent.
|
|
87
|
+
|
|
88
|
+
## Rules
|
|
89
|
+
|
|
90
|
+
- NEVER write source code
|
|
91
|
+
- NEVER mark any test case as passing — all new TCs start as `⬜ Pending`
|
|
92
|
+
- NEVER modify existing test case entries — only append new ones
|
|
93
|
+
- In Feedback Mode, only write TCs scoped to the active feedback — do not add unrelated cases
|