@osfactory/har 1.11.0 → 1.13.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
@@ -4519,7 +4519,9 @@ var init_schema = __esm({
4519
4519
  version: external_exports.number().int().default(1),
4520
4520
  createdAt: external_exports.string(),
4521
4521
  updatedAt: external_exports.string(),
4522
- outcome: WorkUnitOutcomeSchema.optional()
4522
+ outcome: WorkUnitOutcomeSchema.optional(),
4523
+ /** Outcomes cleared by a later `launch --work-id` on the same unit (#352), oldest first. */
4524
+ previousOutcomes: external_exports.array(WorkUnitOutcomeSchema).optional()
4523
4525
  }).passthrough();
4524
4526
  WorkAttemptRecordSchema = external_exports.object({
4525
4527
  version: external_exports.number().int().default(1),
@@ -37319,11 +37321,6 @@ function upsertWorkUnit(harnessRoot, input) {
37319
37321
  ensureWorkEvidenceIgnored(harnessRoot);
37320
37322
  const existing = findWorkUnit(harnessRoot, metadata.workUnitId);
37321
37323
  if (existing) {
37322
- if (existing.outcome) {
37323
- throw new Error(
37324
- `Work unit ${metadata.workUnitId} is already ${existing.outcome.decision}; reopening is not supported`
37325
- );
37326
- }
37327
37324
  for (const field of ["source", "sourceUrl", "title", "parentWorkUnitId"]) {
37328
37325
  if (metadata[field] !== void 0 && existing[field] !== void 0 && metadata[field] !== existing[field]) {
37329
37326
  throw new Error(
@@ -37349,7 +37346,10 @@ function upsertWorkUnit(harnessRoot, input) {
37349
37346
  version: 1,
37350
37347
  createdAt: existing?.createdAt ?? now,
37351
37348
  updatedAt: now,
37352
- outcome: existing?.outcome
37349
+ // A new launch on a decided unit reopens it (#352): a teardown or complete
37350
+ // closed one attempt, not the issue. The old outcome stays on the record.
37351
+ outcome: void 0,
37352
+ previousOutcomes: existing?.outcome ? [...existing.previousOutcomes ?? [], existing.outcome] : existing?.previousOutcomes
37353
37353
  });
37354
37354
  writeRecord2(
37355
37355
  hashedRecordPath(harnessRoot, WORK_UNITS_DIR, metadata.workUnitId),
@@ -40781,6 +40781,35 @@ var init_worktree_ownership = __esm({
40781
40781
  }
40782
40782
  });
40783
40783
 
40784
+ // src/runtime/slot-database.ts
40785
+ function planSlotDatabaseSteps(env3, processManager) {
40786
+ if (processManager === "simulator") return [];
40787
+ const steps = [];
40788
+ if (env3.HARNESS_DB_MIGRATE_CMD) {
40789
+ steps.push({
40790
+ envVar: "HARNESS_DB_MIGRATE_CMD",
40791
+ label: `Applying database schema: ${env3.HARNESS_DB_MIGRATE_CMD}`,
40792
+ command: env3.HARNESS_DB_MIGRATE_CMD,
40793
+ what: "migrate"
40794
+ });
40795
+ }
40796
+ if (env3.HARNESS_DB_SEED_CMD && !infraEnabled(env3, "db")) {
40797
+ steps.push({
40798
+ envVar: "HARNESS_DB_SEED_CMD",
40799
+ label: `Running seeds: ${env3.HARNESS_DB_SEED_CMD}`,
40800
+ command: env3.HARNESS_DB_SEED_CMD,
40801
+ what: "seed"
40802
+ });
40803
+ }
40804
+ return steps;
40805
+ }
40806
+ var init_slot_database = __esm({
40807
+ "src/runtime/slot-database.ts"() {
40808
+ "use strict";
40809
+ init_infra();
40810
+ }
40811
+ });
40812
+
40784
40813
  // src/runtime/hooks.ts
40785
40814
  function readHookHarnessEnv(harnessDir) {
40786
40815
  const envPath = path37.join(harnessDir, "harness.env");
@@ -41203,13 +41232,13 @@ async function launchSession(options) {
41203
41232
  ], { env: { ...process.env, ...env3 } });
41204
41233
  if (bootstrap.code !== 0) throw new Error(`minimal data bootstrap failed (${bootstrap.code})`);
41205
41234
  }
41206
- if (pm !== "simulator" && env3.HARNESS_DB_MIGRATE_CMD) {
41207
- log(`Applying database schema: ${env3.HARNESS_DB_MIGRATE_CMD}`);
41208
- const migrate = exec("bash", [
41235
+ for (const step of planSlotDatabaseSteps(env3, pm)) {
41236
+ log(step.label);
41237
+ const result = exec("bash", [
41209
41238
  "-c",
41210
- `cd ${JSON.stringify(session.workDir)} && set -a && . ${JSON.stringify(session.envFile)} && set +a && eval "$HARNESS_DB_MIGRATE_CMD"`
41239
+ `cd ${JSON.stringify(session.workDir)} && set -a && . ${JSON.stringify(session.envFile)} && set +a && eval "$${step.envVar}"`
41211
41240
  ], { env: { ...process.env, ...env3 } });
41212
- if (migrate.code !== 0) throw new Error(`database migrate command failed (${migrate.code})`);
41241
+ if (result.code !== 0) throw new Error(`database ${step.what} command failed (${result.code})`);
41213
41242
  }
41214
41243
  let simName = "";
41215
41244
  let simUdid = "";
@@ -41386,6 +41415,7 @@ var init_launch = __esm({
41386
41415
  init_worktree_ownership();
41387
41416
  init_exec();
41388
41417
  init_infra();
41418
+ init_slot_database();
41389
41419
  init_worktree();
41390
41420
  init_agent_env();
41391
41421
  init_provision();
@@ -86163,6 +86193,7 @@ Run \`har env doctor\` for the full report.
86163
86193
  const doctorSummary = summarizeDoctorReport(doctor);
86164
86194
  if (doctorSummary) launchBanner += `WARN: ${doctorSummary}
86165
86195
  `;
86196
+ const launchNotices = [];
86166
86197
  if (isTelemetryEnabled() && process.env.NODE_ENV !== "test") {
86167
86198
  const ensured = await ensureTelemetryInfrastructure({ startIfNeeded: true });
86168
86199
  if (ensured.message) {
@@ -86202,6 +86233,7 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
86202
86233
  blocked: true
86203
86234
  };
86204
86235
  }
86236
+ const priorOutcome = findWorkUnit(harnessRoot, workUnitId)?.outcome;
86205
86237
  upsertWorkUnit(harnessRoot, {
86206
86238
  workUnitId,
86207
86239
  source: options.source,
@@ -86210,6 +86242,12 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
86210
86242
  parentWorkUnitId: options.parentWorkUnitId,
86211
86243
  relatedLinks: options.relatedLinks
86212
86244
  });
86245
+ if (priorOutcome) {
86246
+ const reason = priorOutcome.reason ? `: ${priorOutcome.reason}` : "";
86247
+ launchNotices.push(
86248
+ `Reopened work unit ${workUnitId} (${priorOutcome.decision} ${priorOutcome.decidedAt}${reason})`
86249
+ );
86250
+ }
86213
86251
  createWorkAttempt(harnessRoot, {
86214
86252
  attemptId,
86215
86253
  workUnitId,
@@ -86276,11 +86314,16 @@ Usage from Cursor / Claude / Codex (@osfactory/otel-hook) will appear under Work
86276
86314
  });
86277
86315
  }
86278
86316
  }
86317
+ if (launchNotices.length) {
86318
+ launchBanner += launchNotices.map((notice) => `NOTE: ${notice}
86319
+ `).join("");
86320
+ }
86279
86321
  if (launchBanner) {
86280
86322
  envResult.stderr = `${launchBanner}${envResult.stderr ?? ""}`;
86281
86323
  }
86282
- if (guard.readiness?.warnings?.length) {
86283
- envResult.warnings = guard.readiness.warnings;
86324
+ const warnings = [...guard.readiness?.warnings ?? [], ...launchNotices];
86325
+ if (warnings.length) {
86326
+ envResult.warnings = warnings;
86284
86327
  }
86285
86328
  }
86286
86329
  return envResult;
@@ -50,8 +50,13 @@ export HARNESS_ECOSYSTEM="auto"
50
50
  # list the ones this project needs here (e.g. "db redis mailpit").
51
51
  export HARNESS_INFRA_SERVICES="db"
52
52
 
53
- # Database setup commands (run once against template DB in setup-infra.sh).
54
- # Empty = not configured; setup-infra.sh skips the step.
53
+ # Database setup commands. Both run from the work dir with the slot env sourced.
54
+ # - MIGRATE runs once against the Postgres template DB (when "db" is in
55
+ # HARNESS_INFRA_SERVICES) and again per slot at launch, so schema drift after a
56
+ # code change surfaces as a launch error, not runtime 500s.
57
+ # - SEED runs once against the Postgres template DB (slots clone it), or once per
58
+ # slot at launch for file-backed databases such as SQLite (#345).
59
+ # Empty = not configured; the step is skipped.
55
60
  export HARNESS_DB_MIGRATE_CMD=""
56
61
  export HARNESS_DB_SEED_CMD=""
57
62
 
@@ -38,8 +38,13 @@ export HARNESS_ECOSYSTEM="auto"
38
38
  # compose file, then list the ones this project needs here (e.g. "db redis").
39
39
  export HARNESS_INFRA_SERVICES=""
40
40
 
41
- # Database setup commands (run once against template DB in setup-infra.sh).
42
- # Empty = not configured; setup-infra.sh skips the step.
41
+ # Database setup commands. Both run from the work dir with the slot env sourced.
42
+ # - MIGRATE runs once against the Postgres template DB (when "db" is in
43
+ # HARNESS_INFRA_SERVICES) and again per slot at launch, so schema drift after a
44
+ # code change surfaces as a launch error, not runtime 500s.
45
+ # - SEED runs once against the Postgres template DB (slots clone it), or once per
46
+ # slot at launch for file-backed databases such as SQLite (#345).
47
+ # Empty = not configured; the step is skipped.
43
48
  export HARNESS_DB_MIGRATE_CMD=""
44
49
  export HARNESS_DB_SEED_CMD=""
45
50
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osfactory/har",
3
- "version": "1.11.0",
3
+ "version": "1.13.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",