@osfactory/har 1.3.0 → 1.4.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 CHANGED
@@ -4796,7 +4796,9 @@ var init_schema = __esm({
4796
4796
  toolCallId: external_exports.string().optional(),
4797
4797
  correlationId: external_exports.string().optional(),
4798
4798
  workUnitId: WorkUnitIdSchema.optional(),
4799
- attemptId: WorkAttemptIdSchema.optional()
4799
+ attemptId: WorkAttemptIdSchema.optional(),
4800
+ /** One occupancy of a slot (#316) — records are scoped to it, not to slot id. */
4801
+ occupancyKey: external_exports.string().min(1).optional()
4800
4802
  }).passthrough();
4801
4803
  AgentTrajectoryRecordSchema = AgentTrajectoryRecordV1Schema;
4802
4804
  USAGE_HARVEST_VERSION = 2;
@@ -4811,6 +4813,8 @@ var init_schema = __esm({
4811
4813
  suffix: external_exports.string().optional(),
4812
4814
  workUnitId: WorkUnitIdSchema.optional(),
4813
4815
  attemptId: WorkAttemptIdSchema.optional(),
4816
+ /** One occupancy of a slot (#316) — usage is scoped to it, not to slot id. */
4817
+ occupancyKey: external_exports.string().min(1).optional(),
4814
4818
  tokensInput: external_exports.number().nonnegative().default(0),
4815
4819
  tokensOutput: external_exports.number().nonnegative().default(0),
4816
4820
  tokensCacheRead: external_exports.number().nonnegative().default(0),
@@ -32752,6 +32756,13 @@ function findValidation(checkoutDir, treeHash) {
32752
32756
  return void 0;
32753
32757
  }
32754
32758
  }
32759
+ function findPassingFullValidation(input) {
32760
+ if (!isCheckoutRoot(input.checkoutDir)) return void 0;
32761
+ const { treeHash } = computeWorktreeSnapshot(input.checkoutDir);
32762
+ const record2 = findValidation(input.checkoutDir, treeHash) ?? (input.harnessRoot ? findValidation(input.harnessRoot, treeHash) : void 0);
32763
+ if (record2?.status === "pass" && record2.full) return record2;
32764
+ return void 0;
32765
+ }
32755
32766
  function attachCommit(checkoutDir, treeHash, commitSha) {
32756
32767
  const record2 = findValidation(checkoutDir, treeHash);
32757
32768
  if (!record2) return void 0;
@@ -79330,9 +79341,9 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
79330
79341
  return envResult;
79331
79342
  }
79332
79343
  /**
79333
- * Finish a session: full verification (recorded as a validation keyed by the
79334
- * worktree tree hash), then teardown. The session branch is kept so the user
79335
- * can push it and open a PR.
79344
+ * Finish a session: reuse the last matching passing full validation (or
79345
+ * re-run verify when asked), then teardown. The session branch is kept so
79346
+ * the user can push it and open a PR.
79336
79347
  */
79337
79348
  async completeEnvironment(options) {
79338
79349
  const harnessRoot = resolveHarnessRoot(options.repoPath);
@@ -79344,9 +79355,11 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
79344
79355
  stderr: `No active session for agent ${options.agentId}. Run launch first.`
79345
79356
  };
79346
79357
  }
79358
+ const runVerify = shouldReverifyOnComplete(options);
79347
79359
  let verification;
79348
79360
  let validation2;
79349
- if (!options.skipVerify) {
79361
+ let reusedValidation = false;
79362
+ if (runVerify) {
79350
79363
  const verify = await this.runVerification({
79351
79364
  repoPath: options.repoPath,
79352
79365
  agentId: options.agentId,
@@ -79359,11 +79372,27 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
79359
79372
  if (verify.code !== 0 || verification?.status !== "pass") {
79360
79373
  return {
79361
79374
  ...verify,
79362
- stderr: verify.stderr + "\nVerification failed \u2014 session NOT completed. Fix the failures and rerun, or complete with skipVerify."
79375
+ stderr: verify.stderr + "\nVerification failed \u2014 session NOT completed. Fix the failures and rerun `complete --verify`, or use teardown to free the slot without claiming completion."
79376
+ };
79377
+ }
79378
+ } else {
79379
+ const checkoutDir = resolveValidationCheckoutDir({
79380
+ worktreePath: session.worktreePath,
79381
+ workDir: session.workDir,
79382
+ harnessRoot
79383
+ });
79384
+ validation2 = findPassingFullValidation({ checkoutDir, harnessRoot });
79385
+ if (!validation2) {
79386
+ return {
79387
+ code: 1,
79388
+ stdout: "",
79389
+ stderr: `No passing full validation matches the current worktree. The tree may have changed since the last \`har env verify --full\`. Re-run verification with: har env complete ${options.agentId} --verify
79390
+ Or free the slot without claiming completion: har env teardown ${options.agentId}`
79363
79391
  };
79364
79392
  }
79393
+ reusedValidation = true;
79365
79394
  }
79366
- if (session.workUnitId && session.attemptId && !options.skipVerify && !validation2) {
79395
+ if (session.workUnitId && session.attemptId && runVerify && !validation2) {
79367
79396
  return {
79368
79397
  code: 1,
79369
79398
  stdout: "",
@@ -79387,11 +79416,13 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
79387
79416
  trigger: options.trigger
79388
79417
  });
79389
79418
  if (teardown.code !== 0) return { ...teardown, verification };
79419
+ const reusedNote = reusedValidation && validation2 ? `Reused passing full validation ${validation2.validationId} for tree ${validation2.treeHash}.
79420
+ ` : "";
79390
79421
  const branchNote = session.branch ? `Branch kept: ${session.branch} \u2014 push it with: git push -u origin ${session.branch}
79391
79422
  ` : "";
79392
79423
  return {
79393
79424
  ...teardown,
79394
- stdout: teardown.stdout + branchNote,
79425
+ stdout: teardown.stdout + reusedNote + branchNote,
79395
79426
  branch: session.branch,
79396
79427
  workDir: session.workDir,
79397
79428
  worktreePath: session.worktreePath,
@@ -79430,6 +79461,10 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
79430
79461
  return toEnvironmentRunResult(result);
79431
79462
  }
79432
79463
  };
79464
+ function shouldReverifyOnComplete(options) {
79465
+ if (options.verify === true) return true;
79466
+ return options.skipVerify === false;
79467
+ }
79433
79468
  var defaultRunService = new RunService();
79434
79469
  function createRunService(executor = localScriptExecutor) {
79435
79470
  return new RunService(executor);
@@ -79919,12 +79954,12 @@ Typical agent workflow:
79919
79954
  har env launch <id> # new session from the main checkout's HEAD
79920
79955
  # \u2026edit only under the printed work dir\u2026
79921
79956
  har env verify <id> --full
79922
- har env complete <id> # done: verify + free the slot (branch kept for PR)
79957
+ har env complete <id> # done: reuse last full validation + free the slot (branch kept for PR)
79923
79958
 
79924
79959
  Slot lifecycle (do not confuse these):
79925
79960
  launch start a NEW session (worktree from $REPO_ROOT HEAD)
79926
79961
  recover resume a failed/partial launch (keeps the existing worktree)
79927
- complete finish successfully: full verify, then teardown (frees the slot)
79962
+ complete finish successfully: reuse last matching full validation, then teardown (frees the slot)
79928
79963
  teardown abandon / free the slot without a completion validation
79929
79964
 
79930
79965
  Occupied slots always block a new launch:
@@ -79940,7 +79975,7 @@ var HAR_ENV_EPILOG = `Environment lifecycle:
79940
79975
  launch <id> fresh session worktree from the main checkout's current HEAD
79941
79976
  recover <id> alias for launch --resume (failed/starting only)
79942
79977
  verify <id> run checks (--full before declaring done)
79943
- complete <id> full verify + teardown; keeps branch for push/PR (frees slot)
79978
+ complete <id> reuse last full validation + teardown; --verify to re-run; keeps branch (frees slot)
79944
79979
  teardown <id> free the slot without recording a completion validation
79945
79980
 
79946
79981
  Occupied slots:
@@ -80168,11 +80203,15 @@ var envCommand = {
80168
80203
  handleTeardown
80169
80204
  ).command(
80170
80205
  "complete <id>",
80171
- "Finish a session: full verify (recorded as validation), teardown, keep the branch for a PR",
80172
- (y) => y.positional("id", { type: "number", describe: "Agent slot id (see .har/stages.json agentSlots)" }).option("repo", { type: "string", default: "." }).option("skip-verify", {
80206
+ "Finish a session: reuse the last matching full validation, teardown, keep the branch for a PR",
80207
+ (y) => y.positional("id", { type: "number", describe: "Agent slot id (see .har/stages.json agentSlots)" }).option("repo", { type: "string", default: "." }).option("verify", {
80173
80208
  type: "boolean",
80174
80209
  default: false,
80175
- describe: "Tear down without running verification (no validation is recorded)"
80210
+ describe: "Re-run full verification before teardown (use when the tree may have changed since the last passing --full)"
80211
+ }).option("skip-verify", {
80212
+ type: "boolean",
80213
+ hidden: true,
80214
+ describe: "Deprecated no-op: complete already skips re-verification by default"
80176
80215
  }),
80177
80216
  handleComplete
80178
80217
  ).command(
@@ -80694,6 +80733,7 @@ async function handleComplete(argv) {
80694
80733
  const result = await completeEnvironment({
80695
80734
  repoPath: repo,
80696
80735
  agentId,
80736
+ verify: argv.verify,
80697
80737
  skipVerify: argv.skipVerify,
80698
80738
  capture: false
80699
80739
  });
@@ -90261,6 +90301,14 @@ async function runLineGate(options) {
90261
90301
  }
90262
90302
  const registry2 = readStageRegistry(repoPath);
90263
90303
  const registeredIds = new Set(registry2.stages.map((s) => s.id));
90304
+ if (options.agentId === void 0) {
90305
+ const needSlot = required2.filter((stage) => registry2.stages.find((s) => s.id === stage.id)?.requiresAgentId).map((stage) => stage.id);
90306
+ if (needSlot.length > 0) {
90307
+ throw new Error(
90308
+ `Gate stage(s) ${needSlot.join(", ")} run in an agent slot \u2014 pass --agent <id> (har line gate ${options.station} --line ${lineId} --agent 1).`
90309
+ );
90310
+ }
90311
+ }
90264
90312
  const results = [];
90265
90313
  let pass = true;
90266
90314
  for (const stage of required2) {
@@ -90431,7 +90479,12 @@ var PreflightEnvironmentOutputSchema = ShellRunOutputSchema.extend({
90431
90479
  var CompleteEnvironmentInputSchema = external_exports.object({
90432
90480
  repo: external_exports.string().default("."),
90433
90481
  agentId: agentIdSchema,
90434
- skipVerify: external_exports.boolean().default(false).describe("Tear down without running verification (no validation is recorded)")
90482
+ verify: external_exports.boolean().optional().describe(
90483
+ "Re-run full verification before teardown when the tree may have changed since the last passing --full"
90484
+ ),
90485
+ skipVerify: external_exports.boolean().optional().describe(
90486
+ "Deprecated. Complete already skips re-verification by default. Pass verify=true to re-run; skipVerify=false still forces a re-run."
90487
+ )
90435
90488
  });
90436
90489
  var CompleteEnvironmentOutputSchema = ShellRunOutputSchema.extend({
90437
90490
  branch: external_exports.string().optional().describe("Session branch kept for the user to push / open a PR"),
@@ -90739,14 +90792,18 @@ var HAR_MCP_TOOLS = [
90739
90792
  },
90740
90793
  {
90741
90794
  name: "har_complete_environment",
90742
- description: "Finish a session when the work is done: runs full verification (recorded as a validation of the worktree tree hash), tears the slot down, and KEEPS the session branch so the user can push it and open a PR.",
90795
+ description: "Finish a session when the work is done: reuses the last passing full validation for the current worktree (or re-runs verify when verify=true), tears the slot down, and KEEPS the session branch so the user can push it and open a PR.",
90743
90796
  inputSchema: objectJsonSchema(
90744
90797
  {
90745
90798
  repo: repoJsonProperty,
90746
90799
  agentId: agentIdJsonProperty,
90800
+ verify: {
90801
+ type: "boolean",
90802
+ description: "Re-run full verification before teardown when the tree may have changed since the last passing --full"
90803
+ },
90747
90804
  skipVerify: {
90748
90805
  type: "boolean",
90749
- description: "Tear down without running verification (no validation is recorded)"
90806
+ description: "Deprecated. Complete already skips re-verification by default. Pass verify=true to re-run."
90750
90807
  }
90751
90808
  },
90752
90809
  ["agentId"]
@@ -91083,6 +91140,7 @@ async function handleMcpToolCall(toolName, args, defaultRepo = ".") {
91083
91140
  const result = await completeEnvironment({
91084
91141
  repoPath: repo,
91085
91142
  agentId,
91143
+ verify: input.verify,
91086
91144
  skipVerify: input.skipVerify,
91087
91145
  capture: true,
91088
91146
  trigger: "mcp"
@@ -91277,7 +91335,7 @@ var ONBOARDING_GUIDE_STEPS = [
91277
91335
  body: [
91278
91336
  "Quick: har env verify <id>",
91279
91337
  "Done: har env verify <id> --full then har env complete <id>",
91280
- "`complete` records a validation, frees the slot, and keeps the session branch for a PR."
91338
+ "`complete` reuses the last matching full validation, frees the slot, and keeps the session branch for a PR. Pass --verify if the tree changed."
91281
91339
  ].join("\n")
91282
91340
  },
91283
91341
  {
@@ -57,7 +57,8 @@ Do not substitute ad-hoc test commands for harness verification. Any edit after
57
57
  ## 5. Finish
58
58
 
59
59
  ```bash
60
- har env complete <id> # full verify + validation record + teardown, keeps the session branch
60
+ har env complete <id> # reuse last passing full validation + teardown, keeps the session branch
61
+ har env complete <id> --verify # re-run full verify first if the tree may have changed
61
62
  ```
62
63
 
63
64
  Report to the user: verification result, the session branch name (so they can push / open a PR), and the slot preview URLs if the app is running.
@@ -6,7 +6,7 @@
6
6
  - [ ] New behavior has automated test coverage
7
7
  - [ ] Changes committed **in the session worktree** with a clear message
8
8
  - [ ] Present session handoff (summary, branch, preview URLs) and **wait for user** before `complete`, push, or PR
9
- - [ ] On user approval of the default: push + open PR (when `gh`/GitHub MCP available), then `har env complete <id>` (or MCP `har_complete_environment`) — full verify + validation + teardown, branch kept
9
+ - [ ] On user approval of the default: push + open PR (when `gh`/GitHub MCP available), then `har env complete <id>` (or MCP `har_complete_environment`) — reuse last passing full validation + teardown, branch kept. Pass `--verify` / `verify: true` if the tree may have changed.
10
10
 
11
11
  ### Session handoff
12
12
 
@@ -7,7 +7,7 @@
7
7
  - [ ] Changes committed **in the session worktree** with a clear message
8
8
  - [ ] The user got the preview URLs to test the app themselves
9
9
  - [ ] Present session handoff (summary, branch, preview URLs) and **wait for user** before `complete`, push, or PR
10
- - [ ] On user approval of the default: push + open PR (when `gh`/GitHub MCP available), then `har env complete <id>` (or MCP `har_complete_environment`) — full verify + validation + teardown, branch kept
10
+ - [ ] On user approval of the default: push + open PR (when `gh`/GitHub MCP available), then `har env complete <id>` (or MCP `har_complete_environment`) — reuse last passing full validation + teardown, branch kept. Pass `--verify` / `verify: true` if the tree may have changed.
11
11
 
12
12
  ### Session handoff
13
13
 
@@ -6,7 +6,7 @@
6
6
  - [ ] New behavior has automated test coverage (unit tests via XCTest)
7
7
  - [ ] Changes committed **in the session worktree** with a clear message
8
8
  - [ ] Present session handoff (summary, branch, preview URLs) and **wait for user** before `complete`, push, or PR
9
- - [ ] On user approval of the default: push + open PR (when `gh`/GitHub MCP available), then `har env complete <id>` (or MCP `har_complete_environment`) — full verify + validation + teardown, branch kept
9
+ - [ ] On user approval of the default: push + open PR (when `gh`/GitHub MCP available), then `har env complete <id>` (or MCP `har_complete_environment`) — reuse last passing full validation + teardown, branch kept. Pass `--verify` / `verify: true` if the tree may have changed.
10
10
 
11
11
  ### Session handoff
12
12
 
@@ -15,6 +15,7 @@ never in the main checkout.
15
15
  or open a PR (`--delete-branch` to drop it).
16
16
  - If launch fails after creating a worktree/env file, the registry records `status: failed`.
17
17
  Resume it instead of starting fresh: `har env launch <id> --resume` or `har env recover <id>`.
18
- - `har env complete <id>` finishes a session: full verify (recorded as a validation),
19
- then teardown — branch kept.
18
+ - `har env complete <id>` finishes a session: reuses the last matching passing
19
+ full validation, then teardown — branch kept. Pass `--verify` to re-run full
20
+ verify if the tree may have changed.
20
21
  - `--no-worktree` runs the slot from the repo root instead (single-agent mode).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osfactory/har",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "HAR: open agent harness CLI and MCP server for coding agents, isolated worktrees, and software-factory workflows",
5
5
  "keywords": [
6
6
  "har",