Skip to content

fix(server): GET /api/workflows/:name missed home-scoped workflows#1405

Merged
Wirasm merged 4 commits into
coleam00:devfrom
lraphael:fix/get-workflow-home-scope
May 12, 2026
Merged

fix(server): GET /api/workflows/:name missed home-scoped workflows#1405
Wirasm merged 4 commits into
coleam00:devfrom
lraphael:fix/get-workflow-home-scope

Conversation

@lraphael
Copy link
Copy Markdown
Contributor

@lraphael lraphael commented Apr 24, 2026

Summary

  • Problem: GET /api/workflows/:name skipped the home scope (~/.archon/workflows/), making global workflows invisible to the Web UI builder (which loads through this endpoint).
  • Why it matters: After feat(paths,workflows): unify ~/.archon/{workflows,commands,scripts} + drop globalSearchPath (closes #1136) #1315 the list endpoint + CLI discover home-scoped workflows correctly, but the single-workflow endpoint wasn't updated → inconsistent behavior, 404 in the UI even though the workflow exists.
  • What changed: Added a home-scope lookup between project-scope and bundled defaults, mirroring the discovery order of discoverWorkflowsWithConfig. Returns source: 'global' (already in the WorkflowSource union).
  • What did not change (scope boundary): LIST endpoint, bundled/default fallback order, command discovery, any client-facing shape beyond the new source: 'global' case.

UX Journey

Before

User                       Archon Server                        Web UI
────                       ─────────────                        ──────
places workflow in
  ~/.archon/workflows/
opens /workflows/builder
  ?edit=<name>      ─────▶ GET /api/workflows/:name
                           ├─ project scope?  miss
                           └─ bundled default? miss
                           ◀──────────── 404 Not Found
                                                                "Failed to load
                                                                  workflow (404)"

After

User                       Archon Server                              Web UI
────                       ─────────────                              ──────
places workflow in
  ~/.archon/workflows/
opens /workflows/builder
  ?edit=<name>      ─────▶ GET /api/workflows/:name
                           ├─ project scope?    miss
                           ├─ [+ home scope?    HIT  → source:'global']
                           └─ bundled default?  (skipped)
                           ◀──────────── 200 { workflow, source:'global' }
                                                                builder renders
                                                                  12-node DAG,
                                                                  green "Valid"

Architecture Diagram

Before

                ┌──────────────────────────────────┐
                │  GET /api/workflows/:name        │
                │  (packages/server/.../api.ts)    │
                └─────────────┬────────────────────┘
                              │
              ┌───────────────┴───────────────┐
              ▼                               ▼
   ┌────────────────────┐        ┌────────────────────────┐
   │ project scope      │        │ bundled defaults       │
   │ <cwd>/.archon/...  │        │ packages/server/.../   │
   │ (readFile + parse) │        │   defaults/*.yaml      │
   └────────────────────┘        └────────────────────────┘

   home scope           ── NOT REACHED ──   getHomeWorkflowsPath()
   ~/.archon/workflows/                     (already used by LIST + CLI)

After

                ┌──────────────────────────────────┐
                │  GET /api/workflows/:name  [~]   │
                │  (packages/server/.../api.ts)    │
                └─────────────┬────────────────────┘
                              │
              ┌───────────────┼───────────────┬─────────────────────┐
              ▼               ▼               ▼                     ▼
   ┌──────────────────┐  ┌─────────────────────────┐   ┌────────────────────────┐
   │ project scope    │══│ [+ home scope]          │══▶│ bundled defaults       │
   │ <cwd>/.archon/.. │  │ ~/.archon/workflows/    │   │ defaults/*.yaml        │
   │ (unchanged)      │  │ getHomeWorkflowsPath()  │   │ (unchanged)            │
   │                  │  │ readFile + parseWorkflow│   │                        │
   │                  │  │ source: 'global'        │   │                        │
   └──────────────────┘  └─────────────────────────┘   └────────────────────────┘
                                       │
                                       └─ ENOENT → fall through
                                          parse error → 500
                                          (workflow.fetch_home_failed)

Connection inventory:

From To Status Notes
GET /api/workflows/:name handler project-scope readFile unchanged first lookup, same as before
GET /api/workflows/:name handler getHomeWorkflowsPath() new imported from existing helper, already used by LIST + CLI
GET /api/workflows/:name handler home-scope readFile new ~/.archon/workflows/<sanitized-name>.yaml
GET /api/workflows/:name handler parseWorkflow (home YAML) new reuses existing parser
GET /api/workflows/:name handler bundled defaults modified now reached only after project + home miss
isValidCommandName guard request handler unchanged still fires first, blocks path traversal

Label Snapshot

  • Risk: risk: low
  • Size: size: S (+116 / -3, two files)
  • Scope: server
  • Module: server:workflows

Change Metadata

  • Change type: bug
  • Primary scope: server

Linked Issue

Validation Evidence (required)

# Server suite — full test run
cd packages/server && bun run test
# → all 9 test files pass (224 tests / 0 fails)

# Type-check
cd packages/server && bun run type-check
# → clean (bun x tsc --noEmit, no output = no errors)

# Targeted run for the changed test file
bun test packages/server/src/routes/api.workflows.test.ts
# → 29 pass / 0 fail (includes 2 new tests: home-scope hit, shadow precedence)

Evidence provided:

  • Added two integration tests (api.workflows.test.ts): home-scope hit (source: 'global') and project-over-home shadow precedence with spyOn(readFile) proving the home path is never opened when project wins.
  • Manual reproduction on dev: placed a workflow in ~/.archon/workflows/, hit /workflows/builder?edit=<name> in the UI. Before patch: "Failed to load workflow (404)". After patch: 12-node DAG renders, Valid badge green.

Security Impact (required)

  • New permissions/capabilities? No — same readFile on .yaml files, different path.
  • New external network calls? No.
  • Secrets/tokens handling changed? No.
  • File system access scope changed? Yes, but narrowly: one additional readFile of ~/.archon/workflows/<sanitized-name>.yaml. The name is already validated by isValidCommandName (path-traversal guard, covered by test at line 189). No directory traversal or user-controlled paths beyond the existing guard.

Compatibility / Migration

  • Backward compatible? Yes — purely additive. A workflow that only existed as bundled or project before keeps the same source value and body.
  • Config/env changes? No.
  • Database migration needed? No.

Human Verification (required)

Verified scenarios:

  • Workflow only in ~/.archon/workflows/ → API returns source: 'global', UI builder loads it.
  • Same filename in project + home → project wins, home readFile is never called (spy-asserted).
  • Bundled default (archon-assist) still returns source: 'bundled' when no project/home match.
  • Invalid name (path traversal like ..secret) still 400 before any readFile (existing test at line 189, still passes).

Edge cases checked:

  • Home directory missing → ENOENT falls through to bundled (no 500).
  • Home YAML exists but unparseable → 500 with "Home workflow file is invalid: ..." (dedicated error branch).

What was not verified:

  • Docker path (/.archon) — only verified the macOS homedir path. getHomeWorkflowsPath() already handles both via getArchonHome(), so this should be inherited correctly.

Side Effects / Blast Radius (required)

  • Affected subsystems/workflows: only GET /api/workflows/:name (single-workflow fetch). No change to LIST, PUT, or any workflow-run path.
  • Potential unintended effects: if a user has a ~/.archon/workflows/<name>.yaml file with the same name as a bundled default, the home version now takes precedence over bundled (but still loses to project). This matches discoverWorkflowsWithConfig and the LIST endpoint — users already rely on this precedence for the list view, so it aligns the two views.
  • Guardrails/monitoring for early detection: existing name validation (isValidCommandName) still fires first; parse errors return 500 with a distinct log key (workflow.fetch_home_failed).

Rollback Plan (required)

  • Fast rollback command/path: revert the commit on api.ts (28 lines); the test additions are non-breaking and can stay or be reverted together.
  • Feature flags or config toggles: none — this is a straight bug fix, behavior-additive for anyone not using the home scope.
  • Observable failure symptoms: unexpected 500s with workflow.fetch_home_failed in logs would indicate malformed YAML in a home workflow — user-recoverable by fixing the file.

Risks and Mitigations

  • Risk: None significant. The change is ~28 lines, structurally identical to the existing project-scope branch, with dedicated test coverage and file-read-spy verification of shadow precedence.
    • Mitigation: dedicated 500 branch with workflow.fetch_home_failed log key; existing isValidCommandName guard unchanged.

Summary by CodeRabbit

  • New Features

    • Discover workflows from the user home directory (returned as source: "global"); project-scoped workflows continue to take precedence over home, which falls back to bundled defaults.
  • Bug Fixes

    • Malformed or unreadable home workflow files now produce a 500 error instead of silent failure.
  • Tests

    • Added tests covering home-scope discovery, malformed-home handling, precedence (project vs home), and avoiding unnecessary home-file reads.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 60fad2c0-8f80-4964-bde5-7f8dfb192714

📥 Commits

Reviewing files that changed from the base of the PR and between 668afda and f6620ea.

📒 Files selected for processing (2)
  • packages/server/src/routes/api.ts
  • packages/server/src/routes/api.workflows.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/server/src/routes/api.ts
  • packages/server/src/routes/api.workflows.test.ts

📝 Walkthrough

Walkthrough

The single-workflow fetch endpoint now checks for home-scoped workflow files at ~/.archon/workflows/<name>.yaml between project scope and bundled defaults; successful parses return source: 'global', ENOENT falls through, and other read/parse errors return 500. Import and inline comment ordering updated; tests added for success, parse error, and precedence.

Changes

Workflow Discovery Logic

Layer / File(s) Summary
Imports
packages/server/src/routes/api.ts
Adds getHomeWorkflowsPath to imports so the route can resolve home-scoped workflows.
Discovery Comment
packages/server/src/routes/api.ts
Clarifies the first discovery step as “project scope”.
Home Lookup Implementation
packages/server/src/routes/api.ts
Inserts a discovery step that reads/parses ~/.archon/workflows/<name>.yaml, returns source: 'global' on success, treats ENOENT as a miss, and surfaces read/parse errors as 500.
Bundled Step Relabel
packages/server/src/routes/api.ts
Re-labels bundled/defaults as the third discovery step after adding home lookup.
Tests — imports
packages/server/src/routes/api.workflows.test.ts
Adds spyOn import for spying on readFile in tests.
Tests — home-only case
packages/server/src/routes/api.workflows.test.ts
Adds test that returns a home-only workflow with source: "global" when no project match exists.
Tests — malformed home workflow
packages/server/src/routes/api.workflows.test.ts
Adds test that returns 500 when the home-scoped workflow YAML is invalid.
Tests — project precedence
packages/server/src/routes/api.workflows.test.ts
Adds test ensuring project-scoped workflow wins over home-scoped and that home file is not read when project match succeeds.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Server
  participant ProjectFS as Project FS
  participant HomeFS as Home FS
  participant Bundled as Bundled Defaults

  Client->>Server: GET /api/workflows/:name (optional cwd)
  alt cwd provided
    Server->>ProjectFS: check for project-scoped workflow
    alt project found
      ProjectFS-->>Server: return project workflow (source: project)
      Server-->>Client: 200 { workflow, source: "project" }
    else project not found
      Server->>HomeFS: read ~/.archon/workflows/:name.yaml
      alt home found & parsed
        HomeFS-->>Server: parsed workflow (source: global)
        Server-->>Client: 200 { workflow, source: "global" }
      else home not found
        Bundled-->>Server: return bundled workflow
        Server-->>Client: 200 { workflow, source: "bundled" }
      end
    end
  else no cwd
    Server->>HomeFS: read ~/.archon/workflows/:name.yaml
    alt home found & parsed
      HomeFS-->>Server: parsed workflow (source: global)
      Server-->>Client: 200 { workflow, source: "global" }
    else home not found
      Bundled-->>Server: return bundled workflow
      Server-->>Client: 200 { workflow, source: "bundled" }
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

  • coleam00/Archon#959: Alters fallback behavior for GET /api/workflows/:name and relates to bundled/project/home resolution.

Poem

🐰
I hop through dirs to find a plan,
From project burrows to my global den,
I parse the YAML, tidy and bright,
Return it home, if found in sight,
Sniff, skip, and serve — a rabbit's delight!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding home-scoped workflow lookup to the GET /api/workflows/:name endpoint that was previously missed.
Description check ✅ Passed The description comprehensively covers all required template sections: problem, why it matters, what changed, scope boundaries, UX journey with before/after flows, detailed architecture diagrams with connection inventory, all metadata labels, validation evidence with test runs, security impact analysis, compatibility assessment, human verification of edge cases, side effects/blast radius, and rollback plan.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/server/src/routes/api.workflows.test.ts (1)

293-329: Shadow precedence test — consider strengthening the "home not attempted" assertion.

The assertion body.source === 'project' proves step 1 returned successfully, which implies step 2 was skipped given the current code path — but because parseWorkflow is globally mocked to return a fixed workflow regardless of input content, the test cannot actually distinguish whether the home file was opened. If a future refactor were to accidentally read both files, this test would still pass.

Optional hardening: spy on readFile (or inspect logger calls) and assert the home path was not accessed. Not a blocker — the current assertion is still correct for the current implementation.

Optional: stricter assertion via readFile spy
+      const readFileSpy = spyOn(await import('fs/promises'), 'readFile');
       mockListCodebases.mockImplementationOnce(async () => [{ default_cwd: testDir }]);
       const response = await app.request(`/api/workflows/shared?cwd=${testDir}`);
       expect(response.status).toBe(200);
       const body = (await response.json()) as { source: string };
-      // Project must shadow home — home lookup should not even be attempted.
       expect(body.source).toBe('project');
+      // Assert no readFile call targeted the home path
+      const homePath = join(homeWorkflowsDir, 'shared.yaml');
+      expect(readFileSpy.mock.calls.some(args => String(args[0]) === homePath)).toBe(false);
+      readFileSpy.mockRestore();

Requires adding spyOn to the bun:test import.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/server/src/routes/api.workflows.test.ts` around lines 293 - 329,
Test currently only checks body.source === 'project' but parseWorkflow is
globally mocked so the test can’t detect if the home file was actually read; add
a spy on the file reader and assert the home path was never accessed.
Specifically, in the test 'project-scope shadows home-scope when same filename
exists in both' (where parseWorkflow is mocked), create a spy (via spyOn from
bun:test or your test framework) on fs/promises.readFile (or the file-read
helper used by the workflows code) before calling the API, then assert the spy
was not called with the home workflow path (the join(homeWorkflowsDir,
'shared.yaml') value) and that it was called with the project path if desired;
keep restoring/removing the spy in the finally block. This ensures
parseWorkflow’s mock can’t mask an unintended read of the home file.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/server/src/routes/api.workflows.test.ts`:
- Around line 293-329: Test currently only checks body.source === 'project' but
parseWorkflow is globally mocked so the test can’t detect if the home file was
actually read; add a spy on the file reader and assert the home path was never
accessed. Specifically, in the test 'project-scope shadows home-scope when same
filename exists in both' (where parseWorkflow is mocked), create a spy (via
spyOn from bun:test or your test framework) on fs/promises.readFile (or the
file-read helper used by the workflows code) before calling the API, then assert
the spy was not called with the home workflow path (the join(homeWorkflowsDir,
'shared.yaml') value) and that it was called with the project path if desired;
keep restoring/removing the spy in the finally block. This ensures
parseWorkflow’s mock can’t mask an unintended read of the home file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9d8c8821-1f4f-4014-95d0-4835f99f3954

📥 Commits

Reviewing files that changed from the base of the PR and between 9122673 and 1d4869b.

📒 Files selected for processing (2)
  • packages/server/src/routes/api.ts
  • packages/server/src/routes/api.workflows.test.ts

lraphael pushed a commit to lraphael/Archon that referenced this pull request Apr 24, 2026
Address CodeRabbit nitpick on coleam00#1405: `parseWorkflow` is globally mocked,
so asserting `body.source === 'project'` alone cannot catch a regression
that reads both files before deciding. Add a `spyOn(fs.readFile)` that
fails if the home path was ever opened.

Restores the spy in finally so other tests aren't affected.
@Wirasm
Copy link
Copy Markdown
Collaborator

Wirasm commented Apr 27, 2026

Hi @lraphael — thanks for opening this PR.

This repository uses a PR template at .github/pull_request_template.md with several required sections. A few of them appear to be empty or placeholder here:

  • UX Journey
  • Architecture Diagram
  • Label Snapshot
  • Change Metadata

Could you fill those out (even briefly)? The template helps reviewers understand scope, risk, and rollback — it speeds up review significantly.

If a section genuinely doesn't apply, just write "N/A" in it rather than leaving it blank.

@Wirasm
Copy link
Copy Markdown
Collaborator

Wirasm commented Apr 27, 2026

@lraphael related to #1138 — overlapping area or partial fix.

@lraphael
Copy link
Copy Markdown
Contributor Author

Thanks for the review @Wirasm — added UX Journey, Architecture Diagram, Label Snapshot, and Change Metadata sections. Let me know if any of them need more detail.

Raphael Lechner added 3 commits May 9, 2026 16:36
The single-workflow endpoint searched only project-scope, bundled, and
filesystem defaults — skipping `~/.archon/workflows/` entirely. This
made global workflows invisible to the web UI builder (which loads via
this endpoint) even though `GET /api/workflows` (list) surfaced them
correctly after PR coleam00#1315.

Adds a home-scope lookup between project and bundled tiers, matching
`discoverWorkflowsWithConfig`. Uses `source: 'global'` (already in the
`WorkflowSource` union).

Reproduces the bug: create a workflow in `~/.archon/workflows/`, then
open `/workflows/builder?edit=<name>` → 404 "Workflow not found".
After this fix: loads correctly with `source: "global"`.

Related to coleam00#1138.
Two new tests for GET /api/workflows/:name:

- home-scope hit: file in ~/.archon/workflows/, no project match
  → returns source: 'global'
- shadow: same filename in both project and home
  → project wins, home is not even attempted

Uses ARCHON_HOME env var to redirect home lookups to a tmpdir during
the test. Restores the prior value in finally so test order doesn't
leak state.
Address CodeRabbit nitpick on coleam00#1405: `parseWorkflow` is globally mocked,
so asserting `body.source === 'project'` alone cannot catch a regression
that reads both files before deciding. Add a `spyOn(fs.readFile)` that
fails if the home path was ever opened.

Restores the spy in finally so other tests aren't affected.
@lraphael lraphael force-pushed the fix/get-workflow-home-scope branch from 110df38 to 668afda Compare May 9, 2026 14:36
@lraphael
Copy link
Copy Markdown
Contributor Author

lraphael commented May 9, 2026

Hi @Wirasm @coleam00 — gentle bump on this one.

I just hit the symptom in current dev while running an archon-plan-to-mr workflow from the web UI: /workflows/runs/<id> showed "Loading graph..." indefinitely. After cherry-picking this branch into a local merge, the graph rendered (GET /api/workflows/archon-plan-to-mr → 200 instead of 404).

Re: relation to #1138 / #1257: this is complementary, not overlapping.

Both are needed for the web UI to fully work with home-scoped workflows. The investigation in #1138 caught the list path but missed the singular :name route. Happy to either keep them as two PRs (review independently, merge #1257 first then I'll rebase this one) or consolidate into one if that's cleaner.

Quick status update:

  • Rebased onto current dev just now — clean, MERGEABLE again
  • PR template sections added on Apr 27 per earlier feedback

Thanks!

@Wirasm
Copy link
Copy Markdown
Collaborator

Wirasm commented May 11, 2026

Review Summary

Verdict: minor-fixes-needed

Your PR cleanly completes the discovery-order mirror for GET /api/workflows/:name — adding home-scope lookup between project-scope and bundled defaults. The code, error handling, and structured logging are all consistent with existing patterns. The two new integration tests are solid, especially the readFile spy that verifies shadow precedence at the filesystem-call level. A couple of small improvements before merge:

Suggested fixes

  • packages/server/src/routes/api.ts:~2269 — Add a test for the 500 error path when a home-scoped workflow file is corrupt (malformed YAML). Something like:

    // In a test parallel to "returns home-scoped workflow with source:global when project/bundled miss":
    fs.writeFileSync(homeWorkflowPath, 'invalid: [yaml');
    const response = await server.fetch(...);
    expect(response.status).toBe(500);
    expect((await response.json()).error).toContain('Home workflow file is invalid');
  • packages/server/src/routes/api.ts:2264-2268 — The multi-line comment on the home-scope fallback references why the bug was added ("previously skipped") and mentions the web UI builder. That's historical noise that will be confusing to future readers. Shorten it to:

    // 2. Fall back to home-scoped workflow (`~/.archon/workflows/`).
    // Mirrors the discovery order in `discoverWorkflowsWithConfig`.

    The discoverWorkflowsWithConfig reference is the useful anchor — it ties this endpoint to the canonical discovery logic.

Minor / nice-to-have

  • api.ts:2241: The (project scope) annotation is a WHAT restatement. Optional: drop it.
  • api.ts:2265: The (binary: embedded map; dev: also check filesystem) parenthetical is implementation trivia. Optional: // 3. Fall back to bundled defaults.

Compliments

  • readFile spy approach in the shadow-precedence test is well done — catching the regression risk at the filesystem-call level (not just at the return-value level) is exactly the right way to verify shadow ordering.
  • PR description is thorough with before/after diagrams, validation evidence, security analysis, and rollback plan.

Reviewed via maintainer-review-pr workflow (Pi/Minimax). Aspects run: code-review, error-handling, test-coverage, comment-quality, docs-impact.

- Shorten home-scope fallback comment to point at `discoverWorkflowsWithConfig`
  as the canonical discovery anchor; drop historical "previously skipped" and
  web-UI builder noise.
- Drop "(project scope)" / bundled-defaults parentheticals — both restate WHAT
  the code already says.
- Add 500-path test: malformed home-scoped YAML returns 500 with
  "Home workflow file is invalid" error message.
@lraphael
Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review, @Wirasm — all four points addressed in f6620ea:

Suggested fixes

  • ✅ Added the 500-path test for malformed home-scoped YAML. Since parseWorkflow is module-mocked in this suite, the test writes invalid: [yaml to the home path and primes mockParseWorkflow.mockReturnValueOnce({ error: ... }) so the error surfaces through the route exactly as it would in production. Asserts status 500 + "Home workflow file is invalid".
  • ✅ Shortened the home-scope fallback comment to point at discoverWorkflowsWithConfig as the canonical anchor — dropped the historical "previously skipped" / web-UI-builder context.

Minor / nice-to-have

  • ✅ Dropped (project scope) annotation on step 1.
  • ✅ Dropped (binary: embedded map; dev: also check filesystem) parenthetical on step 3.

Test run: bun test packages/server/src/routes/api.workflows.test.ts → 30 pass / 0 fail.

Appreciate the callout on the readFile spy approach — agreed that catching shadow-precedence at the filesystem-call level is the right granularity. Ready for another look whenever it suits you.

@Wirasm Wirasm merged commit b580ca0 into coleam00:dev May 12, 2026
4 checks passed
@Wirasm Wirasm mentioned this pull request May 12, 2026
cropse pushed a commit to cropse/Archon that referenced this pull request May 19, 2026
…oleam00#1405)

* fix(server): GET /api/workflows/:name missed home-scoped workflows

The single-workflow endpoint searched only project-scope, bundled, and
filesystem defaults — skipping `~/.archon/workflows/` entirely. This
made global workflows invisible to the web UI builder (which loads via
this endpoint) even though `GET /api/workflows` (list) surfaced them
correctly after PR coleam00#1315.

Adds a home-scope lookup between project and bundled tiers, matching
`discoverWorkflowsWithConfig`. Uses `source: 'global'` (already in the
`WorkflowSource` union).

Reproduces the bug: create a workflow in `~/.archon/workflows/`, then
open `/workflows/builder?edit=<name>` → 404 "Workflow not found".
After this fix: loads correctly with `source: "global"`.

Related to coleam00#1138.

* test(server): cover home-scope lookup + project/home precedence

Two new tests for GET /api/workflows/:name:

- home-scope hit: file in ~/.archon/workflows/, no project match
  → returns source: 'global'
- shadow: same filename in both project and home
  → project wins, home is not even attempted

Uses ARCHON_HOME env var to redirect home lookups to a tmpdir during
the test. Restores the prior value in finally so test order doesn't
leak state.

* test(server): harden shadow test — assert home readFile is never called

Address CodeRabbit nitpick on coleam00#1405: `parseWorkflow` is globally mocked,
so asserting `body.source === 'project'` alone cannot catch a regression
that reads both files before deciding. Add a `spyOn(fs.readFile)` that
fails if the home path was ever opened.

Restores the spy in finally so other tests aren't affected.

* review(server): address Wirasm feedback on PR coleam00#1405

- Shorten home-scope fallback comment to point at `discoverWorkflowsWithConfig`
  as the canonical discovery anchor; drop historical "previously skipped" and
  web-UI builder noise.
- Drop "(project scope)" / bundled-defaults parentheticals — both restate WHAT
  the code already says.
- Add 500-path test: malformed home-scoped YAML returns 500 with
  "Home workflow file is invalid" error message.

---------

Co-authored-by: Raphael Lechner <raphael.l@asix.pro>
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.

2 participants