Skip to content

feat(core): inject workflow run context into orchestrator prompt#1065

Merged
coleam00 merged 2 commits intodevfrom
archon/task-fix-issue-1055
Apr 16, 2026
Merged

feat(core): inject workflow run context into orchestrator prompt#1065
coleam00 merged 2 commits intodevfrom
archon/task-fix-issue-1055

Conversation

@coleam00
Copy link
Copy Markdown
Owner

@coleam00 coleam00 commented Apr 10, 2026

Summary

  • Problem: After a workflow completes, the AI has zero awareness of what the workflow produced. Users asking follow-up questions like "what did it change?" get unhelpful responses.
  • Why it matters: Workflow summaries are already persisted in the DB as workflow_result messages, but buildFullPrompt() never loaded them — wasted data and broken UX.
  • What changed: Three targeted additions: (1) getRecentWorkflowResultMessages() DB query in messages.ts, (2) formatWorkflowContextSection() formatter in prompt-builder.ts, (3) handleMessage() fetches and injects the context into buildFullPrompt() via a new optional workflowContext parameter.
  • What did not change: No schema migrations, no new tables, no changes to how workflow results are stored. Platform-specific behavior is unchanged. buildFullPrompt() output is byte-for-byte identical when no workflow results exist.

UX Journey

Before

  User                   Archon                      AI
  ────                   ──────                      ──
  "Fix issue #42" ─────▶ routes to workflow
                         workflow runs, stores summary in DB
  sees result card ◀──── UI shows result

  "What did it change?" ▶ handleMessage() builds prompt
                          NO workflow context loaded
                          AI: "I don't have context about
                               recent workflow results"  ✗

After

  User                   Archon                      AI
  ────                   ──────                      ──
  "Fix issue #42" ─────▶ routes to workflow
                         workflow runs, stores summary in DB
  sees result card ◀──── UI shows result

  "What did it change?" ▶ [handleMessage() queries recent workflow_result messages]
                          [formatWorkflowContextSection() builds context block]
                          [buildFullPrompt() injects "Recent Workflow Results" section]
                          AI: "The workflow made the following changes: ..."  ✓

Architecture Diagram

Before

  handleMessage()
      │
      ├─▶ buildFullPrompt(conversation, codebases, workflows, message,
      │                   issueContext, threadContext, attachedFiles)
      │       └─▶ builds prompt (NO workflow history)
      │
      └─▶ assistant.query(fullPrompt)

After

  handleMessage()
      │
      ├─▶ [messageDb.getRecentWorkflowResultMessages(conversation.id, 3)]  [+]
      │       └─▶ remote_agent_messages WHERE category='workflow_result'    [+]
      │
      ├─▶ [formatWorkflowContextSection(workflowResults)]                   [+]
      │
      ├─▶ buildFullPrompt(conversation, codebases, workflows, message,
      │                   issueContext, threadContext, attachedFiles,
      │                   workflowContext)                                   [~]
      │       └─▶ injects "Recent Workflow Results" section when present    [~]
      │
      └─▶ assistant.query(fullPrompt)

Connection inventory:

From To Status Notes
handleMessage() messageDb.getRecentWorkflowResultMessages() new Fetches up to 3 recent workflow results
handleMessage() formatWorkflowContextSection() new Formats results into prompt section
handleMessage() buildFullPrompt() modified Adds workflowContext optional param
buildFullPrompt() prompt output modified Injects context block when present

Label Snapshot

  • Risk: risk: low
  • Size: size: S
  • Scope: core
  • Module: core:orchestrator

Change Metadata

  • Change type: feature
  • Primary scope: core

Linked Issue

Validation Evidence (required)

bun run validate
  • Type check: ✅ Pass — all 9 packages, no errors
  • Lint: ✅ Pass — 0 errors, 0 warnings
  • Format: ✅ Pass — all files formatted
  • Tests: ✅ Pass — all packages, 0 failures (@archon/paths 107+6skip, @archon/git 135, @archon/isolation 211, @archon/workflows ~700+, @archon/core ~900+, @archon/adapters 343, @archon/server ~235, @archon/cli ~130, @archon/web 117)

Security Impact (required)

  • New permissions/capabilities? No
  • New external network calls? No
  • Secrets/tokens handling changed? No
  • File system access scope changed? No

Compatibility / Migration

  • Backward compatible? Yes — workflowContext is optional; when undefined, buildFullPrompt() output is byte-for-byte identical to pre-PR behavior
  • Config/env changes? No
  • Database migration needed? No — queries existing remote_agent_messages rows with existing metadata JSON structure

Human Verification (required)

  • Verified scenarios: Type-check, lint, format, and full test suite all pass via bun run validate
  • Edge cases checked: formatWorkflowContextSection([]) returns '' (no empty section injected); malformed metadata JSON defaults to 'unknown' names but still surfaces the summary content; DB errors return [] non-throwing with warn log
  • What was not verified: Live end-to-end workflow run followed by follow-up question (requires running app)

Side Effects / Blast Radius (required)

  • Affected subsystems: @archon/core orchestrator prompt building only
  • Potential unintended effects: Minor token budget increase (~200-500 tokens) for conversations with recent workflow results; bounded by limit=3 default
  • Guardrails: Non-throwing query — DB failure silently returns empty context; formatWorkflowContextSection([]) returns '' so no noise when no workflows have run

Rollback Plan (required)

  • Fast rollback: Revert the commit (git revert 3e3ddf25)
  • Feature flags: None — the injection is gated on workflowContext being non-empty, which requires actual workflow result rows in the DB
  • Observable failure symptoms: Orchestrator responses would include an unexpected "Recent Workflow Results" section — easy to spot in logs

Risks and Mitigations

  • Risk: buildFullPrompt() output changes for existing conversations with workflow history
    • Mitigation: Only changes output when workflowContext is truthy; getRecentWorkflowResultMessages() returns [] for conversations with no workflow results (no change at all)
  • Risk: JSON parse failure on malformed message metadata
    • Mitigation: Inner try/catch defaults to 'unknown' for workflowName/runId while still including the summary content
  • Risk: Token budget increase from injected context
    • Mitigation: Hard limit of 3 results at the DB query level; each entry is a single summary paragraph

Summary by CodeRabbit

Release Notes

  • New Features
    • The orchestrator now retrieves and uses recent workflow execution results as context when generating responses, enabling more informed follow-up answers based on actual workflow history.
    • Improved system reliability with graceful error handling—context retrieval failures no longer interrupt agent operations.

After a workflow completes, the AI had no awareness of results when
answering follow-up questions. This adds a "Recent Workflow Results"
section to the orchestrator prompt by querying persisted workflow_result
messages from the conversation.

Changes:
- Add getRecentWorkflowResultMessages() to db/messages.ts
- Add WorkflowResultContext type and formatWorkflowContextSection() to prompt-builder.ts
- Extend buildFullPrompt() with optional workflowContext parameter
- Fetch and inject workflow context in handleMessage() before prompt building

Fixes #1055

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 10, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 39e3056e-35ff-4ca9-9d1a-d29e52f1f3e1

📥 Commits

Reviewing files that changed from the base of the PR and between 536584d and bf8bc8e.

📒 Files selected for processing (6)
  • packages/core/src/db/messages.test.ts
  • packages/core/src/db/messages.ts
  • packages/core/src/orchestrator/orchestrator-agent.test.ts
  • packages/core/src/orchestrator/orchestrator-agent.ts
  • packages/core/src/orchestrator/prompt-builder.test.ts
  • packages/core/src/orchestrator/prompt-builder.ts

📝 Walkthrough

Walkthrough

This PR implements workflow context injection into the orchestrator prompt by adding a database query function to retrieve recent workflow result messages, a formatter to structure them as prompt text, and orchestrator agent logic to fetch and inject this context before building the AI prompt.

Changes

Cohort / File(s) Summary
Database Message Queries
packages/core/src/db/messages.ts, packages/core/src/db/messages.test.ts
Added getRecentWorkflowResultMessages() function that queries messages with workflowResult metadata, featuring database-agnostic SQL generation for PostgreSQL JSON operators (metadata->>'workflowResult') vs. SQLite (json_extract(metadata, '$.workflowResult')), with configurable limit (default 3) and error resilience.
Workflow Context Formatting
packages/core/src/orchestrator/prompt-builder.ts, packages/core/src/orchestrator/prompt-builder.test.ts
Added WorkflowResultContext interface and formatWorkflowContextSection() function to convert workflow execution data into a markdown "Recent Workflow Results" section suitable for prompt injection.
Orchestrator Agent Integration
packages/core/src/orchestrator/orchestrator-agent.ts, packages/core/src/orchestrator/orchestrator-agent.test.ts
Extended handleMessage() to retrieve and parse recent workflow result messages, extract workflowName and runId from metadata, format them via formatWorkflowContextSection(), and inject into the prompt via new workflowContext parameter in buildFullPrompt(); updated buildFullPrompt() signature to accept optional workflow context for appending after Thread Context block.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Orchestrator as orchestrator-agent.ts
    participant DB as messages.ts
    participant Formatter as prompt-builder.ts
    participant AI as buildFullPrompt

    User->>Orchestrator: handleMessage(conversationId, ...)
    Orchestrator->>DB: getRecentWorkflowResultMessages(conversationId, limit=3)
    DB->>DB: Build database-specific SQL<br/>(PostgreSQL or SQLite JSON syntax)
    DB-->>Orchestrator: Promise<MessageRow[]>
    Orchestrator->>Orchestrator: Parse each message metadata<br/>Extract workflowName, runId
    Orchestrator->>Formatter: formatWorkflowContextSection(results)
    Formatter->>Formatter: Build markdown section<br/>if results.length > 0
    Formatter-->>Orchestrator: workflowContext: string
    Orchestrator->>AI: buildFullPrompt(..., workflowContext?)
    AI->>AI: Inject workflow context<br/>after Thread Context block
    AI-->>Orchestrator: fullPrompt: string
    Orchestrator-->>User: AI response with context awareness
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 The orchestrator's mind grows bright,
Now seeing workflows in full light—
Each run's whisper, each node's song,
Guides the AI along, strong and long,
Follow-ups bloom from context's gift! 🌟

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch archon/task-fix-issue-1055

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coleam00
Copy link
Copy Markdown
Owner Author

Comprehensive PR Review

PR: #1065 - feat(core): inject workflow run context into orchestrator prompt
Reviewed by: 4 specialized agents (code-review, error-handling, test-coverage, comment-quality)
Date: 2026-04-10


Summary

This PR introduces a clean, well-architected feature - injecting recent workflow results into the orchestrator prompt for better follow-up awareness. The non-blocking enrichment design, token budget (LIMIT 3 at SQL level), and pure formatter function are all solid. However, two runtime bugs make the feature completely non-functional in every deployment, and zero tests were added for three new testable code units.

Verdict: REQUEST_CHANGES

Severity Count
CRITICAL 1
HIGH 3
MEDIUM 2
LOW 5

Critical Issues

Wrong metadata filter key - feature is a complete no-op

Location: packages/core/src/db/messages.ts:78-81

The query filters on metadata.category = workflow_result, but category is never persisted to the database. It exists only in the BufferedSegment struct for SSE streaming logic. The actual DB metadata for a workflow result message looks like { "workflowResult": { "workflowName": "...", "runId": "..." } }.

The query returns 0 rows on every call. workflowContext is always undefined. The feature has no effect in production.

Fix: Change the metadata filter to check for the presence of the workflowResult key:

Evidence: persistence.ts:259-263 shows category is NOT included in the flushed metadata - only toolCalls, workflowDispatch, and workflowResult are persisted.


High Issues

JSON.parse on JSONB column always throws in PostgreSQL

Location: packages/core/src/orchestrator/orchestrator-agent.ts:763

node-postgres automatically deserializes JSONB columns into JS objects. Calling JSON.parse(msg.metadata) on an object coerces it to "[object Object]" and throws SyntaxError. The inner catch silently swallows it - so even after the CRITICAL fix above, PostgreSQL users will always see workflowName: unknown and runId: unknown.

Fix: Guard with typeof before parsing:


formatWorkflowContextSection - pure function, zero tests

Location: packages/core/src/orchestrator/prompt-builder.ts:51-65

Pure formatter with four untested behaviors: empty-input early return, header construction, per-result formatting, trimEnd(). Any regression silently corrupts the orchestrator prompt. Zero dependencies - trivial to test. Add to prompt-builder.test.ts.


getRecentWorkflowResultMessages - DB query, zero tests

Location: packages/core/src/db/messages.ts:73-97

The dialect switch (PostgreSQL JSON extraction vs SQLite json_extract) is completely untested. The non-throwing contract is load-bearing. getDatabaseType is a new import not present in the existing connection mock in messages.test.ts. Must add getDatabaseType to the mock and add tests covering both SQL dialects, parameter binding, default limit, non-throwing contract, and successful row return.


Medium Issues

Silent JSON.parse failure - no structured log

Location: packages/core/src/orchestrator/orchestrator-agent.ts:762-769

The inner catch block has only an inline comment - no getLog().warn(), no conversationId, no messageId. The outer catch never sees it. A corrupt metadata row is completely undiagnosable in production.

Fix: Add one line inside the catch: getLog().warn({ err: parseErr, conversationId, messageId: msg.id }, orchestrator.workflow_result_metadata_parse_failed);


handleMessage workflow-context block - no integration coverage

Location: packages/core/src/orchestrator/orchestrator-agent.ts:748-785

../db/messages is not mocked in orchestrator-agent.test.ts. formatWorkflowContextSection is missing from the ./prompt-builder mock - any test reaching the AI routing path would throw TypeError: formatWorkflowContextSection is not a function. Both mocks need to be added.


Low Issues

Issue Location Suggestion
SELECT * anti-pattern messages.ts:83 Use SELECT id, content, metadata with projected type
buildFullPrompt suffix placement untested orchestrator-agent.ts:480-502 Add indirect test via handleMessage once mocks are in place
File-level module doc understates scope messages.ts:1-3 (Web UI history) to (Web UI history and orchestrator prompt enrichment)
MessageRow.metadata comment says frontend-only messages.ts:19 parsed by frontend to parsed by frontend and server-side
formatWorkflowContextSection docstring imprecise prompt-builder.ts:48-50 caller checks truthiness is inaccurate - it is buildFullPrompt that checks

What is Good

  • Clean non-blocking design: outer try-catch ensures enrichment failures never break message delivery
  • limit = 3 enforced at SQL level - solid token-budget constraint
  • WorkflowResultContext is a well-scoped, correctly-placed interface with no type duplication
  • formatWorkflowContextSection is a pure formatter - zero side effects, excellent SRP
  • buildFullPrompt signature extension is backward-compatible and correctly handled in both prompt paths
  • Prompt placement is semantically correct (between thread history and current request)
  • getDatabaseType() is the right import (cheap env-var check, no DB connection required)
  • Log event names follow domain.action_state convention correctly
  • Non-throwing DB contract is explicitly documented in JSDoc
  • Inline fallback comments are excellent examples of documenting intentional silent fallbacks per CLAUDE.md

Next Steps

  1. Fix CRITICAL: filter on workflowResult key presence, not category
  2. Fix HIGH bug: guard JSON.parse with typeof check for PostgreSQL JSONB
  3. Add tests: formatWorkflowContextSection unit tests, getRecentWorkflowResultMessages dialect + contract tests, handleMessage mock updates
  4. Add warn log inside inner catch for metadata parse failures

Reviewed by Archon comprehensive-pr-review workflow
Full artifacts: ~/.archon/workspaces/coleam00/Archon/artifacts/runs/45140ee0ff4d703cf3a00c1ae1282771/review/

- CRITICAL: fix metadata filter in getRecentWorkflowResultMessages to check
  for workflowResult key presence instead of category (which is never persisted
  to DB); feature was completely non-functional on every call
- HIGH: guard JSON.parse(msg.metadata) with typeof check to handle PostgreSQL
  JSONB columns returned as objects (not strings) by node-postgres
- MEDIUM: add structured warn log inside inner metadata parse catch block
- LOW: use SELECT id, content, metadata instead of SELECT * in new DB query
- LOW: update comments in messages.ts and prompt-builder.ts for accuracy
- Tests: add formatWorkflowContextSection unit tests (pure function coverage)
- Tests: add getRecentWorkflowResultMessages tests (dialect switch + contract)
- Tests: add getDatabaseType mock to messages.test.ts connection mock
- Tests: add ../db/messages mock and formatWorkflowContextSection to
  prompt-builder mock in orchestrator-agent.test.ts
- Tests: add handleMessage workflow context injection behavioral tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coleam00
Copy link
Copy Markdown
Owner Author

Fix Report

All 11 review findings addressed. Commit: bf8bc8e


CRITICAL Fixed: Wrong metadata filter key (messages.ts)

The query filtered on metadata.category = 'workflow_result' — a field that is never persisted to the DB (it's SSE-only). Feature was completely non-functional on every call.

Fixed: Changed to check for workflowResult key presence:

  • PostgreSQL: (metadata->>'workflowResult') IS NOT NULL
  • SQLite: json_extract(metadata, '$.workflowResult') IS NOT NULL
  • Removed the 'workflow_result' param; params are now [conversationId, limit]

HIGH Fixed: JSON.parse on PostgreSQL JSONB object (orchestrator-agent.ts)

node-postgres returns JSONB columns as plain JS objects. JSON.parse(object) throws SyntaxError, causing PostgreSQL users to always see workflowName: 'unknown'.

Fixed: Added typeof guard:

const parsed = typeof msg.metadata === 'string' ? JSON.parse(msg.metadata) : msg.metadata;

MEDIUM Fixed: Silent metadata parse failure — added structured warn log

Added getLog().warn({ err, conversationId, messageId: msg.id }, 'orchestrator.workflow_result_metadata_parse_failed') inside the inner catch.


LOW Fixed: SELECT * anti-pattern

Changed to SELECT id, content, metadata with Pick<MessageRow, 'id' | 'content' | 'metadata'>.

LOW Fixed: Comment quality (3 items)

Updated module doc, MessageRow.metadata inline comment, and formatWorkflowContextSection docstring per review.


Tests Added

File New Tests
prompt-builder.test.ts 5 tests for formatWorkflowContextSection (empty, header, per-result format, multiple, trimEnd)
messages.test.ts 6 tests for getRecentWorkflowResultMessages (PG SQL, SQLite SQL, params, default limit=3, non-throwing, returns rows) + getDatabaseType added to connection mock
orchestrator-agent.test.ts 5 integration tests for handleMessage workflow context injection + formatWorkflowContextSection and ../db/messages mocks added

Validation: type-check ✓, lint ✓, format ✓, test ✓ (0 failures)

@coleam00
Copy link
Copy Markdown
Owner Author

Archon PR Validation Report

Verdict: APPROVE

Summary

Bug confirmed on main across all four claims — workflow result summaries are persisted to DB but never loaded into the orchestrator prompt. The fix is minimal and correct: a targeted DB query (getRecentWorkflowResultMessages), a clean formatter (formatWorkflowContextSection), and straightforward wiring in handleMessage(). No schema migrations, no regressions, full CLAUDE.md compliance.

Bug Confirmation

Claim Main Feature
Workflow results stored but never loaded into prompt Confirmed Fixed
buildFullPrompt() missing workflow context param Confirmed Fixed
handleMessage() never queries workflow results Confirmed Fixed
messages.ts has no workflow result query function Confirmed Fixed

Issues

No blocking issues found.

Minor suggestion (non-blocking): Type widening cast in getRecentWorkflowResultMessages — query selects 3 columns but returns full MessageRow[]. Consider SELECT * or narrowing the return type.

Fix Quality: 5/5 | Confidence: HIGH


Validated by archon-validate-pr workflow

@coleam00 coleam00 marked this pull request as ready for review April 16, 2026 12:54
@coleam00 coleam00 merged commit 2732288 into dev Apr 16, 2026
4 checks passed
@coleam00 coleam00 deleted the archon/task-fix-issue-1055 branch April 16, 2026 12:55
joaobmonteiro pushed a commit to joaobmonteiro/Archon that referenced this pull request Apr 26, 2026
…1055

feat(core): inject workflow run context into orchestrator prompt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(core): inject workflow run context into orchestrator prompt for follow-up awareness

1 participant