@osfactory/har 0.1.1 → 0.3.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.
- package/dist/index.js +719 -4688
- package/dist/prompts/system-authoring.md +26 -0
- package/dist/templates/adaptation-prompt-init.md +37 -1
- package/dist/templates/adaptation-prompt-maintain.md +30 -0
- package/dist/templates/har-boilerplate/CLAUDE.agent.md +14 -0
- package/dist/templates/har-boilerplate/README.md +18 -1
- package/dist/templates/har-boilerplate/agent-cli.sh +7 -1
- package/dist/templates/har-boilerplate/agent-slot.sh +70 -7
- package/dist/templates/har-boilerplate/harness.env +15 -0
- package/dist/templates/har-boilerplate/launch.sh +50 -0
- package/dist/templates/har-boilerplate/stages.json +1 -1
- package/dist/templates/har-boilerplate/verify.sh +17 -12
- package/dist/templates/har-boilerplate-cli/CLAUDE.agent.md +8 -0
- package/dist/templates/har-boilerplate-cli/README.md +6 -1
- package/dist/templates/har-boilerplate-cli/agent-slot.sh +70 -7
- package/dist/templates/har-boilerplate-cli/harness.env +8 -0
- package/dist/templates/har-boilerplate-cli/launch.sh +41 -0
- package/dist/templates/har-boilerplate-cli/stages.json +1 -1
- package/dist/templates/har-boilerplate-cli/verify.sh +14 -7
- package/dist/templates/har-boilerplate-ios/CLAUDE.agent.md +8 -0
- package/dist/templates/har-boilerplate-ios/README.md +5 -1
- package/dist/templates/har-boilerplate-ios/agent-slot.sh +70 -7
- package/dist/templates/har-boilerplate-ios/harness.env +4 -0
- package/dist/templates/har-boilerplate-ios/launch.sh +41 -0
- package/dist/templates/har-boilerplate-ios/stages.json +1 -1
- package/dist/templates/har-boilerplate-ios/verify.sh +15 -7
- package/package.json +7 -7
|
@@ -58,6 +58,29 @@ PM2 processes for the primary application only, matching how it runs in dev.
|
|
|
58
58
|
### `.har/verify.sh`
|
|
59
59
|
Real typecheck, lint, test, and health check commands — replace all TODOs.
|
|
60
60
|
|
|
61
|
+
## Readiness vs liveness (required)
|
|
62
|
+
Do not treat a passing health check as adaptation complete. A usable agent
|
|
63
|
+
environment has layered readiness:
|
|
64
|
+
|
|
65
|
+
1. **Infra ready** — shared services and template data stores exist.
|
|
66
|
+
2. **Slot data ready** — every per-slot data store is created or cloned.
|
|
67
|
+
3. **Process ready** — primary app processes are online and health passes.
|
|
68
|
+
4. **Agent usable** — documented credentials/workflows work, required default
|
|
69
|
+
data exists, and UI/API smoke is not blocked by asset/dev-server issues.
|
|
70
|
+
|
|
71
|
+
Compare the harness against the repository's full local-dev setup. If you skip
|
|
72
|
+
heavy steps such as full seed, optional services, asset compilation modes, or
|
|
73
|
+
background daemons, add the minimum substitute in `.har/` scripts or document
|
|
74
|
+
why no substitute is needed. If the app has multiple databases/stores, provision
|
|
75
|
+
all per-slot state. If launch generates config, validate the nested keys the app
|
|
76
|
+
actually reads. Put agent-usability checks in full verify, a project-owned
|
|
77
|
+
readiness script, or documented smoke URLs. Health alone is not sufficient for
|
|
78
|
+
UI/auth apps.
|
|
79
|
+
|
|
80
|
+
Ensure `launch.sh` writes the slot registry before slow or fragile steps, and
|
|
81
|
+
`verify.sh` resolves env/work dir through `agent-slot.sh` so partial launches are
|
|
82
|
+
recoverable.
|
|
83
|
+
|
|
61
84
|
### `.har/CLAUDE.agent.md`
|
|
62
85
|
Detailed agent instructions: commands, credentials, architecture, definition of done.
|
|
63
86
|
|
|
@@ -101,4 +124,7 @@ Set slot limits in `.har/stages.json` (`agentSlots`) and `.har/harness.env` (`HA
|
|
|
101
124
|
- unused harness files deleted (`deleteHarnessFile`), e.g. `attach.sh` when unused
|
|
102
125
|
- `.har/README.md` file table matches the files that actually exist
|
|
103
126
|
- `CLAUDE.agent.md` shows only real URLs/ports and commands that run
|
|
127
|
+
- skipped full-dev setup has a minimal bootstrap or clearly documented limitation
|
|
128
|
+
- all per-slot data stores are provisioned, not only the primary database
|
|
129
|
+
- `CLAUDE.agent.md` defines what "agent usable" means, including credentials or smoke checks when applicable
|
|
104
130
|
8. **Call finishAuthoring** when complete
|
|
@@ -44,6 +44,39 @@ PM2 processes for the primary application only, matching how it runs in dev. Ski
|
|
|
44
44
|
### `.har/verify.sh`
|
|
45
45
|
Real typecheck, lint, test, and health check commands — replace all TODOs.
|
|
46
46
|
|
|
47
|
+
### Readiness vs liveness (required)
|
|
48
|
+
Do not treat a passing health check as adaptation complete. Before finishing,
|
|
49
|
+
make the harness explicit about the layers that apply to this repository:
|
|
50
|
+
|
|
51
|
+
1. **Infra ready** — shared services are running and template data stores exist.
|
|
52
|
+
2. **Slot data ready** — every per-slot data store is created or cloned, not only
|
|
53
|
+
the primary database.
|
|
54
|
+
3. **Process ready** — the primary app processes are online and
|
|
55
|
+
`HARNESS_HEALTH_CHECK_PATH` passes.
|
|
56
|
+
4. **Agent usable** — a real workflow an agent needs is possible: documented
|
|
57
|
+
credentials work, a tenant/org/project exists when the app requires one, the
|
|
58
|
+
UI is not blocked by dev-server overlays, or an authenticated API smoke works.
|
|
59
|
+
|
|
60
|
+
Compare the harness against the project's full local-dev setup. If the harness
|
|
61
|
+
intentionally skips slow or heavy steps (full seed, optional services, asset
|
|
62
|
+
mode, background daemons), add the minimum substitute directly in `.har/`
|
|
63
|
+
scripts or document why no substitute is needed. In particular:
|
|
64
|
+
|
|
65
|
+
- If `HARNESS_DB_SEED_CMD` is empty or schema-only, add an idempotent minimal
|
|
66
|
+
bootstrap step for required tenants/users/settings, or document why schema-only
|
|
67
|
+
is enough.
|
|
68
|
+
- If the app has multiple databases, schemas, queues, object stores, search
|
|
69
|
+
indexes, or other per-slot state, provision all of them in `setup-infra.sh`
|
|
70
|
+
and `launch.sh`.
|
|
71
|
+
- If launch generates config, validate the nested keys the app actually reads
|
|
72
|
+
against upstream examples/defaults, not only top-level keys.
|
|
73
|
+
- If the dev server can block agents with overlays or noisy HMR failures,
|
|
74
|
+
choose and document an agent-friendly asset mode.
|
|
75
|
+
- Put Layer 3 checks in `verify --full`, a project-owned readiness script, or
|
|
76
|
+
documented smoke URLs. Health alone is not sufficient for UI/auth apps.
|
|
77
|
+
- Ensure `launch.sh` writes the slot registry before slow or fragile steps, and
|
|
78
|
+
`verify.sh` resolves env/work dir through `agent-slot.sh`.
|
|
79
|
+
|
|
47
80
|
### Optional Playwright stage
|
|
48
81
|
If the user ran `har env add-stage playwright` (or `@playwright/test` is in package.json):
|
|
49
82
|
|
|
@@ -65,7 +98,7 @@ Agents run in parallel on configurable slot ids. Ports: `BASE + (AGENT_ID × 10)
|
|
|
65
98
|
Set slot limits in `.har/stages.json` (`agentSlots`) and `.har/harness.env` (`HARNESS_AGENT_SLOT_MIN` / `HARNESS_AGENT_SLOT_MAX`) based on machine capacity.
|
|
66
99
|
|
|
67
100
|
### Git worktree
|
|
68
|
-
`launch.sh` creates an isolated worktree at `~/worktrees/<
|
|
101
|
+
`launch.sh` creates an isolated session worktree at `~/worktrees/<base>-<sha4>-har-agent-<id>-<rand4>` by default (`HARNESS_USE_WORKTREE=true`) and records it in `.har/slots/agent-<id>.json`. Agents should commit from that worktree, not the main checkout.
|
|
69
102
|
|
|
70
103
|
## Step 3 — Update repo-root `AGENT.md`
|
|
71
104
|
|
|
@@ -112,6 +145,9 @@ The boilerplate ships more than any single repository needs. Strip it down to st
|
|
|
112
145
|
- [ ] Unused harness files are deleted (e.g. `attach.sh` when tmux isn't part of the workflow; CLI profile: `ecosystem.agent.template.cjs`, `env.template`)
|
|
113
146
|
- [ ] `.har/README.md` file table lists exactly the files that exist — no more, no less
|
|
114
147
|
- [ ] `.har/CLAUDE.agent.md` shows only real URLs/ports/credentials (e.g. drop the Frontend row for an API-only project) and project commands that actually run
|
|
148
|
+
- [ ] If full seed/setup is skipped, `.har/` provides a minimal bootstrap or clearly documents why none is needed
|
|
149
|
+
- [ ] All per-slot data stores are provisioned, not just the primary database
|
|
150
|
+
- [ ] `.har/CLAUDE.agent.md` defines what "agent usable" means for this repo: login/API smoke, credentials, required default data, and known skipped dev-setup steps
|
|
115
151
|
|
|
116
152
|
## Rules
|
|
117
153
|
|
|
@@ -28,6 +28,36 @@ Align commands and instructions with the current stack.
|
|
|
28
28
|
### `.har/env.template`, `setup-infra.sh`, `docker-compose.agent.yml`
|
|
29
29
|
Update only if infra changed.
|
|
30
30
|
|
|
31
|
+
### Readiness vs liveness regression check
|
|
32
|
+
Do not treat a passing health check as proof that the harness is still usable.
|
|
33
|
+
When maintaining an existing harness, re-check the layers that apply:
|
|
34
|
+
|
|
35
|
+
1. **Infra ready** — shared services and template data stores still match the app.
|
|
36
|
+
2. **Slot data ready** — every per-slot data store is created or cloned, not only
|
|
37
|
+
the primary database.
|
|
38
|
+
3. **Process ready** — app processes are online and `HARNESS_HEALTH_CHECK_PATH`
|
|
39
|
+
passes.
|
|
40
|
+
4. **Agent usable** — documented credentials/workflows still work, required
|
|
41
|
+
default data exists, UI/API smoke is not blocked by asset/dev-server issues,
|
|
42
|
+
and any skipped full-dev setup has a minimal substitute or clear limitation.
|
|
43
|
+
|
|
44
|
+
Look specifically for drift introduced since the last adaptation:
|
|
45
|
+
|
|
46
|
+
- A seed command was removed or made schema-only without a minimal bootstrap.
|
|
47
|
+
- A new database, schema, queue, object store, search index, or other per-slot
|
|
48
|
+
dependency was added but launch only provisions the original primary store.
|
|
49
|
+
- Config generation writes plausible top-level keys while the app reads nested
|
|
50
|
+
defaults from another file.
|
|
51
|
+
- The dev server mode is fine for humans but blocks browser automation or agents
|
|
52
|
+
with overlays/noisy HMR.
|
|
53
|
+
- `verify.sh` became health-only and no longer checks the key workflow that makes
|
|
54
|
+
the slot usable.
|
|
55
|
+
- `launch.sh` writes the slot registry only after fragile late steps; partial
|
|
56
|
+
launches must remain discoverable by verify/status/teardown.
|
|
57
|
+
|
|
58
|
+
Update `.har/CLAUDE.agent.md` with skipped setup steps, substitutes, credentials,
|
|
59
|
+
and the repo-specific definition of "agent usable."
|
|
60
|
+
|
|
31
61
|
### HAR platform upgrades checklist
|
|
32
62
|
|
|
33
63
|
When upgrading `@osfactory/har` or adopting new harness standards:
|
|
@@ -22,9 +22,23 @@ This slot runs **only the primary application** (`HARNESS_PRIMARY_APP`). Externa
|
|
|
22
22
|
./.har/agent-cli.sh ${AGENT_ID} health
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
## Readiness
|
|
26
|
+
|
|
27
|
+
Adapt this section for the repository. A passing health check means the process
|
|
28
|
+
is alive; it does not automatically mean an agent can use the app.
|
|
29
|
+
|
|
30
|
+
- **Health**: `./.har/agent-cli.sh ${AGENT_ID} health`
|
|
31
|
+
- **Agent-usable smoke**: document the login/API/UI workflow agents should try,
|
|
32
|
+
or wire it into `HARNESS_READINESS_CMD` / full verify.
|
|
33
|
+
- **Credentials/default data**: document any test users, tenants, projects, or
|
|
34
|
+
settings created by the harness.
|
|
35
|
+
- **Skipped full-dev setup**: document anything intentionally omitted from the
|
|
36
|
+
upstream developer setup and the minimal substitute in `.har/`.
|
|
37
|
+
|
|
25
38
|
## Definition of done
|
|
26
39
|
|
|
27
40
|
- [ ] Full verification returns `"status": "pass"` (`har env verify ${AGENT_ID} --full`, MCP `har_run_verification` with `full: true`, or `./.har/verify.sh ${AGENT_ID} --full`)
|
|
41
|
+
- [ ] The slot is agent-usable for this repo's documented smoke workflow, not only health-check green
|
|
28
42
|
- [ ] When `stages/browser-e2e.sh` exists, full verify includes Playwright — adapt specs under `tests/` for UI changes
|
|
29
43
|
- [ ] New behavior has automated test coverage (unit and/or browser as appropriate)
|
|
30
44
|
- [ ] Changes committed **in the session worktree** with a clear message
|
|
@@ -61,10 +61,27 @@ Read **`stages.json`** for registered stages and **`verificationStages`** for th
|
|
|
61
61
|
| Mode | Command | Typical steps |
|
|
62
62
|
|------|---------|---------------|
|
|
63
63
|
| Quick | `har env verify <id>` or `verify.sh <id>` | Project checks in `verify.sh` (stops early on failure) |
|
|
64
|
-
| Full | `har env verify <id> --full` or `verify.sh <id> --full` | Quick steps + lint + **`browser-e2e`** when `.har/stages/browser-e2e.sh` exists |
|
|
64
|
+
| Full | `har env verify <id> --full` or `verify.sh <id> --full` | Quick steps + lint + optional readiness smoke + **`browser-e2e`** when `.har/stages/browser-e2e.sh` exists |
|
|
65
65
|
|
|
66
66
|
Install Playwright stage: `har env add-stage playwright` (optional). UI changes should add or update specs under `tests/`.
|
|
67
67
|
|
|
68
|
+
## Readiness layers
|
|
69
|
+
|
|
70
|
+
Do not treat a health endpoint as the whole definition of success. Adapt this
|
|
71
|
+
section for the repository:
|
|
72
|
+
|
|
73
|
+
| Layer | What it means | Where to encode it |
|
|
74
|
+
|-------|---------------|--------------------|
|
|
75
|
+
| Infra ready | Shared services and template data stores exist | `setup-infra.sh`, `docker-compose.agent.yml` |
|
|
76
|
+
| Slot data ready | Every per-slot data store is created/cloned | `launch.sh` |
|
|
77
|
+
| Process ready | Primary app is running and health passes | `launch.sh`, `verify.sh` |
|
|
78
|
+
| Agent usable | Login/API/UI smoke works with documented data | `HARNESS_READINESS_CMD`, browser-e2e, `CLAUDE.agent.md` |
|
|
79
|
+
|
|
80
|
+
If full local-dev setup is too heavy to run in the harness, document the skipped
|
|
81
|
+
steps and add the minimum substitute directly in `.har/` scripts (for example an
|
|
82
|
+
idempotent bootstrap for required users/tenants/settings). Health alone is not
|
|
83
|
+
enough for UI/auth apps.
|
|
84
|
+
|
|
68
85
|
## Run history
|
|
69
86
|
|
|
70
87
|
| Entry point | Writes `.har/runs/`? |
|
|
@@ -56,7 +56,13 @@ process.stdin.on('end', () => {
|
|
|
56
56
|
fi
|
|
57
57
|
|
|
58
58
|
if [ "$PM2_FOUND" = true ]; then
|
|
59
|
-
|
|
59
|
+
REG_FILE="$(slot_registry_file "$AGENT_ID")"
|
|
60
|
+
if [ ! -f "$REG_FILE" ]; then
|
|
61
|
+
echo "Warning: slot registry missing at $REG_FILE — verify/teardown may not resolve this slot."
|
|
62
|
+
fi
|
|
63
|
+
if [ -z "$ENV_FILE" ]; then
|
|
64
|
+
echo "Warning: .env.agent.${AGENT_ID} could not be resolved — relaunch with --replace when ready."
|
|
65
|
+
fi
|
|
60
66
|
elif [ -n "$ENV_FILE" ] && [ -f "$ENV_FILE" ]; then
|
|
61
67
|
echo "Agent ${AGENT_ID}: active (no PM2 processes)"
|
|
62
68
|
# shellcheck source=/dev/null
|
|
@@ -50,7 +50,8 @@ try {
|
|
|
50
50
|
# Writes the registry entry from SLOT_* variables:
|
|
51
51
|
# required: SLOT_AGENT_ID, SLOT_MODE (worktree|root), SLOT_WORK_DIR
|
|
52
52
|
# optional: SLOT_SUFFIX, SLOT_WORKTREE_PATH, SLOT_BRANCH, SLOT_BASE_BRANCH,
|
|
53
|
-
# SLOT_BASE_COMMIT, SLOT_PORTS_JSON, SLOT_PREVIEW_URLS_JSON,
|
|
53
|
+
# SLOT_BASE_COMMIT, SLOT_PORTS_JSON, SLOT_PREVIEW_URLS_JSON,
|
|
54
|
+
# SLOT_PURPOSE, SLOT_STATUS, SLOT_LAST_ERROR
|
|
54
55
|
write_slot_registry() {
|
|
55
56
|
local file
|
|
56
57
|
file="$(slot_registry_file "$SLOT_AGENT_ID")"
|
|
@@ -65,7 +66,7 @@ const entry = {
|
|
|
65
66
|
mode: e.SLOT_MODE,
|
|
66
67
|
workDir: e.SLOT_WORK_DIR,
|
|
67
68
|
createdAt: new Date().toISOString(),
|
|
68
|
-
status: "active",
|
|
69
|
+
status: e.SLOT_STATUS || "active",
|
|
69
70
|
};
|
|
70
71
|
if (e.SLOT_SUFFIX) entry.suffix = e.SLOT_SUFFIX;
|
|
71
72
|
if (e.SLOT_WORKTREE_PATH) entry.worktreePath = e.SLOT_WORKTREE_PATH;
|
|
@@ -73,6 +74,7 @@ if (e.SLOT_BRANCH) entry.branch = e.SLOT_BRANCH;
|
|
|
73
74
|
if (e.SLOT_BASE_BRANCH) entry.baseBranch = e.SLOT_BASE_BRANCH;
|
|
74
75
|
if (e.SLOT_BASE_COMMIT) entry.baseCommit = e.SLOT_BASE_COMMIT;
|
|
75
76
|
if (e.SLOT_PURPOSE) entry.purpose = e.SLOT_PURPOSE;
|
|
77
|
+
if (e.SLOT_LAST_ERROR) entry.lastError = e.SLOT_LAST_ERROR;
|
|
76
78
|
for (const [key, env] of [["ports", "SLOT_PORTS_JSON"], ["previewUrls", "SLOT_PREVIEW_URLS_JSON"]]) {
|
|
77
79
|
if (e[env]) try { entry[key] = JSON.parse(e[env]); } catch {}
|
|
78
80
|
}
|
|
@@ -115,13 +117,15 @@ slot_dirty_summary() {
|
|
|
115
117
|
# Print a warning before replacing an occupied slot (stdout — visible to agents).
|
|
116
118
|
print_slot_replace_warning() {
|
|
117
119
|
local agent_id="$1"
|
|
118
|
-
local reg wt branch work_dir purpose created dirty_summary head
|
|
120
|
+
local reg wt branch work_dir purpose created status last_error dirty_summary head
|
|
119
121
|
reg="$(slot_registry_file "$agent_id")"
|
|
120
122
|
wt="$(existing_slot_worktree "$agent_id")"
|
|
121
123
|
branch="$(read_slot_field "$reg" branch || true)"
|
|
122
124
|
work_dir="$(read_slot_field "$reg" workDir || true)"
|
|
123
125
|
purpose="$(read_slot_field "$reg" purpose || true)"
|
|
124
126
|
created="$(read_slot_field "$reg" createdAt || true)"
|
|
127
|
+
status="$(read_slot_field "$reg" status || true)"
|
|
128
|
+
last_error="$(read_slot_field "$reg" lastError || true)"
|
|
125
129
|
dirty_summary="$(slot_dirty_summary "$wt")"
|
|
126
130
|
if [ -n "$wt" ] && [ -d "$wt" ]; then
|
|
127
131
|
head="$(git -C "$wt" rev-parse --short HEAD 2>/dev/null || true)"
|
|
@@ -133,6 +137,8 @@ print_slot_replace_warning() {
|
|
|
133
137
|
[ -n "$wt" ] && echo " Worktree: ${wt}" >&2
|
|
134
138
|
[ -n "$work_dir" ] && echo " Work dir: ${work_dir}" >&2
|
|
135
139
|
[ -n "$branch" ] && echo " Branch: ${branch}${head:+ @ ${head}}" >&2
|
|
140
|
+
[ -n "$status" ] && echo " Status: ${status}" >&2
|
|
141
|
+
[ -n "$last_error" ] && echo " Error: ${last_error}" >&2
|
|
136
142
|
[ -n "$created" ] && echo " Since: ${created}" >&2
|
|
137
143
|
echo " Git: ${dirty_summary}" >&2
|
|
138
144
|
echo "" >&2
|
|
@@ -182,11 +188,30 @@ require_slot_replace_confirm() {
|
|
|
182
188
|
exit 2
|
|
183
189
|
}
|
|
184
190
|
|
|
191
|
+
git_common_dir() {
|
|
192
|
+
local cwd="$1"
|
|
193
|
+
local out
|
|
194
|
+
out="$(git -C "$cwd" rev-parse --git-common-dir 2>/dev/null)" || return 1
|
|
195
|
+
case "$out" in
|
|
196
|
+
/*) echo "$out" ;;
|
|
197
|
+
*) (cd "$cwd" && cd "$out" && pwd) ;;
|
|
198
|
+
esac
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
same_git_checkout() {
|
|
202
|
+
local left right
|
|
203
|
+
left="$(git_common_dir "$1" || true)"
|
|
204
|
+
right="$(git_common_dir "$2" || true)"
|
|
205
|
+
[ -n "$left" ] && [ -n "$right" ] && [ "$left" = "$right" ]
|
|
206
|
+
}
|
|
207
|
+
|
|
185
208
|
# Echo the previous session's worktree path for a slot: registry first, then
|
|
186
|
-
#
|
|
209
|
+
# legacy and randomized session worktree fallbacks. Empty output when none exists.
|
|
187
210
|
existing_slot_worktree() {
|
|
188
211
|
local agent_id="$1"
|
|
189
|
-
local reg path
|
|
212
|
+
local reg path candidate repo_root rel_prefix
|
|
213
|
+
repo_root="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
214
|
+
rel_prefix="$(git -C "$repo_root" rev-parse --show-prefix 2>/dev/null || true)"
|
|
190
215
|
reg="$(slot_registry_file "$agent_id")"
|
|
191
216
|
if [ -f "$reg" ]; then
|
|
192
217
|
path="$(read_slot_field "$reg" worktreePath || true)"
|
|
@@ -198,12 +223,20 @@ existing_slot_worktree() {
|
|
|
198
223
|
path="$HOME/worktrees/${HARNESS_PROJECT_NAME}-agent-${agent_id}"
|
|
199
224
|
if [ -d "$path" ]; then
|
|
200
225
|
echo "$path"
|
|
226
|
+
return 0
|
|
201
227
|
fi
|
|
228
|
+
for candidate in "$HOME"/worktrees/*-har-agent-"${agent_id}"-*; do
|
|
229
|
+
[ -d "$candidate" ] || continue
|
|
230
|
+
same_git_checkout "$repo_root" "$candidate" || continue
|
|
231
|
+
[ -f "$candidate/${rel_prefix}.env.agent.${agent_id}" ] || continue
|
|
232
|
+
echo "$candidate"
|
|
233
|
+
return 0
|
|
234
|
+
done
|
|
202
235
|
return 0
|
|
203
236
|
}
|
|
204
237
|
|
|
205
|
-
# Resolve .env.agent.<id> — registry work dir first, then legacy
|
|
206
|
-
#
|
|
238
|
+
# Resolve .env.agent.<id> — registry work dir first, then legacy and
|
|
239
|
+
# randomized session worktree fallbacks.
|
|
207
240
|
resolve_agent_env_file() {
|
|
208
241
|
local agent_id="$1"
|
|
209
242
|
local repo_root="$2"
|
|
@@ -229,6 +262,15 @@ resolve_agent_env_file() {
|
|
|
229
262
|
return 0
|
|
230
263
|
fi
|
|
231
264
|
done
|
|
265
|
+
local candidate_dir
|
|
266
|
+
for candidate in "$HOME"/worktrees/*-har-agent-"${agent_id}"-*/${rel_prefix}.env.agent."${agent_id}"; do
|
|
267
|
+
if [ -f "$candidate" ]; then
|
|
268
|
+
candidate_dir="$(cd "$(dirname "$candidate")" && pwd)"
|
|
269
|
+
same_git_checkout "$repo_root" "$candidate_dir" || continue
|
|
270
|
+
echo "$candidate"
|
|
271
|
+
return 0
|
|
272
|
+
fi
|
|
273
|
+
done
|
|
232
274
|
return 1
|
|
233
275
|
}
|
|
234
276
|
|
|
@@ -259,6 +301,16 @@ resolve_agent_work_dir() {
|
|
|
259
301
|
echo "$work_dir"
|
|
260
302
|
}
|
|
261
303
|
|
|
304
|
+
# Portable millisecond clock (GNU date %N is unavailable on macOS/BSD).
|
|
305
|
+
now_ms() {
|
|
306
|
+
node -e 'process.stdout.write(String(Date.now()))' 2>/dev/null || echo 0
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
# JSON-escape step output; truncate to 50 lines in node (avoids SIGPIPE under pipefail).
|
|
310
|
+
escape_step_output() {
|
|
311
|
+
printf '%s' "$1" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const s=d.trim().split('\n').slice(0,50).join('\n');process.stdout.write(JSON.stringify(s))})" 2>/dev/null || echo '""'
|
|
312
|
+
}
|
|
313
|
+
|
|
262
314
|
# Run browser-e2e on verify --full when the Playwright stage template is installed.
|
|
263
315
|
run_browser_e2e_if_present() {
|
|
264
316
|
local script_dir="$1"
|
|
@@ -268,3 +320,14 @@ run_browser_e2e_if_present() {
|
|
|
268
320
|
"$e2e" "$agent_id"
|
|
269
321
|
fi
|
|
270
322
|
}
|
|
323
|
+
|
|
324
|
+
# Optional project-owned "agent usable" smoke beyond health.
|
|
325
|
+
run_readiness_if_configured() {
|
|
326
|
+
local agent_id="$1"
|
|
327
|
+
if [ -z "${HARNESS_READINESS_CMD:-}" ]; then
|
|
328
|
+
echo "No HARNESS_READINESS_CMD configured; skipping readiness smoke."
|
|
329
|
+
return 0
|
|
330
|
+
fi
|
|
331
|
+
local cmd="${HARNESS_READINESS_CMD//\{agentId\}/$agent_id}"
|
|
332
|
+
eval "$cmd"
|
|
333
|
+
}
|
|
@@ -48,3 +48,18 @@ har_pg() {
|
|
|
48
48
|
# Database setup commands (run once against template DB in setup-infra.sh)
|
|
49
49
|
export HARNESS_DB_MIGRATE_CMD="echo 'TODO: set migrate command'"
|
|
50
50
|
export HARNESS_DB_SEED_CMD="echo 'TODO: set seed command'"
|
|
51
|
+
|
|
52
|
+
# Optional readiness/data hooks for larger apps. Enable only when this project
|
|
53
|
+
# needs them, and keep the scripts idempotent.
|
|
54
|
+
#
|
|
55
|
+
# Multiple per-slot data stores can be represented as logical_name:template_db
|
|
56
|
+
# pairs and handled directly in setup-infra.sh / launch.sh.
|
|
57
|
+
# export HARNESS_TEMPLATE_DBS="main:template___PROJECT_NAME__"
|
|
58
|
+
#
|
|
59
|
+
# Use a minimal bootstrap when full seed is too slow/heavy but the app still
|
|
60
|
+
# needs default tenants, users, settings, feature flags, or similar data.
|
|
61
|
+
# export HARNESS_DB_MINIMAL_BOOTSTRAP_CMD="./.har/bootstrap-data.sh"
|
|
62
|
+
#
|
|
63
|
+
# Use readiness checks for "agent usable" smoke beyond a health endpoint:
|
|
64
|
+
# login/API auth, representative UI page, required data, secondary stores.
|
|
65
|
+
# export HARNESS_READINESS_CMD="./.har/readiness.sh"
|
|
@@ -147,6 +147,55 @@ REPO_ROOT="$WORK_DIR" \
|
|
|
147
147
|
envsubst '${AGENT_ID} ${API_PORT} ${FE_PORT} ${DEBUG_PORT} ${DB_PORT} ${MINIO_PORT} ${BROWSER_PORT} ${REPO_ROOT}' \
|
|
148
148
|
< "$SCRIPT_DIR/env.template" > "$ENV_FILE"
|
|
149
149
|
|
|
150
|
+
REGISTRY_WRITTEN=false
|
|
151
|
+
mark_slot_failed() {
|
|
152
|
+
local exit_code="$?"
|
|
153
|
+
if [ "$exit_code" != "0" ] && [ "$REGISTRY_WRITTEN" = true ]; then
|
|
154
|
+
log "Launch failed after creating the session. Recording failed slot state..."
|
|
155
|
+
set +e
|
|
156
|
+
SLOT_AGENT_ID="$AGENT_ID" \
|
|
157
|
+
SLOT_MODE="$([ "$USE_WORKTREE" = true ] && echo worktree || echo root)" \
|
|
158
|
+
SLOT_WORK_DIR="$WORK_DIR" \
|
|
159
|
+
SLOT_SUFFIX="${SUFFIX:-}" \
|
|
160
|
+
SLOT_WORKTREE_PATH="${WORKTREE_DIR:-}" \
|
|
161
|
+
SLOT_BRANCH="${BRANCH:-}" \
|
|
162
|
+
SLOT_BASE_BRANCH="${BASE_BRANCH:-}" \
|
|
163
|
+
SLOT_BASE_COMMIT="${BASE_COMMIT:-}" \
|
|
164
|
+
SLOT_PURPOSE="${PURPOSE}" \
|
|
165
|
+
SLOT_PORTS_JSON="{\"frontend\":${FE_PORT},\"api\":${API_PORT},\"debug\":${DEBUG_PORT}}" \
|
|
166
|
+
SLOT_PREVIEW_URLS_JSON="{\"frontend\":\"http://localhost:${FE_PORT}\",\"api\":\"http://localhost:${API_PORT}\"}" \
|
|
167
|
+
SLOT_STATUS="failed" \
|
|
168
|
+
SLOT_LAST_ERROR="launch.sh exited with code ${exit_code}" \
|
|
169
|
+
write_slot_registry
|
|
170
|
+
log " Work dir: ${WORK_DIR}"
|
|
171
|
+
log " Env file: ${ENV_FILE}"
|
|
172
|
+
log " Recovery: fix the failure, then relaunch with --replace when ready."
|
|
173
|
+
fi
|
|
174
|
+
}
|
|
175
|
+
trap mark_slot_failed EXIT
|
|
176
|
+
|
|
177
|
+
# Record the session before slow or fragile setup so verify/status/teardown can
|
|
178
|
+
# recover partial launches that already created a worktree and env file.
|
|
179
|
+
SLOT_AGENT_ID="$AGENT_ID" \
|
|
180
|
+
SLOT_MODE="$([ "$USE_WORKTREE" = true ] && echo worktree || echo root)" \
|
|
181
|
+
SLOT_WORK_DIR="$WORK_DIR" \
|
|
182
|
+
SLOT_SUFFIX="${SUFFIX:-}" \
|
|
183
|
+
SLOT_WORKTREE_PATH="${WORKTREE_DIR:-}" \
|
|
184
|
+
SLOT_BRANCH="${BRANCH:-}" \
|
|
185
|
+
SLOT_BASE_BRANCH="${BASE_BRANCH:-}" \
|
|
186
|
+
SLOT_BASE_COMMIT="${BASE_COMMIT:-}" \
|
|
187
|
+
SLOT_PURPOSE="${PURPOSE}" \
|
|
188
|
+
SLOT_PORTS_JSON="{\"frontend\":${FE_PORT},\"api\":${API_PORT},\"debug\":${DEBUG_PORT}}" \
|
|
189
|
+
SLOT_PREVIEW_URLS_JSON="{\"frontend\":\"http://localhost:${FE_PORT}\",\"api\":\"http://localhost:${API_PORT}\"}" \
|
|
190
|
+
SLOT_STATUS="starting" \
|
|
191
|
+
write_slot_registry
|
|
192
|
+
REGISTRY_WRITTEN=true
|
|
193
|
+
|
|
194
|
+
if [ -n "${HARNESS_DB_MINIMAL_BOOTSTRAP_CMD:-}" ]; then
|
|
195
|
+
log "Running minimal data bootstrap..."
|
|
196
|
+
(cd "$WORK_DIR" && set -a && . "$ENV_FILE" && set +a && eval "$HARNESS_DB_MINIMAL_BOOTSTRAP_CMD")
|
|
197
|
+
fi
|
|
198
|
+
|
|
150
199
|
# Generate PM2 ecosystem config
|
|
151
200
|
ECOSYSTEM_FILE="$WORK_DIR/ecosystem.agent.${AGENT_ID}.config.cjs"
|
|
152
201
|
log "Generating $ECOSYSTEM_FILE..."
|
|
@@ -199,6 +248,7 @@ SLOT_BASE_COMMIT="${BASE_COMMIT:-}" \
|
|
|
199
248
|
SLOT_PURPOSE="${PURPOSE}" \
|
|
200
249
|
SLOT_PORTS_JSON="{\"frontend\":${FE_PORT},\"api\":${API_PORT},\"debug\":${DEBUG_PORT}}" \
|
|
201
250
|
SLOT_PREVIEW_URLS_JSON="{\"frontend\":\"http://localhost:${FE_PORT}\",\"api\":\"http://localhost:${API_PORT}\"}" \
|
|
251
|
+
SLOT_STATUS="active" \
|
|
202
252
|
write_slot_registry
|
|
203
253
|
|
|
204
254
|
# Launch Claude Code in tmux (optional)
|
|
@@ -38,9 +38,17 @@ WORK_DIR="$(resolve_agent_work_dir "$ENV_FILE")"
|
|
|
38
38
|
API_PORT="${API_PORT:-$(( HARNESS_API_BASE_PORT + AGENT_ID * 10 ))}"
|
|
39
39
|
|
|
40
40
|
echo "==> Verifying agent ${AGENT_ID} (work dir: ${WORK_DIR})..." >&2
|
|
41
|
+
REG_FILE="$(slot_registry_file "$AGENT_ID")"
|
|
42
|
+
echo " Work dir: ${WORK_DIR}" >&2
|
|
43
|
+
echo " Env file: ${ENV_FILE}" >&2
|
|
44
|
+
if [ -f "$REG_FILE" ]; then
|
|
45
|
+
echo " Registry: ${REG_FILE}" >&2
|
|
46
|
+
else
|
|
47
|
+
echo " Registry: missing (${REG_FILE})" >&2
|
|
48
|
+
fi
|
|
41
49
|
|
|
42
50
|
OVERALL_PASS=true
|
|
43
|
-
START_TOTAL=$(
|
|
51
|
+
START_TOTAL=$(now_ms)
|
|
44
52
|
RESULTS_JSON="[]"
|
|
45
53
|
|
|
46
54
|
run_step() {
|
|
@@ -49,14 +57,14 @@ run_step() {
|
|
|
49
57
|
local start end elapsed exit_code output
|
|
50
58
|
|
|
51
59
|
printf " → %-40s" "$name..." >&2
|
|
52
|
-
start=$(
|
|
60
|
+
start=$(now_ms)
|
|
53
61
|
|
|
54
62
|
set +e
|
|
55
63
|
output=$(cd "$WORK_DIR" && set -a && . "$ENV_FILE" && set +a && eval "$cmd" 2>&1)
|
|
56
64
|
exit_code=$?
|
|
57
65
|
set -e
|
|
58
66
|
|
|
59
|
-
end=$(
|
|
67
|
+
end=$(now_ms)
|
|
60
68
|
elapsed=$(( end - start ))
|
|
61
69
|
|
|
62
70
|
local pass_bool step_output_escaped
|
|
@@ -70,9 +78,7 @@ run_step() {
|
|
|
70
78
|
OVERALL_PASS=false
|
|
71
79
|
fi
|
|
72
80
|
|
|
73
|
-
step_output_escaped=$(
|
|
74
|
-
node -e "let d=''; process.stdin.on('data',c=>d+=c); process.stdin.on('end',()=>process.stdout.write(JSON.stringify(d.trim())))" \
|
|
75
|
-
2>/dev/null || echo '""')
|
|
81
|
+
step_output_escaped=$(escape_step_output "$output")
|
|
76
82
|
|
|
77
83
|
RESULTS_JSON=$(echo "$RESULTS_JSON" | node -e "
|
|
78
84
|
const fs = require('fs');
|
|
@@ -92,14 +98,14 @@ run_http_step() {
|
|
|
92
98
|
local start end elapsed exit_code output
|
|
93
99
|
|
|
94
100
|
printf " → %-40s" "$name..." >&2
|
|
95
|
-
start=$(
|
|
101
|
+
start=$(now_ms)
|
|
96
102
|
|
|
97
103
|
set +e
|
|
98
104
|
output=$(curl -sf "$url" 2>&1)
|
|
99
105
|
exit_code=$?
|
|
100
106
|
set -e
|
|
101
107
|
|
|
102
|
-
end=$(
|
|
108
|
+
end=$(now_ms)
|
|
103
109
|
elapsed=$(( end - start ))
|
|
104
110
|
|
|
105
111
|
local pass_bool step_output_escaped
|
|
@@ -112,9 +118,7 @@ run_http_step() {
|
|
|
112
118
|
OVERALL_PASS=false
|
|
113
119
|
fi
|
|
114
120
|
|
|
115
|
-
step_output_escaped=$(
|
|
116
|
-
node -e "let d=''; process.stdin.on('data',c=>d+=c); process.stdin.on('end',()=>process.stdout.write(JSON.stringify(d.trim())))" \
|
|
117
|
-
2>/dev/null || echo '""')
|
|
121
|
+
step_output_escaped=$(escape_step_output "$output")
|
|
118
122
|
|
|
119
123
|
RESULTS_JSON=$(echo "$RESULTS_JSON" | node -e "
|
|
120
124
|
const fs = require('fs');
|
|
@@ -138,12 +142,13 @@ run_http_step "api-health" "http://localhost:${API_PORT}${HARNESS_HEALTH_CHECK_P
|
|
|
138
142
|
|
|
139
143
|
if [ -n "$FULL" ]; then
|
|
140
144
|
run_step "lint" "echo 'TODO: npm run lint'" || true
|
|
145
|
+
run_step "readiness" "run_readiness_if_configured \"$AGENT_ID\"" || true
|
|
141
146
|
run_step "browser-e2e" "run_browser_e2e_if_present \"$SCRIPT_DIR\" \"$AGENT_ID\"" || true
|
|
142
147
|
fi
|
|
143
148
|
|
|
144
149
|
# ── Output results ────────────────────────────────────────────────────────────
|
|
145
150
|
|
|
146
|
-
END_TOTAL=$(
|
|
151
|
+
END_TOTAL=$(now_ms)
|
|
147
152
|
TOTAL_MS=$(( END_TOTAL - START_TOTAL ))
|
|
148
153
|
|
|
149
154
|
node -e "
|
|
@@ -15,9 +15,17 @@
|
|
|
15
15
|
./.har/agent-cli.sh ${AGENT_ID} status
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
## Readiness
|
|
19
|
+
|
|
20
|
+
Adapt this section for the repository. For pure CLI/library repos, full verify
|
|
21
|
+
may be enough. If the project needs services, auth, seeded data, or a sample
|
|
22
|
+
workflow, document the required credentials/default data and wire a smoke into
|
|
23
|
+
`HARNESS_READINESS_CMD` or full verify.
|
|
24
|
+
|
|
18
25
|
## Definition of done
|
|
19
26
|
|
|
20
27
|
- [ ] Full verification returns `"status": "pass"` (`har env verify ${AGENT_ID} --full`, MCP `har_run_verification` with `full: true`, or `./.har/verify.sh ${AGENT_ID} --full`)
|
|
28
|
+
- [ ] The slot is agent-usable for this repo's documented smoke workflow when runtime services are involved
|
|
21
29
|
- [ ] When `stages/browser-e2e.sh` exists, full verify includes Playwright — adapt specs under `tests/` for UI changes
|
|
22
30
|
- [ ] New behavior has automated test coverage
|
|
23
31
|
- [ ] Changes committed **in the session worktree** with a clear message
|
|
@@ -59,7 +59,12 @@ Read **`stages.json`** and **`verificationStages`**. Optional: `har env add-stag
|
|
|
59
59
|
| Mode | Command | Typical steps |
|
|
60
60
|
|------|---------|---------------|
|
|
61
61
|
| Quick | `har env verify <id>` or `verify.sh <id>` | typecheck, unit tests |
|
|
62
|
-
| Full | `har env verify <id> --full` or `verify.sh <id> --full` | + lint,
|
|
62
|
+
| Full | `har env verify <id> --full` or `verify.sh <id> --full` | + lint, optional readiness smoke, **browser-e2e** when `stages/browser-e2e.sh` exists |
|
|
63
|
+
|
|
64
|
+
For repos that need runtime services, distinguish health from usability. If the
|
|
65
|
+
harness skips slow local-dev setup, document the skipped steps and add a minimal
|
|
66
|
+
bootstrap/readiness check when agents need default data, credentials, or an
|
|
67
|
+
authenticated workflow.
|
|
63
68
|
|
|
64
69
|
Use `har env launch 1 --no-worktree` or `./.har/launch.sh 1 --no-worktree` only when working in the repo root.
|
|
65
70
|
|