@osfactory/har 0.37.0 → 0.38.1
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 +450 -493
- package/dist/templates/adaptation-prompt-init.md +1 -1
- package/dist/templates/agent-skills/har-wt.md +2 -2
- package/dist/templates/cursor-rule.mdc.template +2 -3
- package/dist/templates/har-boilerplate/CLAUDE.agent.md +1 -1
- package/dist/templates/har-boilerplate/README.md +4 -4
- package/dist/templates/har-boilerplate/agent-cli.sh +1 -1
- package/dist/templates/har-boilerplate/agent-slot.sh +24 -66
- package/dist/templates/har-boilerplate/launch.sh +6 -15
- package/dist/templates/har-boilerplate/preflight.sh +3 -12
- package/dist/templates/har-boilerplate-cli/CLAUDE.agent.md +1 -1
- package/dist/templates/har-boilerplate-cli/README.md +3 -3
- package/dist/templates/har-boilerplate-cli/agent-slot.sh +24 -64
- package/dist/templates/har-boilerplate-cli/launch.sh +6 -14
- package/dist/templates/har-boilerplate-cli/preflight.sh +3 -12
- package/dist/templates/har-boilerplate-ios/CLAUDE.agent.md +1 -1
- package/dist/templates/har-boilerplate-ios/README.md +3 -3
- package/dist/templates/har-boilerplate-ios/agent-slot.sh +15 -48
- package/dist/templates/har-boilerplate-ios/launch.sh +9 -11
- package/package.json +1 -1
|
@@ -153,7 +153,7 @@ Document and configure ports in `.har/harness.env` and `.har/README.md`. Use the
|
|
|
153
153
|
| Mailpit | `HARNESS_MAILPIT_*_PORT_DEFAULT` | Scan configured ranges |
|
|
154
154
|
| Headless browser | `HARNESS_BROWSER_PORT_DEFAULT` | Scan configured ranges |
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
Onboarding may set an initial `agentSlots.max` in `.har/stages.json`. After you know the real stack cost, **re-tune** that limit for machine capacity (lighter stack → you can raise; heavy Docker/DB → lower). `har env maintain --finalize` syncs legacy `HARNESS_AGENT_SLOT_*` exports in `harness.env`.
|
|
157
157
|
|
|
158
158
|
**Port / infra checklist:**
|
|
159
159
|
|
|
@@ -11,7 +11,7 @@ har env status # or ./.har/agent-cli.sh 1 status
|
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
- One slot ≈ one task. If slot 1 is occupied by unrelated work, use slot 2+ (limit: `agentSlots` in `.har/stages.json`).
|
|
14
|
-
-
|
|
14
|
+
- Occupied slots always block a new launch: run `har env teardown <id>` (or `complete <id>`) first, then launch again.
|
|
15
15
|
|
|
16
16
|
## 2. Launch — BEFORE editing any file
|
|
17
17
|
|
|
@@ -19,7 +19,7 @@ har env status # or ./.har/agent-cli.sh 1 status
|
|
|
19
19
|
har env launch <id> # or ./.har/launch.sh <id>
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
Launch creates a fresh session worktree from HEAD and prints its **work dir** (also recorded in `.har/slots/agent-<id>.json`, path like `~/worktrees/<base>-<sha4>-har-agent-<id>-<rand4>`). If a previous launch failed partway, retry with `--resume` instead of
|
|
22
|
+
Launch creates a fresh session worktree from HEAD and prints its **work dir** (also recorded in `.har/slots/agent-<id>.json`, path like `~/worktrees/<base>-<sha4>-har-agent-<id>-<rand4>`). If a previous launch failed partway, retry with `--resume` instead of starting fresh.
|
|
23
23
|
|
|
24
24
|
If the task already has a stable issue or ticket ID, bind it without changing the
|
|
25
25
|
methodology that produced it:
|
|
@@ -15,20 +15,19 @@ work around it with ad-hoc commands.
|
|
|
15
15
|
|
|
16
16
|
## Before making changes
|
|
17
17
|
|
|
18
|
-
1. **On the main checkout, switch to the intended base** (usually `main`) — every launch creates a worktree from that HEAD.
|
|
18
|
+
1. **On the main checkout, switch to the intended base** (usually `main`) — every launch creates a worktree from that HEAD.
|
|
19
19
|
2. **Launch your slot FIRST — before editing any file.** Use `har_launch_environment` with `agentId: 1` (MCP) or `har env launch 1`. Launch always creates a **fresh session worktree** and returns its **work dir**. If the task binds to a durable external work item (a GitHub issue, a Linear ticket, a task from the user's tracker), pass `workUnitId` + `title` on launch (`har_launch_environment` `workUnitId`/`title`, or CLI `--work-id`/`--work-title`) so Mission Control attributes usage and events to that work item instead of leaving them session-only.
|
|
20
20
|
3. **Make ALL file edits under the returned work dir — never in the main checkout.** The work dir looks like `~/worktrees/<base-branch>-<sha4>-har-agent-<id>-<rand4>` (recorded in `.har/slots/agent-<id>.json`). Edits there hot-reload in the running slot; use `har env restart <id>` or `./.har/agent-cli.sh <id> restart` if a change doesn't take.
|
|
21
21
|
4. Read [AGENT.md](./AGENT.md) at the repo root
|
|
22
22
|
5. Read [.har/README.md](./.har/README.md) and [.har/stages.json](./.har/stages.json)
|
|
23
23
|
6. After launch, read [.har/CLAUDE.agent.md](./.har/CLAUDE.agent.md) for slot URLs and definition of done
|
|
24
24
|
|
|
25
|
-
When a slot is occupied:
|
|
25
|
+
When a slot is occupied: it always blocks a new launch. Run `complete` / `teardown` on that slot id first, then `launch` again. Occupied slots do not choose the base branch either way — switch the main checkout to your intended base before launching. Failed/starting launches can be resumed instead: `--resume` / `recover`.
|
|
26
26
|
|
|
27
27
|
### Slot safety (read before launch)
|
|
28
28
|
|
|
29
29
|
- **One slot ≈ one task** — do not share slot 1 across unrelated chats; use slot 2+ for parallel work.
|
|
30
30
|
- **Call `har_get_status` / `har env status`** before launch when a slot may be occupied.
|
|
31
|
-
- **Never** set `force`/`confirmReplace` without the user explicitly approving replacement.
|
|
32
31
|
- **Commit early and often** in the session worktree — teardown keeps the branch, not uncommitted work.
|
|
33
32
|
- **Gitignored paths are ephemeral** (`state/`, `runs/`, local clones) — they are lost when the worktree is removed.
|
|
34
33
|
- With telemetry on (default, including prompts), Mission Control derives session purpose from the first captured user prompt.
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
| **Database** | `agent_${AGENT_ID}` on `localhost:${DB_PORT}` (when `db` is in `HARNESS_INFRA_SERVICES`) |
|
|
13
13
|
| **Work dir** | Fresh session worktree per launch — see the launch output or `.har/slots/agent-${AGENT_ID}.json` |
|
|
14
14
|
|
|
15
|
-
**Never edit the main checkout** — launch FIRST, then make ALL file edits under the work dir from the launch output. Edits there hot-reload in the running slot; use `./.har/agent-cli.sh ${AGENT_ID} restart` if a change doesn't take.
|
|
15
|
+
**Never edit the main checkout** — launch FIRST, then make ALL file edits under the work dir from the launch output. Edits there hot-reload in the running slot; use `./.har/agent-cli.sh ${AGENT_ID} restart` if a change doesn't take. An occupied slot always blocks a new launch — run `./.har/teardown.sh ${AGENT_ID}` (or `complete`) first, then launch again.
|
|
16
16
|
|
|
17
17
|
This slot runs **only the primary application** (`HARNESS_PRIMARY_APP`). External dependencies and any supporting services run once, shared by all slots — `setup-infra.sh` manages them; never start them yourself.
|
|
18
18
|
|
|
@@ -187,13 +187,13 @@ Switch that checkout to your intended base before launch. The session is recorde
|
|
|
187
187
|
the work dir through it. Make ALL file edits under the work dir printed by launch,
|
|
188
188
|
never in the main checkout.
|
|
189
189
|
|
|
190
|
-
-
|
|
191
|
-
|
|
192
|
-
|
|
190
|
+
- Occupied slots always block a new launch: `har env complete <id>` (or `teardown <id>`),
|
|
191
|
+
then `har env launch <id>`. A new launch never chooses `main` for you — switch the
|
|
192
|
+
main checkout to your intended base first.
|
|
193
193
|
- `teardown` removes the worktree but **keeps the session branch** so you can push it
|
|
194
194
|
or open a PR (`--delete-branch` to drop it).
|
|
195
195
|
- If launch fails after creating a worktree/env file, the registry records `status: failed`.
|
|
196
|
-
Resume
|
|
196
|
+
Resume it instead of starting fresh: `har env launch <id> --resume` or `har env recover <id>`.
|
|
197
197
|
- `har env complete <id>` finishes a session: full verify (recorded as a validation),
|
|
198
198
|
then teardown — branch kept.
|
|
199
199
|
- `--no-worktree` runs the slot from the repo root instead (single-agent mode).
|
|
@@ -103,7 +103,7 @@ process.stdin.on('end', () => {
|
|
|
103
103
|
exit 1
|
|
104
104
|
fi
|
|
105
105
|
if [ -z "$ENV_FILE" ]; then
|
|
106
|
-
echo "ERROR: .env.agent.${AGENT_ID} could not be resolved —
|
|
106
|
+
echo "ERROR: .env.agent.${AGENT_ID} could not be resolved — teardown this slot, then relaunch." >&2
|
|
107
107
|
exit 1
|
|
108
108
|
fi
|
|
109
109
|
elif [ -n "$ENV_FILE" ] && [ -f "$ENV_FILE" ]; then
|
|
@@ -245,7 +245,7 @@ har_resume_session_assignments() {
|
|
|
245
245
|
local status
|
|
246
246
|
status="$(slot_session_status "$agent_id")"
|
|
247
247
|
echo "echo \"ERROR: slot ${agent_id} is not resumable (status=${status:-none}; need failed or starting).\" >&2" >&2
|
|
248
|
-
echo "echo \"
|
|
248
|
+
echo "echo \" Free the slot first: har env teardown ${agent_id} (or complete ${agent_id}), then har env launch ${agent_id}.\" >&2"
|
|
249
249
|
echo 'exit 2'
|
|
250
250
|
return 1
|
|
251
251
|
fi
|
|
@@ -326,36 +326,27 @@ har_regenerate_agent_env_file() {
|
|
|
326
326
|
fi
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
-
# har_launch_preflight <agent_id>
|
|
330
|
-
# Exit 0 when ready; 2 when occupied
|
|
329
|
+
# har_launch_preflight <agent_id> [resume]
|
|
330
|
+
# Exit 0 when ready; 2 when occupied (free the slot first) or not resumable; 1 on machine blockers.
|
|
331
331
|
# Sets FE_PORT/API_PORT/DEBUG_PORT when this harness uses PM2.
|
|
332
332
|
har_launch_preflight() {
|
|
333
333
|
local agent_id="$1"
|
|
334
|
-
local
|
|
335
|
-
local replace="${3:-false}"
|
|
336
|
-
local resume="${4:-false}"
|
|
334
|
+
local resume="${2:-false}"
|
|
337
335
|
|
|
338
336
|
if [ "$resume" = true ]; then
|
|
339
337
|
if ! slot_is_resumable "$agent_id"; then
|
|
340
338
|
local status
|
|
341
339
|
status="$(slot_session_status "$agent_id")"
|
|
342
340
|
echo "ERROR: slot ${agent_id} is not resumable (status=${status:-none}; need failed or starting)." >&2
|
|
343
|
-
echo "
|
|
341
|
+
echo " Free the slot first: har env teardown ${agent_id} (or complete ${agent_id}), then har env launch ${agent_id}." >&2
|
|
344
342
|
return 2
|
|
345
343
|
fi
|
|
346
344
|
echo "==> [agent-${agent_id}] Resuming partial launch (worktree and deps preserved)..." >&2
|
|
347
345
|
elif slot_is_occupied "$agent_id"; then
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
fi
|
|
353
|
-
local wt
|
|
354
|
-
wt="$(existing_slot_worktree "$agent_id")"
|
|
355
|
-
if [ -n "$wt" ] && slot_worktree_dirty "$wt" && [ "$force" != true ]; then
|
|
356
|
-
echo "ERROR: dirty worktree requires --force after explicit user approval." >&2
|
|
357
|
-
return 2
|
|
358
|
-
fi
|
|
346
|
+
print_slot_occupied_warning "$agent_id"
|
|
347
|
+
echo "ERROR: slot ${agent_id} is occupied." >&2
|
|
348
|
+
echo " Free it first: har env teardown ${agent_id} (or complete ${agent_id}), then har env launch ${agent_id}." >&2
|
|
349
|
+
return 2
|
|
359
350
|
fi
|
|
360
351
|
|
|
361
352
|
if har_harness_uses_pm2; then
|
|
@@ -517,8 +508,8 @@ slot_dirty_summary() {
|
|
|
517
508
|
echo "dirty (${count} changed)"
|
|
518
509
|
}
|
|
519
510
|
|
|
520
|
-
# Print
|
|
521
|
-
|
|
511
|
+
# Print details about an occupied slot (stdout — visible to agents).
|
|
512
|
+
print_slot_occupied_warning() {
|
|
522
513
|
local agent_id="$1"
|
|
523
514
|
local reg wt branch work_dir created status last_error dirty_summary head
|
|
524
515
|
reg="$(slot_registry_file "$agent_id")"
|
|
@@ -534,7 +525,7 @@ print_slot_replace_warning() {
|
|
|
534
525
|
fi
|
|
535
526
|
|
|
536
527
|
echo "" >&2
|
|
537
|
-
echo "⚠ Slot ${agent_id} is already in use
|
|
528
|
+
echo "⚠ Slot ${agent_id} is already in use." >&2
|
|
538
529
|
[ -n "$wt" ] && echo " Worktree: ${wt}" >&2
|
|
539
530
|
[ -n "$work_dir" ] && echo " Work dir: ${work_dir}" >&2
|
|
540
531
|
[ -n "$branch" ] && echo " Branch: ${branch}${head:+ @ ${head}}" >&2
|
|
@@ -550,54 +541,21 @@ print_slot_replace_warning() {
|
|
|
550
541
|
base_sha="$(git -C "$base_root" rev-parse --short HEAD 2>/dev/null || true)"
|
|
551
542
|
echo " New session will be based on: ${base_branch}${base_sha:+ @ ${base_sha}}" >&2
|
|
552
543
|
echo " (HEAD of ${base_root})" >&2
|
|
553
|
-
echo "
|
|
544
|
+
echo " Switch that checkout to your intended base before launching a new session." >&2
|
|
554
545
|
echo "" >&2
|
|
555
546
|
fi
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
# Require explicit confirmation before replacing an occupied slot.
|
|
563
|
-
# $1 agent_id $2 force(true/false) $3 replace(true/false)
|
|
564
|
-
# Exits 1 when replacement must not proceed.
|
|
565
|
-
require_slot_replace_confirm() {
|
|
566
|
-
local agent_id="$1"
|
|
567
|
-
local force="$2"
|
|
568
|
-
local replace="$3"
|
|
569
|
-
local wt
|
|
570
|
-
|
|
571
|
-
slot_is_occupied "$agent_id" || return 0
|
|
572
|
-
|
|
573
|
-
print_slot_replace_warning "$agent_id"
|
|
574
|
-
wt="$(existing_slot_worktree "$agent_id")"
|
|
575
|
-
|
|
576
|
-
if [ -n "$wt" ] && slot_worktree_dirty "$wt" && [ "$force" != true ]; then
|
|
577
|
-
echo "ERROR: previous session for slot ${agent_id} has uncommitted changes." >&2
|
|
578
|
-
echo " Commit them in the worktree (branch is kept on teardown), or pass --force to discard." >&2
|
|
579
|
-
echo " --force destroys uncommitted work — only use after explicit user approval." >&2
|
|
580
|
-
exit 1
|
|
581
|
-
fi
|
|
582
|
-
|
|
583
|
-
if [ "$replace" = true ] || [ "${HAR_CONFIRM_REPLACE:-}" = "1" ]; then
|
|
584
|
-
return 0
|
|
547
|
+
if [ "$dirty_summary" != "clean" ] && [ "$dirty_summary" != "unknown" ]; then
|
|
548
|
+
echo " Commit or discard changes in the worktree, then teardown/complete the slot:" >&2
|
|
549
|
+
else
|
|
550
|
+
echo " Free the slot, then launch again:" >&2
|
|
585
551
|
fi
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
echo "Aborted — slot ${agent_id} left unchanged." >&2
|
|
594
|
-
exit 2
|
|
595
|
-
fi
|
|
596
|
-
|
|
597
|
-
echo "ERROR: slot ${agent_id} is occupied." >&2
|
|
598
|
-
echo " Pass --replace (or set HAR_CONFIRM_REPLACE=1) after reviewing the warning above." >&2
|
|
599
|
-
echo " If the worktree is dirty, also pass --force (only after explicit user approval)." >&2
|
|
600
|
-
exit 2
|
|
552
|
+
echo " har env complete ${agent_id} # or: har env teardown ${agent_id}" >&2
|
|
553
|
+
echo " har env launch ${agent_id}" >&2
|
|
554
|
+
echo "" >&2
|
|
555
|
+
echo " complete/teardown remove the worktree. The session branch is kept only if" >&2
|
|
556
|
+
echo " you committed. Gitignored paths (state/, runs/, local clones, .env.local)" >&2
|
|
557
|
+
echo " are NOT preserved." >&2
|
|
558
|
+
echo "" >&2
|
|
601
559
|
}
|
|
602
560
|
|
|
603
561
|
git_common_dir() {
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
# Launches a single agent instance with isolated ports, database, and PM2 processes.
|
|
3
3
|
# Every launch starts a FRESH session: any previous session for the slot is torn
|
|
4
4
|
# down (its branch is kept) and a new suffixed worktree is created from HEAD.
|
|
5
|
+
# Occupied slots always block: run ./.har/teardown.sh (or complete it) first, then relaunch.
|
|
5
6
|
#
|
|
6
|
-
# Usage: ./.har/launch.sh <agent-id> [--no-worktree] [--claude] [--
|
|
7
|
+
# Usage: ./.har/launch.sh <agent-id> [--no-worktree] [--claude] [--resume]
|
|
7
8
|
set -euo pipefail
|
|
8
9
|
|
|
9
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
@@ -17,8 +18,6 @@ source "$SCRIPT_DIR/agent-slot.sh"
|
|
|
17
18
|
AGENT_ID="${1:-}"
|
|
18
19
|
USE_WORKTREE="${HARNESS_USE_WORKTREE:-true}"
|
|
19
20
|
USE_CLAUDE=false
|
|
20
|
-
FORCE=false
|
|
21
|
-
REPLACE=false
|
|
22
21
|
RESUME=false
|
|
23
22
|
WORK_UNIT_ID="${HAR_WORK_UNIT_ID:-}"
|
|
24
23
|
ATTEMPT_ID="${HAR_ATTEMPT_ID:-}"
|
|
@@ -28,8 +27,6 @@ for arg in "$@"; do
|
|
|
28
27
|
--no-worktree) USE_WORKTREE=false ;;
|
|
29
28
|
--worktree) USE_WORKTREE=true ;;
|
|
30
29
|
--claude) USE_CLAUDE=true ;;
|
|
31
|
-
--replace) REPLACE=true ;;
|
|
32
|
-
--force) FORCE=true ;;
|
|
33
30
|
--resume) RESUME=true ;;
|
|
34
31
|
--work-id=*) WORK_UNIT_ID="${arg#*=}" ;;
|
|
35
32
|
--attempt-id=*) ATTEMPT_ID="${arg#*=}" ;;
|
|
@@ -44,7 +41,7 @@ export SLOT_ATTEMPT_ID="$ATTEMPT_ID"
|
|
|
44
41
|
|
|
45
42
|
if [[ -z "$AGENT_ID" ]]; then
|
|
46
43
|
har_load_agent_slot_limits
|
|
47
|
-
echo "Usage: $0 <agent-id> [--no-worktree] [--claude] [--
|
|
44
|
+
echo "Usage: $0 <agent-id> [--no-worktree] [--claude] [--resume]" >&2
|
|
48
45
|
echo " agent-id must be between ${HARNESS_AGENT_SLOT_MIN} and ${HARNESS_AGENT_SLOT_MAX}" >&2
|
|
49
46
|
exit 1
|
|
50
47
|
fi
|
|
@@ -63,7 +60,7 @@ ENV_FILE=""
|
|
|
63
60
|
REGISTRY_WRITTEN=false
|
|
64
61
|
|
|
65
62
|
if [ "$RESUME" = true ]; then
|
|
66
|
-
har_launch_preflight "$AGENT_ID"
|
|
63
|
+
har_launch_preflight "$AGENT_ID" true || exit $?
|
|
67
64
|
eval "$(har_resume_session_assignments "$AGENT_ID")"
|
|
68
65
|
REGISTRY_WRITTEN=true
|
|
69
66
|
mark_slot_failed() {
|
|
@@ -92,14 +89,8 @@ if [ "$RESUME" = true ]; then
|
|
|
92
89
|
trap mark_slot_failed EXIT
|
|
93
90
|
else
|
|
94
91
|
# Preflight before worktree/install — ports, foreign PM2, Docker, occupied slot.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
# Replace any previous session for this slot — requires explicit confirmation.
|
|
98
|
-
if slot_is_occupied "$AGENT_ID"; then
|
|
99
|
-
require_slot_replace_confirm "$AGENT_ID" "$FORCE" "$REPLACE"
|
|
100
|
-
log "Replacing previous session for slot ${AGENT_ID}..."
|
|
101
|
-
"$SCRIPT_DIR/teardown.sh" "$AGENT_ID" >&2
|
|
102
|
-
fi
|
|
92
|
+
# Occupied slots always block here: teardown/complete first, then relaunch.
|
|
93
|
+
har_launch_preflight "$AGENT_ID" || exit $?
|
|
103
94
|
fi
|
|
104
95
|
|
|
105
96
|
if har_harness_uses_pm2; then
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Launch readiness gate — run before launch.sh or standalone.
|
|
3
|
-
# Usage: ./.har/preflight.sh <agent-id>
|
|
3
|
+
# Usage: ./.har/preflight.sh <agent-id>
|
|
4
4
|
# JSON: har env preflight <id> --json
|
|
5
5
|
set -euo pipefail
|
|
6
6
|
|
|
@@ -13,24 +13,15 @@ source "$SCRIPT_DIR/harness.env"
|
|
|
13
13
|
source "$SCRIPT_DIR/agent-slot.sh"
|
|
14
14
|
|
|
15
15
|
AGENT_ID="${1:-}"
|
|
16
|
-
FORCE=false
|
|
17
|
-
REPLACE=false
|
|
18
|
-
|
|
19
|
-
for arg in "$@"; do
|
|
20
|
-
case "$arg" in
|
|
21
|
-
--replace) REPLACE=true ;;
|
|
22
|
-
--force) FORCE=true ;;
|
|
23
|
-
esac
|
|
24
|
-
done
|
|
25
16
|
|
|
26
17
|
if [[ -z "$AGENT_ID" ]]; then
|
|
27
|
-
echo "Usage: $0 <agent-id>
|
|
18
|
+
echo "Usage: $0 <agent-id>" >&2
|
|
28
19
|
exit 1
|
|
29
20
|
fi
|
|
30
21
|
|
|
31
22
|
validate_agent_id "$AGENT_ID"
|
|
32
23
|
|
|
33
|
-
if har_launch_preflight "$AGENT_ID"
|
|
24
|
+
if har_launch_preflight "$AGENT_ID"; then
|
|
34
25
|
echo "Slot ${AGENT_ID}: ready to launch."
|
|
35
26
|
if har_harness_uses_pm2; then
|
|
36
27
|
echo " Ports: frontend=${FE_PORT} api=${API_PORT} debug=${DEBUG_PORT}"
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
| **Agent ID** | ${AGENT_ID} |
|
|
10
10
|
| **Work dir** | Fresh session worktree per launch — see the launch output or `.har/slots/agent-${AGENT_ID}.json` |
|
|
11
11
|
|
|
12
|
-
**Never edit the main checkout** — launch FIRST, then make ALL file edits under the work dir from the launch output.
|
|
12
|
+
**Never edit the main checkout** — launch FIRST, then make ALL file edits under the work dir from the launch output. An occupied slot always blocks a new launch — run `har env teardown <id>` (or `complete <id>`) first, then launch again.
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
15
|
./.har/agent-cli.sh ${AGENT_ID} status
|
|
@@ -145,9 +145,9 @@ Switch that checkout to your intended base before launch. The session is recorde
|
|
|
145
145
|
the work dir through it. Make ALL file edits under the work dir printed by launch,
|
|
146
146
|
never in the main checkout.
|
|
147
147
|
|
|
148
|
-
-
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
- Occupied slots always block a new launch: `har env complete <id>` (or `teardown <id>`),
|
|
149
|
+
then `har env launch <id>`. A new launch never chooses `main` for you — switch the
|
|
150
|
+
main checkout to your intended base first.
|
|
151
151
|
- `teardown` removes the worktree but **keeps the session branch** so you can push it
|
|
152
152
|
or open a PR (`--delete-branch` to drop it).
|
|
153
153
|
- If launch fails after creating a worktree/env file, resume with
|
|
@@ -223,7 +223,7 @@ har_resume_session_assignments() {
|
|
|
223
223
|
local status
|
|
224
224
|
status="$(slot_session_status "$agent_id")"
|
|
225
225
|
echo "echo \"ERROR: slot ${agent_id} is not resumable (status=${status:-none}; need failed or starting).\" >&2" >&2
|
|
226
|
-
echo "echo \"
|
|
226
|
+
echo "echo \" Free the slot first: har env teardown ${agent_id} (or complete ${agent_id}), then har env launch ${agent_id}.\" >&2"
|
|
227
227
|
echo 'exit 2'
|
|
228
228
|
return 1
|
|
229
229
|
fi
|
|
@@ -310,33 +310,26 @@ EOF
|
|
|
310
310
|
fi
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
+
# har_launch_preflight <agent_id> [resume]
|
|
314
|
+
# Exit 0 when ready; 2 when occupied (free the slot first) or not resumable; 1 on machine blockers.
|
|
313
315
|
har_launch_preflight() {
|
|
314
316
|
local agent_id="$1"
|
|
315
|
-
local
|
|
316
|
-
local replace="${3:-false}"
|
|
317
|
-
local resume="${4:-false}"
|
|
317
|
+
local resume="${2:-false}"
|
|
318
318
|
|
|
319
319
|
if [ "$resume" = true ]; then
|
|
320
320
|
if ! slot_is_resumable "$agent_id"; then
|
|
321
321
|
local status
|
|
322
322
|
status="$(slot_session_status "$agent_id")"
|
|
323
323
|
echo "ERROR: slot ${agent_id} is not resumable (status=${status:-none}; need failed or starting)." >&2
|
|
324
|
-
echo "
|
|
324
|
+
echo " Free the slot first: har env teardown ${agent_id} (or complete ${agent_id}), then har env launch ${agent_id}." >&2
|
|
325
325
|
return 2
|
|
326
326
|
fi
|
|
327
327
|
echo "==> [agent-${agent_id}] Resuming partial launch (worktree and deps preserved)..." >&2
|
|
328
328
|
elif slot_is_occupied "$agent_id"; then
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
fi
|
|
334
|
-
local wt
|
|
335
|
-
wt="$(existing_slot_worktree "$agent_id")"
|
|
336
|
-
if [ -n "$wt" ] && slot_worktree_dirty "$wt" && [ "$force" != true ]; then
|
|
337
|
-
echo "ERROR: dirty worktree requires --force after explicit user approval." >&2
|
|
338
|
-
return 2
|
|
339
|
-
fi
|
|
329
|
+
print_slot_occupied_warning "$agent_id"
|
|
330
|
+
echo "ERROR: slot ${agent_id} is occupied." >&2
|
|
331
|
+
echo " Free it first: har env teardown ${agent_id} (or complete ${agent_id}), then har env launch ${agent_id}." >&2
|
|
332
|
+
return 2
|
|
340
333
|
fi
|
|
341
334
|
|
|
342
335
|
if har_harness_uses_pm2; then
|
|
@@ -497,8 +490,8 @@ slot_dirty_summary() {
|
|
|
497
490
|
echo "dirty (${count} changed)"
|
|
498
491
|
}
|
|
499
492
|
|
|
500
|
-
# Print
|
|
501
|
-
|
|
493
|
+
# Print details about an occupied slot (stdout — visible to agents).
|
|
494
|
+
print_slot_occupied_warning() {
|
|
502
495
|
local agent_id="$1"
|
|
503
496
|
local reg wt branch work_dir created status last_error dirty_summary head
|
|
504
497
|
reg="$(slot_registry_file "$agent_id")"
|
|
@@ -514,7 +507,7 @@ print_slot_replace_warning() {
|
|
|
514
507
|
fi
|
|
515
508
|
|
|
516
509
|
echo "" >&2
|
|
517
|
-
echo "⚠ Slot ${agent_id} is already in use
|
|
510
|
+
echo "⚠ Slot ${agent_id} is already in use." >&2
|
|
518
511
|
[ -n "$wt" ] && echo " Worktree: ${wt}" >&2
|
|
519
512
|
[ -n "$work_dir" ] && echo " Work dir: ${work_dir}" >&2
|
|
520
513
|
[ -n "$branch" ] && echo " Branch: ${branch}${head:+ @ ${head}}" >&2
|
|
@@ -530,54 +523,21 @@ print_slot_replace_warning() {
|
|
|
530
523
|
base_sha="$(git -C "$base_root" rev-parse --short HEAD 2>/dev/null || true)"
|
|
531
524
|
echo " New session will be based on: ${base_branch}${base_sha:+ @ ${base_sha}}" >&2
|
|
532
525
|
echo " (HEAD of ${base_root})" >&2
|
|
533
|
-
echo "
|
|
526
|
+
echo " Switch that checkout to your intended base before launching a new session." >&2
|
|
534
527
|
echo "" >&2
|
|
535
528
|
fi
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
# Require explicit confirmation before replacing an occupied slot.
|
|
543
|
-
# $1 agent_id $2 force(true/false) $3 replace(true/false)
|
|
544
|
-
# Exits 1 when replacement must not proceed.
|
|
545
|
-
require_slot_replace_confirm() {
|
|
546
|
-
local agent_id="$1"
|
|
547
|
-
local force="$2"
|
|
548
|
-
local replace="$3"
|
|
549
|
-
local wt
|
|
550
|
-
|
|
551
|
-
slot_is_occupied "$agent_id" || return 0
|
|
552
|
-
|
|
553
|
-
print_slot_replace_warning "$agent_id"
|
|
554
|
-
wt="$(existing_slot_worktree "$agent_id")"
|
|
555
|
-
|
|
556
|
-
if [ -n "$wt" ] && slot_worktree_dirty "$wt" && [ "$force" != true ]; then
|
|
557
|
-
echo "ERROR: previous session for slot ${agent_id} has uncommitted changes." >&2
|
|
558
|
-
echo " Commit them in the worktree (branch is kept on teardown), or pass --force to discard." >&2
|
|
559
|
-
echo " --force destroys uncommitted work — only use after explicit user approval." >&2
|
|
560
|
-
exit 1
|
|
561
|
-
fi
|
|
562
|
-
|
|
563
|
-
if [ "$replace" = true ] || [ "${HAR_CONFIRM_REPLACE:-}" = "1" ]; then
|
|
564
|
-
return 0
|
|
565
|
-
fi
|
|
566
|
-
|
|
567
|
-
if [ -t 0 ] && [ -t 1 ]; then
|
|
568
|
-
local answer
|
|
569
|
-
read -r -p "Replace slot ${agent_id}? Uncommitted work is lost unless committed. [y/N] " answer
|
|
570
|
-
if [[ "$answer" =~ ^[Yy]$ ]]; then
|
|
571
|
-
return 0
|
|
572
|
-
fi
|
|
573
|
-
echo "Aborted — slot ${agent_id} left unchanged." >&2
|
|
574
|
-
exit 2
|
|
529
|
+
if [ "$dirty_summary" != "clean" ] && [ "$dirty_summary" != "unknown" ]; then
|
|
530
|
+
echo " Commit or discard changes in the worktree, then teardown/complete the slot:" >&2
|
|
531
|
+
else
|
|
532
|
+
echo " Free the slot, then launch again:" >&2
|
|
575
533
|
fi
|
|
576
|
-
|
|
577
|
-
echo "
|
|
578
|
-
echo "
|
|
579
|
-
echo "
|
|
580
|
-
|
|
534
|
+
echo " har env complete ${agent_id} # or: har env teardown ${agent_id}" >&2
|
|
535
|
+
echo " har env launch ${agent_id}" >&2
|
|
536
|
+
echo "" >&2
|
|
537
|
+
echo " complete/teardown remove the worktree. The session branch is kept only if" >&2
|
|
538
|
+
echo " you committed. Gitignored paths (state/, runs/, local clones, .env.local)" >&2
|
|
539
|
+
echo " are NOT preserved." >&2
|
|
540
|
+
echo "" >&2
|
|
581
541
|
}
|
|
582
542
|
|
|
583
543
|
git_common_dir() {
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
# Launch an agent slot for CLI/library repos (git worktree by default, optional Docker infra).
|
|
3
3
|
# Every launch starts a FRESH session: any previous session for the slot is torn
|
|
4
4
|
# down (its branch is kept) and a new suffixed worktree is created from HEAD.
|
|
5
|
+
# Occupied slots always block: run ./.har/teardown.sh (or complete it) first, then relaunch.
|
|
5
6
|
#
|
|
6
|
-
# Usage: ./.har/launch.sh <agent-id> [--no-worktree] [--
|
|
7
|
+
# Usage: ./.har/launch.sh <agent-id> [--no-worktree] [--resume]
|
|
7
8
|
set -euo pipefail
|
|
8
9
|
|
|
9
10
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
@@ -16,8 +17,6 @@ source "$SCRIPT_DIR/agent-slot.sh"
|
|
|
16
17
|
|
|
17
18
|
AGENT_ID="${1:-}"
|
|
18
19
|
USE_WORKTREE="${HARNESS_USE_WORKTREE:-true}"
|
|
19
|
-
FORCE=false
|
|
20
|
-
REPLACE=false
|
|
21
20
|
RESUME=false
|
|
22
21
|
WORK_UNIT_ID="${HAR_WORK_UNIT_ID:-}"
|
|
23
22
|
ATTEMPT_ID="${HAR_ATTEMPT_ID:-}"
|
|
@@ -26,8 +25,6 @@ for arg in "$@"; do
|
|
|
26
25
|
case "$arg" in
|
|
27
26
|
--no-worktree) USE_WORKTREE=false ;;
|
|
28
27
|
--worktree) USE_WORKTREE=true ;;
|
|
29
|
-
--replace) REPLACE=true ;;
|
|
30
|
-
--force) FORCE=true ;;
|
|
31
28
|
--resume) RESUME=true ;;
|
|
32
29
|
--work-id=*) WORK_UNIT_ID="${arg#*=}" ;;
|
|
33
30
|
--attempt-id=*) ATTEMPT_ID="${arg#*=}" ;;
|
|
@@ -42,7 +39,7 @@ export SLOT_ATTEMPT_ID="$ATTEMPT_ID"
|
|
|
42
39
|
|
|
43
40
|
if [[ -z "$AGENT_ID" ]]; then
|
|
44
41
|
har_load_agent_slot_limits
|
|
45
|
-
echo "Usage: $0 <agent-id> [--no-worktree] [--
|
|
42
|
+
echo "Usage: $0 <agent-id> [--no-worktree] [--resume]" >&2
|
|
46
43
|
echo " agent-id must be between ${HARNESS_AGENT_SLOT_MIN} and ${HARNESS_AGENT_SLOT_MAX}" >&2
|
|
47
44
|
exit 1
|
|
48
45
|
fi
|
|
@@ -61,7 +58,7 @@ ENV_FILE=""
|
|
|
61
58
|
REGISTRY_WRITTEN=false
|
|
62
59
|
|
|
63
60
|
if [ "$RESUME" = true ]; then
|
|
64
|
-
har_launch_preflight "$AGENT_ID"
|
|
61
|
+
har_launch_preflight "$AGENT_ID" true || exit $?
|
|
65
62
|
eval "$(har_resume_session_assignments "$AGENT_ID")"
|
|
66
63
|
REGISTRY_WRITTEN=true
|
|
67
64
|
mark_slot_failed() {
|
|
@@ -87,13 +84,8 @@ if [ "$RESUME" = true ]; then
|
|
|
87
84
|
}
|
|
88
85
|
trap mark_slot_failed EXIT
|
|
89
86
|
else
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if slot_is_occupied "$AGENT_ID"; then
|
|
93
|
-
require_slot_replace_confirm "$AGENT_ID" "$FORCE" "$REPLACE"
|
|
94
|
-
log "Replacing previous session for slot ${AGENT_ID}..."
|
|
95
|
-
"$SCRIPT_DIR/teardown.sh" "$AGENT_ID" >&2
|
|
96
|
-
fi
|
|
87
|
+
# Occupied slots always block here: teardown/complete first, then relaunch.
|
|
88
|
+
har_launch_preflight "$AGENT_ID" || exit $?
|
|
97
89
|
fi
|
|
98
90
|
|
|
99
91
|
"$SCRIPT_DIR/setup-infra.sh"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Launch readiness gate for CLI/library harnesses (occupied slot only).
|
|
3
|
-
# Usage: ./.har/preflight.sh <agent-id>
|
|
3
|
+
# Usage: ./.har/preflight.sh <agent-id>
|
|
4
4
|
set -euo pipefail
|
|
5
5
|
|
|
6
6
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
@@ -12,24 +12,15 @@ source "$SCRIPT_DIR/harness.env"
|
|
|
12
12
|
source "$SCRIPT_DIR/agent-slot.sh"
|
|
13
13
|
|
|
14
14
|
AGENT_ID="${1:-}"
|
|
15
|
-
FORCE=false
|
|
16
|
-
REPLACE=false
|
|
17
|
-
|
|
18
|
-
for arg in "$@"; do
|
|
19
|
-
case "$arg" in
|
|
20
|
-
--replace) REPLACE=true ;;
|
|
21
|
-
--force) FORCE=true ;;
|
|
22
|
-
esac
|
|
23
|
-
done
|
|
24
15
|
|
|
25
16
|
if [[ -z "$AGENT_ID" ]]; then
|
|
26
|
-
echo "Usage: $0 <agent-id>
|
|
17
|
+
echo "Usage: $0 <agent-id>" >&2
|
|
27
18
|
exit 1
|
|
28
19
|
fi
|
|
29
20
|
|
|
30
21
|
validate_agent_id "$AGENT_ID"
|
|
31
22
|
|
|
32
|
-
if har_launch_preflight "$AGENT_ID"
|
|
23
|
+
if har_launch_preflight "$AGENT_ID"; then
|
|
33
24
|
echo "Slot ${AGENT_ID}: ready to launch."
|
|
34
25
|
exit 0
|
|
35
26
|
fi
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
| **Simulator** | Configured in `.har/harness.env` — `HARNESS_SIMULATOR_NAME` |
|
|
12
12
|
| **Scheme** | Configured in `.har/harness.env` — `HARNESS_XCODE_SCHEME` |
|
|
13
13
|
|
|
14
|
-
**Never edit the main checkout** — launch FIRST, then make ALL file edits under the work dir from the launch output.
|
|
14
|
+
**Never edit the main checkout** — launch FIRST, then make ALL file edits under the work dir from the launch output. An occupied slot always blocks a new launch — run `har env teardown <id>` (or `complete <id>`) first, then launch again.
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
17
|
./.har/agent-cli.sh ${AGENT_ID} status
|
|
@@ -119,8 +119,8 @@ checkout's current HEAD** at
|
|
|
119
119
|
Switch that checkout to your intended base before launch. The session is recorded in
|
|
120
120
|
`.har/slots/agent-<id>.json`.
|
|
121
121
|
|
|
122
|
-
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
- Occupied slots always block a new launch: `har env complete <id>` (or `teardown <id>`),
|
|
123
|
+
then `har env launch <id>`. A new launch never chooses `main` for you — switch the
|
|
124
|
+
main checkout to your intended base first.
|
|
125
125
|
- `teardown` removes the worktree but **keeps the session branch** for push / PR.
|
|
126
126
|
- `har env complete <id>` finishes a session: full verify + teardown, branch kept.
|