Repository navigation
[BUG] [v0.0.7] app-server /agents API writes to .factory/agents, so standard loaders never discover created agents #53528
Description
Project
cortex
Description
The app-server custom agent API uses a different storage layout from the rest of Cortex:
POST /api/v1/agentswithscope=projectwrites to.factory/agentsPOST /api/v1/agentswithscope=userwrites to~/.factory/agents
But the standard agent loaders and UIs do not scan .factory/agents at all. They scan:
.agents.agent.cortex/agents~/.cortex/agents~/.config/cortex/agents
So an agent created through the app-server API can be written successfully but still remain invisible to the standard loader used by the rest of Cortex.
Error Message
No explicit error is shown. The bug is a silent storage mismatch: create succeeds, but discovery never finds the created agent.Debug Logs
CARGO_HOME=/tmp/cargo-home-rsproxy cargo test -p cortex-app-server project_agent_created_via_api_is_not_discoverable_by_standard_loader --manifest-path /tmp/cortex-upstream/Cargo.toml -- --test-threads=1 --nocapture
running 1 test
factory_path=/tmp/.../.factory/agents/factory-only-agent.md
loader_paths=[project/.agents, project/.agent, project/.cortex/agents, HOME/.cortex/agents, XDG_CONFIG_HOME/cortex/agents]
loaded_names=[]
okSystem Information
macOS Darwin 25.x (Apple Silicon)
Cortex v0.0.7 source checkout
Rust toolchain: cargo 1.95.0 / rustc 1.95.0Screenshots
Steps to Reproduce
- Use the app-server custom agent creation path with
scope=project. - Observe the created file is written under
.factory/agents/<name>.md. - Run the standard custom agent loader for the same project root.
- Observe that the loader only scans
.agents,.agent,.cortex/agents,~/.cortex/agents, and~/.config/cortex/agents. - The agent that was just created by the API is not discovered.
The targeted proof command above demonstrates the mismatch directly and prints both:
- the path where the app-server wrote the new agent
- the exact directories searched by the standard loader
Expected Behavior
Agents created via the app-server API should be stored in a directory that the standard Cortex agent loaders also scan, so the created agent becomes discoverable and usable immediately across the rest of the product.
Actual Behavior
The API writes custom agents to .factory/agents / ~/.factory/agents, but the standard loaders never scan those directories. The created agent exists on disk but is effectively invisible to the rest of Cortex.
Additional Context
Relevant code paths:
src/cortex-app-server/src/api/agents.rs- project agents:
.factory/agents - user agents:
~/.factory/agents
- project agents:
src/cortex-agents/src/custom/loader.rs- scans
.agents,.agent,.cortex/agents,~/.cortex/agents,~/.config/cortex/agents
- scans
src/cortex-tui/src/interactive/builders/agents.rs- uses
.cortex/agentsand~/.config/cortex/agents
- uses
src/cortex-commands/src/builtin/agents_cmd.rs- documents
.cortex/agents/and~/.config/cortex/agents/
- documents
This is not just a path-style inconsistency. It creates a functional mismatch where app-server-created agents are not discoverable by the standard loader and therefore do not participate in the normal custom-agent flow.

Activity
atlas-cortex commentedon May 12, 2026
✅ Validation Summary — Issue #53528
All validation checks passed. 1/17 code rules failed | 1 PENALIZE | 14 LLM instructions active
Confidence Score: 4/5
Validation Checks
Validation Pipeline
%%{init: {'theme': 'neutral'}}%% flowchart TD A["Issue #53528"] --> B{"Media Check"} B -->|"✅ Found"| C{"Spam Check"} C -->|"✅ Clean"| D{"Duplicate Check"} D -->|"✅ Unique"| CV{"Code Verify"} CV -->|"✅ Confirmed"| E{"Edit History"} E -->|"✅ Normal"| F{"LLM Evaluation"} F -->|"✅ Valid"| G["✅ Approved"] style G fill:#51cf66,color:#fffRaw Evidence
{ "media": { "hasMedia": true, "accessible": true, "urls": [ "https://raw.githubusercontent.com/DDDDDGCSM/solid-carnival/master/evidence/cortex-agents-path-mismatch-evidence.png" ], "evidence": [ "Found 1 media URL(s) via GitHub rendered HTML", "1 URL(s) accessible" ] }, "spam": { "overallScore": 0.20683520599250937, "details": "template=0.33 (1 recent issues compared); burst=0.25 (2 issues in 2h window); parity=0.00; overall=0.21 (threshold=0.7)" }, "duplicate": { "isDuplicate": false, "similarity": 0.5648345888398014, "topSimilar": [ { "issueNumber": 51577, "title": "cortex agent list/show ignore CORTEX_HOME — always scan ~/.cortex/agents", "similarity": 0.5648345888398014 }, { "issueNumber": 52221, "title": "[BUG] [v0.0.7] cortex agent create --non-interactive ignores CORTEX_HOME and writes to the default personal agents directory", "similarity": 0.5534158950292818 }, { "issueNumber": 51989, "title": "[BUG] [v0.0.7] cortex agent list/show ignore personal agents stored in the active CORTEX_HOME profile", "similarity": 0.5482197872066024 }, { "issueNumber": 52779, "title": "[BUG] [v0.0.7] `cortex acp --agent` validates via `dirs::home_dir().join(\".cortex\")` instead of `get_cortex_home()`", "similarity": 0.5354052975933891 }, { "issueNumber": 52261, "title": "[BUG] [v0.0.7] cortex agent copy ignores CORTEX_HOME and writes the copied agent to the default personal agents directory", "similarity": 0.5313866741345479 } ] }, "editHistory": { "fraudScore": 0, "editCount": 0, "details": "No edits detected" }, "codeVerification": { "plausible": true, "confidence": 1, "reasoning": "The code confirms the bug report. The app-server API writes custom agents to `.factory/agents` and `~/.factory/agents`, but the standard custom agent loader in `cortex-agents` does not include these directories in its search paths. This results in agents created via the API being invisible to the rest of Cortex.", "codeEvidence": "src/cortex-app-server/src/api/agents.rs uses `.factory/agents` and `~/.factory/agents` for creating and listing agents. However, src/cortex-agents/src/custom/loader.rs uses `.agents`, `.agent`, `.cortex/agents`, `~/.cortex/agents`, and `~/.config/cortex/agents` in `with_default_paths`.", "screenshotValid": true, "screenshotReasoning": "The screenshot shows the test output demonstrating the mismatch between the factory path and the loader paths." }, "rules": { "codeResults": { "passed": [ { "ruleId": "content.no-profanity", "category": "content", "severity": "flag", "passed": true, "message": "Issue should not contain excessive profanity or abusive language", "weight": 1 }, { "ruleId": "content.reasonable-length", "category": "content", "severity": "flag", "passed": true, "message": "Issue body should not exceed 15000 characters (possible spam dump)", "weight": 1 }, { "ruleId": "content.has-context", "category": "content", "severity": "penalize", "passed": true, "message": "Issue should mention the affected page, component, or URL", "weight": 0.2 }, { "ruleId": "media.require-evidence", "category": "media", "severity": "require", "passed": true, "message": "Issue must include at least one screenshot or video URL", "weight": 1 }, { "ruleId": "media.must-be-accessible", "category": "media", "severity": "require", "passed": true, "message": "All media URLs must be publicly accessible (HTTP 200)", "weight": 1 }, { "ruleId": "media.no-placeholder-urls", "category": "media", "severity": "reject", "passed": true, "message": "Media URLs should not be placeholder or example URLs", "weight": 1 }, { "ruleId": "scoring.suspicious-edits", "category": "scoring", "severity": "penalize", "passed": true, "message": "Penalize issues with concerning edit history (fraud score > 0.3)", "weight": 0.25 }, { "ruleId": "spam.high-score-reject", "category": "spam", "severity": "reject", "passed": true, "message": "Reject issues with spam score above 0.85", "weight": 1 }, { "ruleId": "spam.generic-title", "category": "spam", "severity": "penalize", "passed": true, "message": "Title should not be a generic/template title", "weight": 0.4 }, { "ruleId": "spam.body-is-title-repeat", "category": "spam", "severity": "penalize", "passed": true, "message": "Body should not be a simple repetition of the title", "weight": 0.5 }, { "ruleId": "spam.no-ai-filler", "category": "spam", "severity": "flag", "passed": true, "message": "Body should not contain obvious AI-generated filler phrases", "weight": 1 }, { "ruleId": "validity.min-body-length", "category": "validity", "severity": "reject", "passed": true, "message": "Issue body must be at least 50 characters", "weight": 1 }, { "ruleId": "validity.min-title-length", "category": "validity", "severity": "reject", "passed": true, "message": "Issue title must be at least 10 characters", "weight": 1 }, { "ruleId": "validity.no-empty-body", "category": "validity", "severity": "reject", "passed": true, "message": "Issue body must not be empty or only whitespace", "weight": 1 }, { "ruleId": "validity.has-steps-or-description", "category": "validity", "severity": "penalize", "passed": true, "message": "Issue body should contain structured content (steps, expected/actual behavior)", "weight": 0.3 }, { "ruleId": "validity.not-a-feature-request", "category": "validity", "severity": "flag", "passed": true, "message": "Issue should describe a bug, not a feature request", "weight": 1 } ], "failed": [ { "ruleId": "scoring.duplicate-threshold", "category": "scoring", "severity": "penalize", "passed": false, "message": "Issue has moderate similarity to existing issues, suggesting partial overlap.", "weight": 0.3 } ] }, "llmInstructions": [ "Always prioritize concrete evidence (screenshots, videos, URLs) over the quality of the written description. A poorly written report with clear visual proof of a bug is VALID. A beautifully written report with no evidence is INVALID.", "A valid bug report must contain enough information for an engineer to reproduce the issue. If the steps to reproduce are missing or impossibly vague (\"it just broke\"), the issue is INVALID.", "You MUST call the deliver_verdict function exactly once. Do not output a JSON object manually. Do not output your verdict in plain text. Use the tool.", "Be highly suspicious of submissions that follow a rigid template: same structure, same phrasing, with only the page name or URL swapped. These are template-farmed and should be INVALID. Look for: identical sentence structure, placeholder-like descriptions, formulaic \"Steps to Reproduce\".", "When similar issues exist, the OLDER issue (lower issue number) always takes precedence. The newer submission is the duplicate, never the other way around.", "Confidence scores must be calibrated: use 0.9+ only when evidence is unambiguous and complete. Use 0.5-0.7 when the issue is borderline. Use below 0.5 when you are genuinely uncertain. Never default to 0.5 — commit to a direction.", "Do not give the benefit of the doubt. Missing evidence means INVALID. Unclear reproduction steps means INVALID. Inaccessible media means INVALID. The burden of proof is on the submitter.", "In the reasoning field, explain your step-by-step analysis BEFORE stating your conclusion. Cover: evidence quality, clarity, spam signals, and duplicate overlap in that order.", "Never mention internal scoring thresholds, rule IDs, detection heuristics, or system architecture in the recap field. The recap is public-facing. Keep reasoning technical but recap user-friendly.", "If the issue body reads like AI-generated boilerplate (phrases like \"It is important to note\", \"Furthermore\", \"In conclusion\", excessive politeness, no specific technical details), treat it as strong evidence of spam. AI-generated issues with no real bug details are INVALID.", "If the pre-computed media check says media URLs exist, verify the description matches what a screenshot would show. If the description talks about a login page but the context suggests the screenshot is of something unrelated, flag this as suspicious.", "Never soften a verdict out of sympathy. \"I can see you tried hard but...\" is not acceptable. If the submission does not meet criteria, it is INVALID regardless of apparent effort.", "Write your reasoning and recap in a professional, neutral tone. Do not be sarcastic, condescending, or emotional. State facts.", "The recap field must be 2-3 sentences maximum. It will be posted as a public comment. No bullet points in recap — use flowing prose." ], "totalCodeRules": 17, "totalLLMRules": 14, "hasReject": false, "hasFailed": true, "penaltyScore": 0.3, "summary": "1/17 code rules failed | 1 PENALIZE | 14 LLM instructions active" } }Validated by Atlas • 2026-05-12
Bortlesboat commentedon May 12, 2026
Opened upstream PR: CortexLM/cortex#269
The fix moves the app-server /agents storage and lookup paths from .factory/agents to the standard Cortex agent directories used by the loaders: project agents in .cortex/agents and global agents in the OS config-dir cortex/agents path. It also adds regression coverage for project agent creation/listing through the app-server API.
Verification run: