@inflexa-ai/harness 0.4.1 → 0.4.3

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.
@@ -59,7 +59,7 @@ import { join } from "node:path";
59
59
  import { forStep } from "../auth/types.js";
60
60
  import { createNoopLogger } from "../lib/console-logger.js";
61
61
  import { unwrapOrThrow } from "../lib/result.js";
62
- import { RunDedupCollisionError, countArtifactsForRun, loadDataProfileStatus, queryActiveRun, queryStepArtifactPaths, seedStepExecutions, suspendAnalysis as suspendAnalysisQuery, sweepPendingStepExecutions, updateRunStatus, } from "../state/index.js";
62
+ import { RunDedupCollisionError, countArtifactsForRun, loadDataProfileStatus, queryActiveRun, queryStepArtifactPaths, seedStepExecutions, setRunSynthesisOutcome, suspendAnalysis as suspendAnalysisQuery, sweepPendingStepExecutions, updateRunStatus, } from "../state/index.js";
63
63
  import { isBudgetExceeded } from "../loop/budget-exceeded.js";
64
64
  import { loadStepSummariesFromDisk } from "../execution/run-synthesis.js";
65
65
  import { MAX_UPSTREAM_ARTIFACTS, composeStepBriefing } from "../prompts/briefing.js";
@@ -224,6 +224,7 @@ export function synthesizeFindings(args) {
224
224
  return DBOS.writeStream("events", part);
225
225
  };
226
226
  return synthesizeRun({
227
+ logger: deps.logger,
227
228
  pool: deps.pool,
228
229
  provider: deps.provider,
229
230
  embedding: deps.embedding,
@@ -333,6 +334,9 @@ export async function runExecuteAnalysisBody(input, deps) {
333
334
  // after so the workflow record goes to ERROR.
334
335
  let synthesisError = null;
335
336
  let synthesisFindings = [];
337
+ // Null when synthesis never ran (disabled or no completed steps): the ledger
338
+ // columns stay NULL — "unknown", not a recorded skip.
339
+ let synthesisOutcome = null;
336
340
  const synthesisEnabled = deps.synthesisEnabled ?? true;
337
341
  if (synthesisEnabled && final.completed.size > 0) {
338
342
  try {
@@ -344,9 +348,11 @@ export async function runExecuteAnalysisBody(input, deps) {
344
348
  deps,
345
349
  }), { name: "synthesize-findings" });
346
350
  synthesisFindings = synthOut.findings;
351
+ synthesisOutcome = { status: synthOut.synthesisStatus, reason: synthOut.synthesisReason };
347
352
  }
348
353
  catch (err) {
349
354
  synthesisError = err;
355
+ synthesisOutcome = { status: "failed", reason: err instanceof Error ? err.message : String(err) };
350
356
  logger.error("synthesizeFindings failed — failing the run", { runId, ...logger.errorFields(err) });
351
357
  }
352
358
  }
@@ -369,6 +375,7 @@ export async function runExecuteAnalysisBody(input, deps) {
369
375
  : final.failureReason,
370
376
  forceFailed: synthesisError !== null,
371
377
  findings: synthesisFindings,
378
+ synthesisOutcome,
372
379
  deps,
373
380
  });
374
381
  // Synthesis failure takes priority over the budget-exceeded cascade: a run
@@ -741,6 +748,22 @@ async function collectAndComplete(args) {
741
748
  catch (err) {
742
749
  logger.error("persist-final-status failed", { status, ...logger.errorFields(err) });
743
750
  }
751
+ // Record the run's synthesis outcome AFTER the status write — a reader that
752
+ // sees a terminal status then also sees whether (and why) synthesis ran. A
753
+ // null outcome means synthesis never ran; leave the columns NULL ("unknown").
754
+ // Log-don't-rollback, like the sibling terminal steps: a failed ledger note
755
+ // must not undo an otherwise-complete run.
756
+ const outcome = args.synthesisOutcome;
757
+ if (outcome !== null) {
758
+ try {
759
+ await DBOS.runStep(async () => {
760
+ unwrapOrThrow(await setRunSynthesisOutcome(deps.pool, runId, outcome.status, outcome.reason));
761
+ }, { name: "persist-synthesis-outcome" });
762
+ }
763
+ catch (err) {
764
+ logger.error("persist-synthesis-outcome failed", { synthesisStatus: outcome.status, ...logger.errorFields(err) });
765
+ }
766
+ }
744
767
  // Sweep never-started steps to `skipped` — but ONLY on genuinely-terminal
745
768
  // paths. The gate is the BRANCH, not the written run status: the resumable
746
769
  // 402 pause below also writes "canceled", yet its pending rows must survive
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inflexa-ai/harness",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Host-agnostic bioinformatics agent harness \u2014 hand-rolled agent loop, DBOS-durable workflows, sandboxed compute.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",