Skip to content

fix(service-automation): trigger-fired-run failure log keeps the AutomationResult.error envelope in meta, not the message (#6587) - #6623

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-6587-trigger-run-envelope-splice
Aug 8, 2026
Merged

fix(service-automation): trigger-fired-run failure log keeps the AutomationResult.error envelope in meta, not the message (#6587)#6623
os-project-manager merged 1 commit into
mainfrom
claude/issue-6587-trigger-run-envelope-splice

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Closes #6587

The site

activateFlowTrigger's trigger callback logged a failed trigger-fired run as Trigger-fired run of flow '…' failed: ${result.error ?? 'unknown error'} at logger.error — splicing the AutomationResult.error envelope, which carries a failing node's / driver's text verbatim (#5912), into the log MESSAGE. This is the one same-class site PR #6568's residual sweep reported and left (it was outside #6499's list of 13), on the fired-run path — the identical class as that PR's site 12 (bubbleToParent's envelope branch), with the same downstream harm: ObjectLogger.write() emits one timestamp+level head per call, so foreign newlines shred ONE record into several physical lines of which only the first is greppable.

The fix (the family shape, not re-derived)

Message and cause separated per the merged family idiom (#6498/#6568): the message stays one physical line and carries only controlled facts — the flow name, the trigger type (resolved.triggerType), and the consequence — keeping its original Trigger-fired run of flow '…' failed lead phrase so existing greps still match. The envelope rides the logger's structured meta slot ({ error: result.error ?? 'unknown error' }), passed THIRD in the error(message, error?, meta?) contract with the Error slot deliberately empty (#5575). This is the same treatment #6568 gave site 12's string envelope — not describeThrownForLog, which renders THROWN values; the envelope here is already a string, so it maps straight onto the meta's error field.

Per-site #4632 verdict

Stays error — on its own reasoning, not inertia. The judgment question answers YES here: after the failure the system looks normal from the outside — the triggering event was handled, NO caller holds this result envelope (fire-and-forget path), and nothing retries the run — while the flow's declared effects never landed. The only other trace is the passive run-history row (the 2026-07-17 eval preserved in the site comment: a failing record-change flow produced zero output; the failure lived only in that row). That stderr survives the CLI's boot-quiet stdout window is stream mechanics, not the verdict. The verdict is pinned by test because check:durability-log-level does not reach this package.

Tests

Three new pins appended to engine-residual-log-cause.test.ts (the #6568 file), in its exact idiom — real bytes off a real ObjectLogger, both streams captured, the family's byte-identical multi-line driver fixture. The seam is driven the way a real trigger plugin drives it: the test's FlowTrigger.start captures the engine-built callback and fires it once; the flow's work node THROWS the driver text, so execute()'s catch lands it in result.error verbatim, un-prefixed (the #5912-preserved shape).

  1. JSON: level error, exactly one stderr line, message one line leaking no driver fact, the whole envelope in record.error (meta spread proves the slot), stdout silent. Behaviour-unchanged assert: the callback resolves (nothing thrown back at the trigger plugin) and the failed run still lands in run history with status: 'failed'.
  2. Pretty: one physical line carrying the timestamp+ERROR head and every fact greppable on it.
  3. Prototype spy: meta THIRD, Error slot deliberately empty (finding(service-automation): connector 物化失败的 fail() 也是 ${err.message} 单行插值,同 #5048 的类别、另一个接缝 #5575).

Reverse verification (direction predicted before running)

Predicted: re-splicing the site turns exactly the 3 new pins red — the JSON pin inside expectOneLineWithCause (msg gains the newlines + driver text, record.error vanishes), the pretty pin's toHaveLength(1) (one record becomes 3 physical lines, the driver's first line concatenating onto the message line), the slot pin's message-one-line assert — and the 18 pre-existing pins stay green. Observed: exactly that, 3 failed / 18 passed, each failing on the predicted assertion (the pretty pin reported expected [ …(3) ] to have a length of 1 but got 3). Restored: 21/21 green.

Local runs

Scope

ONE site, per the dispatch. Explicitly untouched: the name-shaped tail (engine.ts :2719, :3365, :3598, :4209, :4899 — a different class; the PM splits it into its own finding at review), evaluateCondition, validateFlowExpressions' fatal failures array, and everything #6568 already fixed. A user-visible log-shape change, so a patch changeset rides along.


🤖 Generated with Claude Code

https://claude.ai/code/session_01USNUyHEr7uaU6MoEWXitei


Generated by Claude Code

…lope in meta, not the message (#6587)

activateFlowTrigger's trigger callback spliced AutomationResult.error —
which carries a failing node's / driver's text verbatim (#5912) — into
the logger.error MESSAGE, the one same-class site PR #6568's residual
sweep left on the fired-run path. The message now stays one physical
line with the controlled facts (flow name, trigger type, consequence)
and the envelope rides the structured meta slot, matching the family
shape (#6499/#6568, site-12 envelope treatment).

Per-site #4632 verdict: stays `error` on its own reasoning — the
fired-run path has no caller holding the result envelope, so the
failure would otherwise look normal from the outside; pinned by three
new tests in engine-residual-log-cause.test.ts (level + stream +
one-line + envelope-in-meta + error-slot layout).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01USNUyHEr7uaU6MoEWXitei
@vercel

vercel Bot commented Aug 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 8, 2026 7:31am

Request Review

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/service-automation.

5 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/automation/flows.mdx (via @objectstack/service-automation)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/service-automation)
  • content/docs/plugins/packages.mdx (via @objectstack/service-automation)
  • content/docs/releases/implementation-status.mdx (via @objectstack/service-automation)
  • content/docs/releases/v9.mdx (via @objectstack/service-automation)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Aug 8, 2026
@os-project-manager
os-project-manager marked this pull request as ready for review August 8, 2026 08:45
@os-project-manager
os-project-manager added this pull request to the merge queue Aug 8, 2026
Merged via the queue into main with commit e218483 Aug 8, 2026
25 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-6587-trigger-run-envelope-splice branch August 8, 2026 09:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

2 participants