@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.
Files changed (27) hide show
  1. package/dist/index.js +719 -4688
  2. package/dist/prompts/system-authoring.md +26 -0
  3. package/dist/templates/adaptation-prompt-init.md +37 -1
  4. package/dist/templates/adaptation-prompt-maintain.md +30 -0
  5. package/dist/templates/har-boilerplate/CLAUDE.agent.md +14 -0
  6. package/dist/templates/har-boilerplate/README.md +18 -1
  7. package/dist/templates/har-boilerplate/agent-cli.sh +7 -1
  8. package/dist/templates/har-boilerplate/agent-slot.sh +70 -7
  9. package/dist/templates/har-boilerplate/harness.env +15 -0
  10. package/dist/templates/har-boilerplate/launch.sh +50 -0
  11. package/dist/templates/har-boilerplate/stages.json +1 -1
  12. package/dist/templates/har-boilerplate/verify.sh +17 -12
  13. package/dist/templates/har-boilerplate-cli/CLAUDE.agent.md +8 -0
  14. package/dist/templates/har-boilerplate-cli/README.md +6 -1
  15. package/dist/templates/har-boilerplate-cli/agent-slot.sh +70 -7
  16. package/dist/templates/har-boilerplate-cli/harness.env +8 -0
  17. package/dist/templates/har-boilerplate-cli/launch.sh +41 -0
  18. package/dist/templates/har-boilerplate-cli/stages.json +1 -1
  19. package/dist/templates/har-boilerplate-cli/verify.sh +14 -7
  20. package/dist/templates/har-boilerplate-ios/CLAUDE.agent.md +8 -0
  21. package/dist/templates/har-boilerplate-ios/README.md +5 -1
  22. package/dist/templates/har-boilerplate-ios/agent-slot.sh +70 -7
  23. package/dist/templates/har-boilerplate-ios/harness.env +4 -0
  24. package/dist/templates/har-boilerplate-ios/launch.sh +41 -0
  25. package/dist/templates/har-boilerplate-ios/stages.json +1 -1
  26. package/dist/templates/har-boilerplate-ios/verify.sh +15 -7
  27. 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/<project>-agent-<id>` by default (`HARNESS_USE_WORKTREE=true`). Agents should commit from that worktree, not the main checkout.
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, SLOT_PURPOSE
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
- # the legacy fixed path (pre-registry sessions). Empty output when none exists.
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 locations
206
- # (repo root, pre-registry fixed worktree path).
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)
@@ -20,7 +20,7 @@
20
20
  {
21
21
  "path": ".env.agent.{agentId}",
22
22
  "kind": "file",
23
- "description": "Generated per-agent environment file"
23
+ "description": "Generated per-agent environment file in the session work dir"
24
24
  }
25
25
  ]
26
26
  },
@@ -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=$(date +%s%3N 2>/dev/null || echo "0")
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=$(date +%s%3N 2>/dev/null || echo "0")
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=$(date +%s%3N 2>/dev/null || echo "0")
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=$(echo "$output" | head -50 | \
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=$(date +%s%3N 2>/dev/null || echo "0")
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=$(date +%s%3N 2>/dev/null || echo "0")
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=$(echo "$output" | head -50 | \
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=$(date +%s%3N 2>/dev/null || echo "0")
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, build, **browser-e2e** when `stages/browser-e2e.sh` exists |
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