@harperfast/template-react-ts-studio 1.12.6 → 1.12.8

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.
@@ -0,0 +1,197 @@
1
+ # Harper Local Development
2
+
3
+ Guidelines for running and managing Harper on a developer machine: isolated dev instances, parallel instances across git worktrees, and the local tooling around them.
4
+
5
+ ## 1. Dev Instances
6
+
7
+ ### 1.1 Running Dev Instances in Git Worktrees
8
+
9
+ How to run one Harper dev instance per git worktree (or per agent session) on the same machine without the instances colliding. This skill ships a ready-to-copy wrapper script, `scripts/harper-dev.mjs`, that does all of it.
10
+
11
+ #### When to Use
12
+
13
+ - You, or several agents, work on one Harper app in parallel `git worktree`s and each needs a running server.
14
+ - A second `harper dev .` fails with `EADDRINUSE`, or exits with `Error: Harper is already running`.
15
+ - You are setting up a project's `npm run dev` so it is safe to start from any checkout.
16
+
17
+ #### How It Works
18
+
19
+ ##### What collides
20
+
21
+ A Harper process claims two things that a second instance started from another worktree would also try to claim:
22
+
23
+ | Shared by default | What goes wrong | Isolate with |
24
+ | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
25
+ | Listeners: 9926 (HTTP), 9925 (operations API), 1883/8883 (MQTT), 9933 (replication, Harper Pro), 9229 (debugger in dev) | `EADDRINUSE` | A per-instance loopback address |
26
+ | The data root (wherever `~/.harperdb/hdb_boot_properties.file` points, usually `~/harper`) | The second start exits with code 4 because `hdb.pid` is held; taking turns means every branch shares one database and component set | A per-worktree data root, keyed on path |
27
+
28
+ **Vary the address, not the port.** Each instance binds the default ports on its own loopback address: `http://127.0.0.3:9926`, `http://127.0.0.7:9926`. No config, `.env` or tooling needs per-worktree port numbers. Browsers scope cookies by host, not port, so sessions don't leak between instances either.
29
+
30
+ **Keep the data root outside the app directory.** Harper writes logs, config, backups and lock files into its data root continuously. Inside the app directory, those writes reach anything watching it (a component with a broad `files` glob, Harper's legacy `WATCH_DIR` watcher, Vite or other dev tooling) and can set off a reload loop. They would also show up in `git status`.
31
+
32
+ ##### What the wrapper does
33
+
34
+ `scripts/harper-dev.mjs` is a dependency-free wrapper around `harper dev .`. On every start it:
35
+
36
+ 1. Derives the data root from the checkout: `~/.harper-dev/<project>/<checkout>-<hash>/`. `<project>` is the repository's directory name, so every worktree of one repository shares a project directory. `<checkout>` is the worktree's directory name, and `<hash>` is the first 12 hex characters of the SHA-256 of the checkout's real path. The same checkout always gets the same root, and another worktree gets another.
37
+ 2. Takes a lock for the checkout, `<data root>.lock`, and holds it until it exits. A second start from the same checkout stops with `already running` instead of racing the first through install or startup.
38
+ 3. Deletes inherited environment variables that are set to `''` but defined in `.env`. Harper's `loadEnv` never overrides a variable that is already set, even to an empty string, so the `.env` value would be silently lost.
39
+ 4. Claims a loopback address from `127.0.0.2` to `127.0.0.33`. Claims are lock files in `~/.harper-dev/.loopback/` holding the wrapper's PID, and claims whose owner has died are taken over. It starts from a slot derived from the path, so a worktree usually keeps its URL, and skips any address where one of Harper's ports is already in use.
40
+ 5. Installs a new data root with `harper install` the first time, without prompting. It uses `DEFAULTS_MODE=dev`, `HDB_ADMIN_USERNAME` (default `admin`) and `HDB_ADMIN_PASSWORD` (default random, printed once). The install runs with a throwaway `HOME`. When `~/.harperdb` has no boot file, Harper's install writes one pointing at the new root, which would repoint bare `harper` commands at this instance.
41
+ 6. Turns off the operations API's Unix socket if binding one in the data root isn't permitted (as in an agent sandbox), since Harper fails to start otherwise.
42
+ 7. Writes `.harper-instance` (URL, host, data root, wrapper PID) into the checkout for tooling.
43
+ 8. Runs Harper with every listener bound to the claimed address. A `host:port` value binds only that address; a bare port binds all interfaces. Passing the data root as the `--ROOTPATH` flag, unlike the `ROOTPATH` env var, also overrides a global install's boot file.
44
+
45
+ ```bash
46
+ harper dev . --ROOTPATH=<data root> \
47
+ --HTTP_PORT=127.0.0.N:9926 --OPERATIONSAPI_NETWORK_PORT=127.0.0.N:9925 \
48
+ --MQTT_NETWORK_PORT=127.0.0.N:1883 --MQTT_NETWORK_SECUREPORT=127.0.0.N:8883 \
49
+ --REPLICATION_SECUREPORT=127.0.0.N:9933 --THREADS_DEBUG_HOST=127.0.0.N \
50
+ --OPERATIONSAPI_NETWORK_DOMAINSOCKET=<data root>/operations-server
51
+ ```
52
+
53
+ 9. Forwards Ctrl-C, `SIGTERM` and `SIGHUP` to Harper, then removes `.harper-instance`, its address claim and its lock when Harper exits.
54
+
55
+ It refuses to start when this checkout already has a live instance, or when an orphaned Harper (left by a `kill -9` of the wrapper) still holds the data root.
56
+
57
+ #### Setup
58
+
59
+ The wrapper is tested with Harper 5.3 on macOS (core and Pro) and Linux (core).
60
+
61
+ ##### Once per machine (macOS only)
62
+
63
+ Linux routes all of `127.0.0.0/8` to loopback. macOS only configures `127.0.0.1`, so alias the range. The aliases last until the next reboot:
64
+
65
+ ```bash
66
+ for i in $(seq 2 33); do sudo ifconfig lo0 alias 127.0.0.$i up; done
67
+ ```
68
+
69
+ To keep them across reboots, install the launchd daemon that `@harperfast/integration-testing` ships (see "Persisting across reboots" in its README). It aliases the same range, which that package's integration-test pool also uses.
70
+
71
+ ##### Once per project
72
+
73
+ 1. Copy `scripts/harper-dev.mjs` from this skill into the app, e.g. as `scripts/harper-dev.mjs`. It is also at `https://raw.githubusercontent.com/HarperFast/skills/main/harper-local-dev/scripts/harper-dev.mjs`.
74
+ 2. Point the dev script at it in `package.json`:
75
+
76
+ ```json
77
+ { "scripts": { "dev": "node scripts/harper-dev.mjs" } }
78
+ ```
79
+
80
+ 3. Add `.harper-instance` to `.gitignore`.
81
+ 4. If the app reads its own public URL from an env var (for OAuth redirects or links in emails), add that variable to `URL_ENV_VARS` at the top of the script. Values the wrapper sets win over `.env`. Every instance also gets `HARPER_DEV_URL`.
82
+ 5. If agents start the server from a sandbox, configure it as described in "Running Under an Agent Sandbox".
83
+
84
+ The wrapper runs `harper` from `PATH`. `npm run` puts `node_modules/.bin` first, so a project-local Harper wins over a global one. Set `HARPER_BIN` to pick a specific executable, such as Harper Pro.
85
+
86
+ ##### Per worktree
87
+
88
+ ```bash
89
+ git worktree add ../my-app-feature -b feature
90
+ cd ../my-app-feature
91
+ cp ../my-app/.env . # gitignored files don't come along; copy so each worktree can diverge
92
+ npm install # a real install per worktree
93
+ npm run dev
94
+ ```
95
+
96
+ ```
97
+ Installing a new Harper data root for /Users/you/code/my-app-feature
98
+ ...
99
+ Harper dev instance for /Users/you/code/my-app-feature
100
+ URL: http://127.0.0.7:9926
101
+ Operations: http://127.0.0.7:9925
102
+ Data root: /Users/you/.harper-dev/my-app/my-app-feature-a1b2c3d4e5f6
103
+ Admin: admin / <generated password>
104
+ ```
105
+
106
+ Worktrees can live anywhere, e.g. sibling directories, or `.claude/worktrees/<name>` the way Claude Code creates them (gitignore that directory). Each worktree has its own database, so seed data, users and any first-run bootstrap happen once per worktree.
107
+
108
+ Don't symlink `node_modules` between worktrees: branches' dependencies drift apart. With production defaults (`applications.allowedDirectory: app`), Harper also refuses modules that resolve outside the app with `Can not load module at ... outside of allowed path ...`. Large read-only assets that are gitignored, such as model files, can be symlinked instead of copied.
109
+
110
+ #### Finding the Instance from Tools
111
+
112
+ Ask the wrapper for the URL. It fails when this checkout has no running instance:
113
+
114
+ ```bash
115
+ HARPER_URL=$(node scripts/harper-dev.mjs url) || exit 1
116
+ ```
117
+
118
+ Don't fall back to `http://localhost:9926` when that fails. It reaches a different Harper, or none, and a QA or seeding tool would then work against the wrong database.
119
+
120
+ The wrapper records the running instance in `.harper-instance`:
121
+
122
+ ```json
123
+ {
124
+ "url": "http://127.0.0.7:9926",
125
+ "operationsUrl": "http://127.0.0.7:9925",
126
+ "host": "127.0.0.7",
127
+ "rootPath": "/Users/you/.harper-dev/my-app/my-app-feature-a1b2c3d4e5f6",
128
+ "pid": 12345
129
+ }
130
+ ```
131
+
132
+ Don't trust the file on its own. It outlives a wrapper that crashed, and by then its address may belong to another worktree. `url` only answers when the recorded wrapper is alive and still holds this checkout's lock and the address claim.
133
+
134
+ Run `url` from the worktree you are working in. Subagents and spawned tools may start in the main checkout, so give them the worktree path explicitly.
135
+
136
+ With dev defaults the debugger listens on `<host>:9229`. In `chrome://inspect`, under Configure, add `127.0.0.7:9229`.
137
+
138
+ #### Differences from a Plain `harper dev .`
139
+
140
+ - New data roots get dev defaults: plain HTTP, CORS `*`, one thread, logs on stdout, and `authorizeLocal`, which treats every request from `127.0.0.x` as the super user. To exercise real authentication, run `npm run dev -- --AUTHENTICATION_AUTHORIZELOCAL=false`. Extra arguments are passed to Harper and saved in the instance's config.
141
+ - Harper only marks session cookies `SameSite=None; Secure` for `localhost` and `127.0.0.1`. On `127.0.0.N`, cross-site requests don't carry the session; same-site browsing is unaffected.
142
+ - Services that allow-list origins or redirect URIs, such as OAuth providers, need the `127.0.0.N` origin added.
143
+
144
+ #### Running Under an Agent Sandbox
145
+
146
+ A sandboxed agent needs two permissions to run the wrapper: writing to `~/.harper-dev`, and listening on loopback ports. For Claude Code, add this to `~/.claude/settings.json` to cover every project, or to a project's `.claude/settings.json` to share it with the team. Claude Code merges these arrays across settings files.
147
+
148
+ ```json
149
+ {
150
+ "sandbox": {
151
+ "filesystem": { "allowWrite": ["~/.harper-dev"] },
152
+ "network": { "allowLocalBinding": true }
153
+ }
154
+ }
155
+ ```
156
+
157
+ - **Trust boundary.** Allowing `~/.harper-dev` lets an agent working in one project modify or delete every project's dev data roots, including the data of a running instance. The roots are disposable and source code stays outside them, so this is usually acceptable. For stricter setups, allow only the project's directory and the address claims, and create `~/.harper-dev` once outside the sandbox: the sandbox can create the allowed directories, but not their parent. `my-app` is the project directory shown in the `Data root` line at startup.
158
+
159
+ ```json
160
+ {
161
+ "sandbox": {
162
+ "filesystem": { "allowWrite": ["~/.harper-dev/my-app", "~/.harper-dev/.loopback"] }
163
+ }
164
+ }
165
+ ```
166
+
167
+ - **`allowLocalBinding`** applies to macOS only. Without it, every bind fails with `EPERM` and the wrapper stops with `not permitted to listen on ...`.
168
+ - **Unix sockets** stay blocked by default, so the wrapper turns off the operations API's Unix socket (startup shows `Unix socket off`). Reach the operations API over its HTTP URL instead.
169
+ - **HTTP from the sandbox:** sandboxed commands send HTTP through a proxy that refuses loopback addresses other than `127.0.0.1`, so `curl http://127.0.0.7:9926/...` gets a `403`. Bypass the proxy with `curl --noproxy '*'`. Node's built-in `fetch` ignores the proxy settings by default and connects directly.
170
+ - **Linux:** sandboxed commands run in their own network namespace, so a server started inside the sandbox is unreachable from the browser and from other sessions. Start `npm run dev` outside the sandbox: in your own terminal, through Claude Code's unsandboxed retry, or by listing the command in `sandbox.excludedCommands`.
171
+
172
+ Other agents' sandboxes need the same two permissions: writes to `~/.harper-dev`, and listening on loopback addresses.
173
+
174
+ #### Cleanup
175
+
176
+ ```bash
177
+ git worktree remove ../my-app-feature
178
+ rm -rf ~/.harper-dev/my-app/my-app-feature-a1b2c3d4e5f6 # optional; the path is printed at startup
179
+ ```
180
+
181
+ Data roots outlive their worktrees, and moving or renaming a checkout starts a new one. Delete a root to re-bootstrap that worktree, `~/.harper-dev/my-app` to reset one project, or all of `~/.harper-dev` to reset everything; `du -sh ~/.harper-dev` shows the space they use. None of this touches source code, `.env` files or a global Harper install.
182
+
183
+ #### Troubleshooting
184
+
185
+ | Message or symptom | Cause and fix |
186
+ | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
187
+ | `loopback addresses ... are not configured` | The macOS aliases are missing; they reset on reboot. Run the alias loop above. |
188
+ | `no free loopback address` | Every address in the range is in use: stop some instances, or raise `HARPER_DEV_LOOPBACK_COUNT` and alias more addresses. On Linux, a Harper bound to all interfaces blocks them all. |
189
+ | `not permitted to listen on 127.0.0.N` | A sandbox blocks binding ports. Allow it; see "Running Under an Agent Sandbox". |
190
+ | `cannot write to ~/.harper-dev/...` | A sandbox blocks writes to the data roots. Allow them; see "Running Under an Agent Sandbox". |
191
+ | `403` from the instance, inside a sandbox | The sandbox's HTTP proxy refuses loopback addresses. Use `curl --noproxy '*'`. |
192
+ | `Harper is already running for this checkout` | Another start from this checkout is running or starting. Use the URL it prints, or stop it first. |
193
+ | `a Harper process from an earlier run is still using ...` | The wrapper was killed without stopping Harper. Run the `kill` command it prints. |
194
+ | `Can not load module at ... outside of allowed path ...` | `node_modules`, or a package in it, is a symlink pointing outside the app. Run a real `npm install` in the worktree. |
195
+ | A value from `.env` is ignored | The variable is already set in the environment, and non-empty values from the shell win. Unset it, or use `loadEnv`'s `override: true`. |
196
+ | Startup banner shows `http://127.0.0.7:127.0.0.7:9926/` | Cosmetic: Harper's banner repeats the host when the port is given as `host:port`. The listener is correct. |
197
+ | `domainSocket ... exceeds the 103-byte ... limit` (warning) | The data root's path is too long for macOS, so the operations API's Unix socket is skipped; TCP still works. Use a shorter `HARPER_DEV_HOME`. |
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: harper-local-dev
3
+ description: Guidelines for running Harper on a developer machine, covering isolated
4
+ dev instances, parallel instances across git worktrees, loopback address
5
+ allocation, and per-instance data roots. Triggers on tasks involving local
6
+ Harper dev servers, running several Harper instances at once, git worktrees,
7
+ port conflicts (EADDRINUSE), dev-mode reload loops, or running a Harper dev
8
+ server under an AI-agent sandbox.
9
+ license: Apache-2.0
10
+ metadata:
11
+ author: harper
12
+ version: '1.0.0'
13
+ ---
14
+
15
+ # Harper Local Development
16
+
17
+ Guidelines for running and managing Harper on a developer machine. Where `harper-best-practices` covers how to build a Harper application, this skill covers the environment you build it in: how dev instances are started, isolated from one another, and cleaned up.
18
+
19
+ ## When to Use
20
+
21
+ Reference these guidelines when:
22
+
23
+ - Running more than one Harper dev instance on the same machine (one per git worktree, branch, or agent session)
24
+ - Diagnosing `EADDRINUSE` on Harper's default ports, or a dev server that reloads in a loop
25
+ - Setting up a project's `npm run dev` so that it is safe to run in parallel
26
+ - Pointing tooling (agents, browsers, test scripts) at the right dev instance for the current worktree
27
+ - Letting a sandboxed AI agent (e.g. Claude Code with the sandbox enabled) start and reach a dev instance
28
+
29
+ ## How It Works
30
+
31
+ 1. Identify the shared state two Harper processes would contend for (ports, data root, file watcher).
32
+ 2. Consult the relevant rule under "Quick Reference" below and read its rule file.
33
+ 3. Prefer the ready-to-copy scripts under `scripts/` over writing your own wrapper.
34
+
35
+ ## Examples
36
+
37
+ See the concrete examples embedded in each rule (wrapper scripts, `package.json` wiring, and debugging commands), plus the reference implementations in `scripts/`.
38
+
39
+ <!-- BEGIN GENERATED INDEX -->
40
+
41
+ ## Rule Categories by Priority
42
+
43
+ | Priority | Category | Impact | Prefix |
44
+ | -------- | ------------- | ------ | ------ |
45
+ | 1 | Dev Instances | HIGH | `dev-` |
46
+
47
+ ## Quick Reference
48
+
49
+ ### 1. Dev Instances (HIGH)
50
+
51
+ - `running-dev-instances-in-worktrees` — Run multiple Harper dev instances in parallel from git worktrees with isolated data roots and loopback addresses.
52
+
53
+ <!-- END GENERATED INDEX -->
54
+
55
+ ## How to Use
56
+
57
+ Read individual rule files for detailed explanations and code examples:
58
+
59
+ ```
60
+ rules/running-dev-instances-in-worktrees.md
61
+ ```
62
+
63
+ ## Full Compiled Document
64
+
65
+ For the complete guide with all rules expanded: `AGENTS.md`
@@ -0,0 +1,198 @@
1
+ ---
2
+ name: running-dev-instances-in-worktrees
3
+ description: Run multiple Harper dev instances in parallel from git worktrees with isolated data roots and loopback addresses.
4
+ metadata:
5
+ mode: synthesized
6
+ ---
7
+
8
+ # Running Dev Instances in Git Worktrees
9
+
10
+ How to run one Harper dev instance per git worktree (or per agent session) on the same machine without the instances colliding. This skill ships a ready-to-copy wrapper script, `scripts/harper-dev.mjs`, that does all of it.
11
+
12
+ ## When to Use
13
+
14
+ - You, or several agents, work on one Harper app in parallel `git worktree`s and each needs a running server.
15
+ - A second `harper dev .` fails with `EADDRINUSE`, or exits with `Error: Harper is already running`.
16
+ - You are setting up a project's `npm run dev` so it is safe to start from any checkout.
17
+
18
+ ## How It Works
19
+
20
+ ### What collides
21
+
22
+ A Harper process claims two things that a second instance started from another worktree would also try to claim:
23
+
24
+ | Shared by default | What goes wrong | Isolate with |
25
+ | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
26
+ | Listeners: 9926 (HTTP), 9925 (operations API), 1883/8883 (MQTT), 9933 (replication, Harper Pro), 9229 (debugger in dev) | `EADDRINUSE` | A per-instance loopback address |
27
+ | The data root (wherever `~/.harperdb/hdb_boot_properties.file` points, usually `~/harper`) | The second start exits with code 4 because `hdb.pid` is held; taking turns means every branch shares one database and component set | A per-worktree data root, keyed on path |
28
+
29
+ **Vary the address, not the port.** Each instance binds the default ports on its own loopback address: `http://127.0.0.3:9926`, `http://127.0.0.7:9926`. No config, `.env` or tooling needs per-worktree port numbers. Browsers scope cookies by host, not port, so sessions don't leak between instances either.
30
+
31
+ **Keep the data root outside the app directory.** Harper writes logs, config, backups and lock files into its data root continuously. Inside the app directory, those writes reach anything watching it (a component with a broad `files` glob, Harper's legacy `WATCH_DIR` watcher, Vite or other dev tooling) and can set off a reload loop. They would also show up in `git status`.
32
+
33
+ ### What the wrapper does
34
+
35
+ `scripts/harper-dev.mjs` is a dependency-free wrapper around `harper dev .`. On every start it:
36
+
37
+ 1. Derives the data root from the checkout: `~/.harper-dev/<project>/<checkout>-<hash>/`. `<project>` is the repository's directory name, so every worktree of one repository shares a project directory. `<checkout>` is the worktree's directory name, and `<hash>` is the first 12 hex characters of the SHA-256 of the checkout's real path. The same checkout always gets the same root, and another worktree gets another.
38
+ 2. Takes a lock for the checkout, `<data root>.lock`, and holds it until it exits. A second start from the same checkout stops with `already running` instead of racing the first through install or startup.
39
+ 3. Deletes inherited environment variables that are set to `''` but defined in `.env`. Harper's `loadEnv` never overrides a variable that is already set, even to an empty string, so the `.env` value would be silently lost.
40
+ 4. Claims a loopback address from `127.0.0.2` to `127.0.0.33`. Claims are lock files in `~/.harper-dev/.loopback/` holding the wrapper's PID, and claims whose owner has died are taken over. It starts from a slot derived from the path, so a worktree usually keeps its URL, and skips any address where one of Harper's ports is already in use.
41
+ 5. Installs a new data root with `harper install` the first time, without prompting. It uses `DEFAULTS_MODE=dev`, `HDB_ADMIN_USERNAME` (default `admin`) and `HDB_ADMIN_PASSWORD` (default random, printed once). The install runs with a throwaway `HOME`. When `~/.harperdb` has no boot file, Harper's install writes one pointing at the new root, which would repoint bare `harper` commands at this instance.
42
+ 6. Turns off the operations API's Unix socket if binding one in the data root isn't permitted (as in an agent sandbox), since Harper fails to start otherwise.
43
+ 7. Writes `.harper-instance` (URL, host, data root, wrapper PID) into the checkout for tooling.
44
+ 8. Runs Harper with every listener bound to the claimed address. A `host:port` value binds only that address; a bare port binds all interfaces. Passing the data root as the `--ROOTPATH` flag, unlike the `ROOTPATH` env var, also overrides a global install's boot file.
45
+
46
+ ```bash
47
+ harper dev . --ROOTPATH=<data root> \
48
+ --HTTP_PORT=127.0.0.N:9926 --OPERATIONSAPI_NETWORK_PORT=127.0.0.N:9925 \
49
+ --MQTT_NETWORK_PORT=127.0.0.N:1883 --MQTT_NETWORK_SECUREPORT=127.0.0.N:8883 \
50
+ --REPLICATION_SECUREPORT=127.0.0.N:9933 --THREADS_DEBUG_HOST=127.0.0.N \
51
+ --OPERATIONSAPI_NETWORK_DOMAINSOCKET=<data root>/operations-server
52
+ ```
53
+
54
+ 9. Forwards Ctrl-C, `SIGTERM` and `SIGHUP` to Harper, then removes `.harper-instance`, its address claim and its lock when Harper exits.
55
+
56
+ It refuses to start when this checkout already has a live instance, or when an orphaned Harper (left by a `kill -9` of the wrapper) still holds the data root.
57
+
58
+ ## Setup
59
+
60
+ The wrapper is tested with Harper 5.3 on macOS (core and Pro) and Linux (core).
61
+
62
+ ### Once per machine (macOS only)
63
+
64
+ Linux routes all of `127.0.0.0/8` to loopback. macOS only configures `127.0.0.1`, so alias the range. The aliases last until the next reboot:
65
+
66
+ ```bash
67
+ for i in $(seq 2 33); do sudo ifconfig lo0 alias 127.0.0.$i up; done
68
+ ```
69
+
70
+ To keep them across reboots, install the launchd daemon that `@harperfast/integration-testing` ships (see "Persisting across reboots" in its README). It aliases the same range, which that package's integration-test pool also uses.
71
+
72
+ ### Once per project
73
+
74
+ 1. Copy `scripts/harper-dev.mjs` from this skill into the app, e.g. as `scripts/harper-dev.mjs`. It is also at `https://raw.githubusercontent.com/HarperFast/skills/main/harper-local-dev/scripts/harper-dev.mjs`.
75
+ 2. Point the dev script at it in `package.json`:
76
+
77
+ ```json
78
+ { "scripts": { "dev": "node scripts/harper-dev.mjs" } }
79
+ ```
80
+
81
+ 3. Add `.harper-instance` to `.gitignore`.
82
+ 4. If the app reads its own public URL from an env var (for OAuth redirects or links in emails), add that variable to `URL_ENV_VARS` at the top of the script. Values the wrapper sets win over `.env`. Every instance also gets `HARPER_DEV_URL`.
83
+ 5. If agents start the server from a sandbox, configure it as described in "Running Under an Agent Sandbox".
84
+
85
+ The wrapper runs `harper` from `PATH`. `npm run` puts `node_modules/.bin` first, so a project-local Harper wins over a global one. Set `HARPER_BIN` to pick a specific executable, such as Harper Pro.
86
+
87
+ ### Per worktree
88
+
89
+ ```bash
90
+ git worktree add ../my-app-feature -b feature
91
+ cd ../my-app-feature
92
+ cp ../my-app/.env . # gitignored files don't come along; copy so each worktree can diverge
93
+ npm install # a real install per worktree
94
+ npm run dev
95
+ ```
96
+
97
+ ```
98
+ Installing a new Harper data root for /Users/you/code/my-app-feature
99
+ ...
100
+ Harper dev instance for /Users/you/code/my-app-feature
101
+ URL: http://127.0.0.7:9926
102
+ Operations: http://127.0.0.7:9925
103
+ Data root: /Users/you/.harper-dev/my-app/my-app-feature-a1b2c3d4e5f6
104
+ Admin: admin / <generated password>
105
+ ```
106
+
107
+ Worktrees can live anywhere, e.g. sibling directories, or `.claude/worktrees/<name>` the way Claude Code creates them (gitignore that directory). Each worktree has its own database, so seed data, users and any first-run bootstrap happen once per worktree.
108
+
109
+ Don't symlink `node_modules` between worktrees: branches' dependencies drift apart. With production defaults (`applications.allowedDirectory: app`), Harper also refuses modules that resolve outside the app with `Can not load module at ... outside of allowed path ...`. Large read-only assets that are gitignored, such as model files, can be symlinked instead of copied.
110
+
111
+ ## Finding the Instance from Tools
112
+
113
+ Ask the wrapper for the URL. It fails when this checkout has no running instance:
114
+
115
+ ```bash
116
+ HARPER_URL=$(node scripts/harper-dev.mjs url) || exit 1
117
+ ```
118
+
119
+ Don't fall back to `http://localhost:9926` when that fails. It reaches a different Harper, or none, and a QA or seeding tool would then work against the wrong database.
120
+
121
+ The wrapper records the running instance in `.harper-instance`:
122
+
123
+ ```json
124
+ {
125
+ "url": "http://127.0.0.7:9926",
126
+ "operationsUrl": "http://127.0.0.7:9925",
127
+ "host": "127.0.0.7",
128
+ "rootPath": "/Users/you/.harper-dev/my-app/my-app-feature-a1b2c3d4e5f6",
129
+ "pid": 12345
130
+ }
131
+ ```
132
+
133
+ Don't trust the file on its own. It outlives a wrapper that crashed, and by then its address may belong to another worktree. `url` only answers when the recorded wrapper is alive and still holds this checkout's lock and the address claim.
134
+
135
+ Run `url` from the worktree you are working in. Subagents and spawned tools may start in the main checkout, so give them the worktree path explicitly.
136
+
137
+ With dev defaults the debugger listens on `<host>:9229`. In `chrome://inspect`, under Configure, add `127.0.0.7:9229`.
138
+
139
+ ## Differences from a Plain `harper dev .`
140
+
141
+ - New data roots get dev defaults: plain HTTP, CORS `*`, one thread, logs on stdout, and `authorizeLocal`, which treats every request from `127.0.0.x` as the super user. To exercise real authentication, run `npm run dev -- --AUTHENTICATION_AUTHORIZELOCAL=false`. Extra arguments are passed to Harper and saved in the instance's config.
142
+ - Harper only marks session cookies `SameSite=None; Secure` for `localhost` and `127.0.0.1`. On `127.0.0.N`, cross-site requests don't carry the session; same-site browsing is unaffected.
143
+ - Services that allow-list origins or redirect URIs, such as OAuth providers, need the `127.0.0.N` origin added.
144
+
145
+ ## Running Under an Agent Sandbox
146
+
147
+ A sandboxed agent needs two permissions to run the wrapper: writing to `~/.harper-dev`, and listening on loopback ports. For Claude Code, add this to `~/.claude/settings.json` to cover every project, or to a project's `.claude/settings.json` to share it with the team. Claude Code merges these arrays across settings files.
148
+
149
+ ```json
150
+ {
151
+ "sandbox": {
152
+ "filesystem": { "allowWrite": ["~/.harper-dev"] },
153
+ "network": { "allowLocalBinding": true }
154
+ }
155
+ }
156
+ ```
157
+
158
+ - **Trust boundary.** Allowing `~/.harper-dev` lets an agent working in one project modify or delete every project's dev data roots, including the data of a running instance. The roots are disposable and source code stays outside them, so this is usually acceptable. For stricter setups, allow only the project's directory and the address claims, and create `~/.harper-dev` once outside the sandbox: the sandbox can create the allowed directories, but not their parent. `my-app` is the project directory shown in the `Data root` line at startup.
159
+
160
+ ```json
161
+ {
162
+ "sandbox": {
163
+ "filesystem": { "allowWrite": ["~/.harper-dev/my-app", "~/.harper-dev/.loopback"] }
164
+ }
165
+ }
166
+ ```
167
+
168
+ - **`allowLocalBinding`** applies to macOS only. Without it, every bind fails with `EPERM` and the wrapper stops with `not permitted to listen on ...`.
169
+ - **Unix sockets** stay blocked by default, so the wrapper turns off the operations API's Unix socket (startup shows `Unix socket off`). Reach the operations API over its HTTP URL instead.
170
+ - **HTTP from the sandbox:** sandboxed commands send HTTP through a proxy that refuses loopback addresses other than `127.0.0.1`, so `curl http://127.0.0.7:9926/...` gets a `403`. Bypass the proxy with `curl --noproxy '*'`. Node's built-in `fetch` ignores the proxy settings by default and connects directly.
171
+ - **Linux:** sandboxed commands run in their own network namespace, so a server started inside the sandbox is unreachable from the browser and from other sessions. Start `npm run dev` outside the sandbox: in your own terminal, through Claude Code's unsandboxed retry, or by listing the command in `sandbox.excludedCommands`.
172
+
173
+ Other agents' sandboxes need the same two permissions: writes to `~/.harper-dev`, and listening on loopback addresses.
174
+
175
+ ## Cleanup
176
+
177
+ ```bash
178
+ git worktree remove ../my-app-feature
179
+ rm -rf ~/.harper-dev/my-app/my-app-feature-a1b2c3d4e5f6 # optional; the path is printed at startup
180
+ ```
181
+
182
+ Data roots outlive their worktrees, and moving or renaming a checkout starts a new one. Delete a root to re-bootstrap that worktree, `~/.harper-dev/my-app` to reset one project, or all of `~/.harper-dev` to reset everything; `du -sh ~/.harper-dev` shows the space they use. None of this touches source code, `.env` files or a global Harper install.
183
+
184
+ ## Troubleshooting
185
+
186
+ | Message or symptom | Cause and fix |
187
+ | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
188
+ | `loopback addresses ... are not configured` | The macOS aliases are missing; they reset on reboot. Run the alias loop above. |
189
+ | `no free loopback address` | Every address in the range is in use: stop some instances, or raise `HARPER_DEV_LOOPBACK_COUNT` and alias more addresses. On Linux, a Harper bound to all interfaces blocks them all. |
190
+ | `not permitted to listen on 127.0.0.N` | A sandbox blocks binding ports. Allow it; see "Running Under an Agent Sandbox". |
191
+ | `cannot write to ~/.harper-dev/...` | A sandbox blocks writes to the data roots. Allow them; see "Running Under an Agent Sandbox". |
192
+ | `403` from the instance, inside a sandbox | The sandbox's HTTP proxy refuses loopback addresses. Use `curl --noproxy '*'`. |
193
+ | `Harper is already running for this checkout` | Another start from this checkout is running or starting. Use the URL it prints, or stop it first. |
194
+ | `a Harper process from an earlier run is still using ...` | The wrapper was killed without stopping Harper. Run the `kill` command it prints. |
195
+ | `Can not load module at ... outside of allowed path ...` | `node_modules`, or a package in it, is a symlink pointing outside the app. Run a real `npm install` in the worktree. |
196
+ | A value from `.env` is ignored | The variable is already set in the environment, and non-empty values from the shell win. Unset it, or use `loadEnv`'s `override: true`. |
197
+ | Startup banner shows `http://127.0.0.7:127.0.0.7:9926/` | Cosmetic: Harper's banner repeats the host when the port is given as `host:port`. The listener is correct. |
198
+ | `domainSocket ... exceeds the 103-byte ... limit` (warning) | The data root's path is too long for macOS, so the operations API's Unix socket is skipped; TCP still works. Use a shorter `HARPER_DEV_HOME`. |
@@ -0,0 +1,20 @@
1
+ # Rules manifest for the harper-local-dev skill.
2
+ #
3
+ # Declarative source of truth for rule taxonomy, sources, and generation mode.
4
+ # See docs/plans-archive/docs-driven-skills.md for the schema.
5
+ #
6
+ # Every rule here is agent-oriented synthesis about the developer environment
7
+ # (worktrees, loopback pools, wrapper scripts) with no single docs source, so
8
+ # all are `mode: synthesized`.
9
+
10
+ rules:
11
+ # ===========================================================================
12
+ # Dev Instances (priority 1 — HIGH)
13
+ # ===========================================================================
14
+
15
+ - rule: running-dev-instances-in-worktrees
16
+ description: Run multiple Harper dev instances in parallel from git worktrees with isolated data roots and loopback addresses.
17
+ category: instances
18
+ priority: 1
19
+ order: 1
20
+ mode: synthesized