Move trajectory storage under .agentworkforce#28
Conversation
📝 WalkthroughWalkthroughThis PR migrates the default trajectory storage location from ChangesStorage directory migration and default path management
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add 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. Comment |
There was a problem hiding this comment.
2 issues found across 139 files
Note: This PR contains a large number of files. cubic only reviews up to 100 files per PR, so some files may not have been reviewed. cubic prioritizes the most important files to review.
On a pro plan you can use ultrareview for larger PRs.
Re-trigger cubic
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a552c80c04
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
|
|
||
| return [join(process.cwd(), ".trajectories")]; | ||
| return [getDefaultTrajectoryDataDir()]; |
There was a problem hiding this comment.
Include legacy data dir in default search fallback
getSearchPaths() now falls back only to .agentworkforce/trajectories, but trail compact code paths (loadTrajectories, getCompactedTrajectoryIds, discardSourceTrajectories) skip non-existent search paths before calling FileStorage.initialize(). In repos that still only have legacy .trajectories/, this means nothing is scanned and migration never runs, so compaction-related commands silently act as if there are no trajectories on first run after upgrade.
Useful? React with 👍 / 👎.
| if isDefaultDataDir || hasTrajectoryChildren { | ||
| resolvedTrajectoryPath = trajectoryPath | ||
| } else { | ||
| environment["TRAJECTORIES_DATA_DIR"] = trajectoryPath | ||
| resolvedTrajectoryPath = trajDirURL.path |
There was a problem hiding this comment.
Keep legacy repo-root detection in viewer path resolver
When the viewer is pointed at a repository root that still uses .trajectories/, this logic rewrites TRAJECTORIES_DATA_DIR to <root>/.agentworkforce/trajectories unless the selected directory already looks like a data dir. A legacy repo root does not match those checks, so the server starts against a non-existent directory and existing trajectories disappear from the UI until users manually pick the legacy folder.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@PROPOSAL-trajectories.md`:
- Line 1139: Update the fenced code block showing the directory tree for
".agentworkforce/trajectories/" by adding a language identifier (e.g., ```text)
to the opening fence so Markdownlint MD040 is satisfied; locate the fenced block
that begins with the line ".agentworkforce/trajectories/" and change the opening
``` to include the language token (such as text) so the block becomes fenced
with ```text.
In `@trail-viewer/server/src/trajectory-service.ts`:
- Around line 92-94: The code builds trajDir by blindly appending
DEFAULT_TRAJECTORY_DATA_DIR to this.dataDir which double-nests when this.dataDir
already points to the trajectory directory; change the resolver to pick the base
dir = process.env.TRAJECTORIES_DATA_DIR ?? this.dataDir and then only append
DEFAULT_TRAJECTORY_DATA_DIR if the base's final path segment is not already
DEFAULT_TRAJECTORY_DATA_DIR (e.g. check path.basename(base) !==
DEFAULT_TRAJECTORY_DATA_DIR), then set trajDir = base or join(base,
DEFAULT_TRAJECTORY_DATA_DIR) and compute completedDir from trajDir; apply the
same change in rebuildIndex and getTrajectory to use the same resolution logic.
- Around line 19-33: The resolver currently only recognizes
DEFAULT_TRAJECTORY_DATA_DIR and will incorrectly redirect repo roots that still
use the legacy "./.trajectories" folder; update isTrajectoryDataDir and
resolveTrajectoryDataDir to treat the legacy name as a valid data dir. Modify
isTrajectoryDataDir to also detect the legacy folder (e.g., check
existsSync(join(path, ".trajectories")) or
normalized.endsWith(".trajectories")), and change resolveTrajectoryDataDir to
return the legacy path when that folder exists on the repo root (before falling
back to join(path, DEFAULT_TRAJECTORY_DATA_DIR)), using the existing function
names isTrajectoryDataDir and resolveTrajectoryDataDir for locating where to
apply the change.
In `@trail-viewer/Sources/Services/LocalServerManager.swift`:
- Around line 125-146: The code in LocalServerManager.swift determines
TRAJECTORIES_DATA_DIR using selected, trajDirURL, isDefaultDataDir,
hasActiveDir, hasCompletedDir and falls back to trajDirURL.path, but it doesn't
detect legacy repos that have a top-level ".trajectories" directory; add a check
for selected.appendingPathComponent(".trajectories", isDirectory: true) (e.g.,
let hasLegacyTrajectories = FileManager.default.fileExists(atPath:
selected.appendingPathComponent(".trajectories").path)) and incorporate it into
the resolution logic so that if hasLegacyTrajectories is true you set
resolvedTrajectoryPath to selected.appendingPathComponent(".trajectories").path
(preserving the existing behavior for isDefaultDataDir or hasTrajectoryChildren
otherwise) before assigning environment["TRAJECTORIES_DATA_DIR"].
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 106e2527-a0df-4d88-96bb-02bdb89fa665
📒 Files selected for processing (140)
.agentworkforce/trajectories/active/trace_imxymv6f8x4b.json.agentworkforce/trajectories/active/traj_1775656221707_fbafbb4c.json.agentworkforce/trajectories/completed/2026-01/traj_cuuwpd2q5rr4.json.agentworkforce/trajectories/completed/2026-01/traj_cuuwpd2q5rr4.md.agentworkforce/trajectories/completed/2026-01/traj_cuuwpd2q5rr4.trace.json.agentworkforce/trajectories/completed/2026-02/traj_gtzye0t83h5a.json.agentworkforce/trajectories/completed/2026-02/traj_gtzye0t83h5a.md.agentworkforce/trajectories/completed/traj_1775579377702_4f7f9060.json.agentworkforce/trajectories/completed/traj_1775579377702_d7c0960f.json.agentworkforce/trajectories/completed/traj_1775579377703_10fca7cd.json.agentworkforce/trajectories/completed/traj_1775579517021_75d423d8.json.agentworkforce/trajectories/completed/traj_1775579517022_56a8cb51.json.agentworkforce/trajectories/completed/traj_1775579517022_f0443bc4.json.agentworkforce/trajectories/completed/traj_1775579517024_ea9370c2.json.agentworkforce/trajectories/completed/traj_1775579684459_bcfd075c.json.agentworkforce/trajectories/completed/traj_1775579684464_082ed115.json.agentworkforce/trajectories/completed/traj_1775579684464_c956bacd.json.agentworkforce/trajectories/completed/traj_1775579684472_6903a2a5.json.agentworkforce/trajectories/completed/traj_1775579684472_fdc465b2.json.agentworkforce/trajectories/completed/traj_1775579684476_a9b5c6b9.json.agentworkforce/trajectories/completed/traj_1775579883001_419efbfc.json.agentworkforce/trajectories/completed/traj_1775579883001_f31aa87b.json.agentworkforce/trajectories/completed/traj_1775579883004_197f89a2.json.agentworkforce/trajectories/completed/traj_1775579883005_c0fcdbcb.json.agentworkforce/trajectories/completed/traj_1775580030112_0bf91d39.json.agentworkforce/trajectories/completed/traj_1775580030115_38fdd60c.json.agentworkforce/trajectories/completed/traj_1775580030115_6ae8429d.json.agentworkforce/trajectories/completed/traj_1775580030115_ec4e7106.json.agentworkforce/trajectories/completed/traj_1775580241282_2cf5e535.json.agentworkforce/trajectories/completed/traj_1775580241282_2d95f122.json.agentworkforce/trajectories/completed/traj_1775580241282_b0bf263f.json.agentworkforce/trajectories/completed/traj_1775580241287_3c613873.json.agentworkforce/trajectories/completed/traj_1775580409392_3bf04f3e.json.agentworkforce/trajectories/completed/traj_1775580409397_48a4a2ba.json.agentworkforce/trajectories/completed/traj_1775580409400_2fc46843.json.agentworkforce/trajectories/completed/traj_1775580409401_2cfac72a.json.agentworkforce/trajectories/completed/traj_1775580409403_e09bc229.json.agentworkforce/trajectories/completed/traj_1775580587438_bf5ed89a.json.agentworkforce/trajectories/completed/traj_1775580587441_5dac7a03.json.agentworkforce/trajectories/completed/traj_1775580587441_8253d528.json.agentworkforce/trajectories/completed/traj_1775580587445_c0a442b5.json.agentworkforce/trajectories/completed/traj_1775580824527_e441d22f.json.agentworkforce/trajectories/completed/traj_1775581182381_f4b36b94.json.agentworkforce/trajectories/completed/traj_1775581182382_04a32af5.json.agentworkforce/trajectories/completed/traj_1775581182382_b0db75c5.json.agentworkforce/trajectories/completed/traj_1775581547975_63b4aab0.json.agentworkforce/trajectories/completed/traj_1775581806172_0bfbf365.json.agentworkforce/trajectories/completed/traj_1775582004453_91203e94.json.agentworkforce/trajectories/completed/traj_1775582241963_3a261e62.json.agentworkforce/trajectories/completed/traj_1775582692229_21d871cb.json.agentworkforce/trajectories/completed/traj_1775582848710_1c41743b.json.agentworkforce/trajectories/completed/traj_1775582995462_aa15ac06.json.agentworkforce/trajectories/completed/traj_1775583188684_0f5f6c47.json.agentworkforce/trajectories/completed/traj_1775583188688_d4d12d64.json.agentworkforce/trajectories/completed/traj_1775583188695_5f6d0186.json.agentworkforce/trajectories/completed/traj_1775583188696_a2babaa9.json.agentworkforce/trajectories/completed/traj_1775583188698_4500ed85.json.agentworkforce/trajectories/completed/traj_1775583478300_8b5d0d45.json.agentworkforce/trajectories/completed/traj_1775587484181_8d5f9706.json.agentworkforce/trajectories/completed/traj_1775587484181_ade8f314.json.agentworkforce/trajectories/completed/traj_1775587484181_f47c4bb8.json.agentworkforce/trajectories/completed/traj_1775587754098_777acf8b.json.agentworkforce/trajectories/completed/traj_1775587754098_99381421.json.agentworkforce/trajectories/completed/traj_1775587754099_2e24aa44.json.agentworkforce/trajectories/completed/traj_1775587942028_d7c657ee.json.agentworkforce/trajectories/completed/traj_1775587942028_edac5455.json.agentworkforce/trajectories/completed/traj_1775587942029_ae2b3a94.json.agentworkforce/trajectories/completed/traj_1775587942032_8e4dc570.json.agentworkforce/trajectories/completed/traj_1775588202995_1c36a335.json.agentworkforce/trajectories/completed/traj_1775588319448_b188885f.json.agentworkforce/trajectories/completed/traj_1775588443144_269a6c7b.json.agentworkforce/trajectories/completed/traj_1775588658881_9013c72e.json.agentworkforce/trajectories/completed/traj_1775588795733_442cdf75.json.agentworkforce/trajectories/completed/traj_1775588982963_ae82593f.json.agentworkforce/trajectories/completed/traj_1775589139752_34e57999.json.agentworkforce/trajectories/completed/traj_1775589353412_b854336d.json.agentworkforce/trajectories/completed/traj_1775589539846_210c5b03.json.agentworkforce/trajectories/completed/traj_1775589539846_468c4604.json.agentworkforce/trajectories/completed/traj_1775589539847_fcc1e72b.json.agentworkforce/trajectories/completed/traj_1775589539853_64dd4d34.json.agentworkforce/trajectories/completed/traj_1775589892159_2d684c41.json.agentworkforce/trajectories/completed/traj_1775589892159_7437e0b3.json.agentworkforce/trajectories/completed/traj_1775589892160_65fa5491.json.agentworkforce/trajectories/completed/traj_1775589892162_7ee958ef.json.agentworkforce/trajectories/completed/traj_1775590064833_71520399.json.agentworkforce/trajectories/completed/traj_1775590064834_91431f8f.json.agentworkforce/trajectories/completed/traj_1775590554286_8f755680.json.agentworkforce/trajectories/completed/traj_1775590554286_bbbf5e8e.json.agentworkforce/trajectories/completed/traj_1775590554287_78bfeb4f.json.agentworkforce/trajectories/completed/traj_1775592035208_089f3045.json.agentworkforce/trajectories/completed/traj_1775592035208_59779aa9.json.agentworkforce/trajectories/completed/traj_1775592035208_9e1d2cdd.json.agentworkforce/trajectories/completed/traj_1775647089663_47b8475f.json.agentworkforce/trajectories/completed/traj_1775654935435_4c3e7705.json.agentworkforce/trajectories/completed/traj_1775656444666_4554590c.json.agentworkforce/trajectories/completed/traj_1775656574638_d09f8bad.json.agentworkforce/trajectories/completed/traj_1775660026655_968f3c47.json.agentworkforce/trajectories/completed/traj_1775661295787_9aa27b54.json.agentworkforce/trajectories/completed/traj_1775662772237_c9a1c6cf.json.gitignore.trajectories/index.jsonAGENTS.mdIMPLEMENTATION-PROPOSAL.mdPROPOSAL-trajectories.mdREADME.mdbiome.jsondocs/architecture/core.mddocs/architecture/memory-integration.mddocs/architecture/web-viewer.mdpackage.jsonscripts/benchmark-compaction.tssrc/cli/commands/compact.tssrc/cli/commands/complete.tssrc/cli/commands/doctor.tssrc/cli/commands/export.tssrc/compact/config.tssrc/core/trailers.tssrc/index.tssrc/sdk/client.tssrc/storage/file.tssrc/storage/index.tstests/compact/llm-compact.test.tstests/export/export.test.tstests/sdk/workflow-compact.test.tstests/storage/concurrent-save.test.tstests/storage/reconcile-real-data.test.tstests/storage/storage.test.tstrail-viewer/Sources/AppConfiguration.swifttrail-viewer/Sources/Services/LocalServerManager.swifttrail-viewer/Sources/Services/QuickLookGenerator.swifttrail-viewer/Sources/Services/SpotlightRegistration.swifttrail-viewer/Sources/Views/WelcomeView.swifttrail-viewer/server/src/trajectory-service.tsworkflows/compact-on-workflow-run.tsworkflows/fix-trajectory-schema/01-investigate.tsworkflows/fix-trajectory-schema/02-implement.tsworkflows/fix-trajectory-schema/README.mdworkflows/llm-compaction.tsworkflows/sdk-autocompact-option.tsworkflows/sdk-workflow-autocompact.ts
💤 Files with no reviewable changes (1)
- .trajectories/index.json
|
|
||
| ### File System (Default) | ||
|
|
||
| ``` |
There was a problem hiding this comment.
Add a language to the fenced code block at Line 1139.
Markdownlint MD040 is triggered here; please annotate this fence (e.g., ```text) to keep doc lint clean.
Suggested fix
-```
+```text
.agentworkforce/trajectories/
├── index.json # Quick lookup index
...</details>
<details>
<summary>🧰 Tools</summary>
<details>
<summary>🪛 markdownlint-cli2 (0.22.1)</summary>
[warning] 1139-1139: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
</details>
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @PROPOSAL-trajectories.md at line 1139, Update the fenced code block showing
the directory tree for ".agentworkforce/trajectories/" by adding a language
identifier (e.g., text) to the opening fence so Markdownlint MD040 is satisfied; locate the fenced block that begins with the line ".agentworkforce/trajectories/" and change the opening to include the
language token (such as text) so the block becomes fenced with ```text.
</details>
<!-- fingerprinting:phantom:triton:hawk -->
<!-- This is an auto-generated comment by CodeRabbit -->
| function isTrajectoryDataDir(path: string): boolean { | ||
| const normalized = path.replace(/[\\/]+$/, ""); | ||
| const hasDataSubdirs = | ||
| existsSync(join(path, "active")) && existsSync(join(path, "completed")); | ||
|
|
||
| return normalized.endsWith(DEFAULT_TRAJECTORY_DATA_DIR) || hasDataSubdirs; | ||
| } | ||
|
|
||
| function resolveTrajectoryDataDir(path: string): string { | ||
| if (isTrajectoryDataDir(path)) { | ||
| return path; | ||
| } | ||
|
|
||
| return join(path, DEFAULT_TRAJECTORY_DATA_DIR); | ||
| } |
There was a problem hiding this comment.
Add legacy repo-root fallback in resolveTrajectoryDataDir.
For repo roots that still store data in ./.trajectories, this resolver always points to ./.agentworkforce/trajectories, which can make existing trajectories appear missing after switchDataDir.
Suggested fix
function resolveTrajectoryDataDir(path: string): string {
if (isTrajectoryDataDir(path)) {
return path;
}
+ const legacy = join(path, ".trajectories");
+ const legacyLooksLikeDataDir =
+ existsSync(join(legacy, "active")) && existsSync(join(legacy, "completed"));
+ if (legacyLooksLikeDataDir) {
+ return legacy;
+ }
+
return join(path, DEFAULT_TRAJECTORY_DATA_DIR);
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@trail-viewer/server/src/trajectory-service.ts` around lines 19 - 33, The
resolver currently only recognizes DEFAULT_TRAJECTORY_DATA_DIR and will
incorrectly redirect repo roots that still use the legacy "./.trajectories"
folder; update isTrajectoryDataDir and resolveTrajectoryDataDir to treat the
legacy name as a valid data dir. Modify isTrajectoryDataDir to also detect the
legacy folder (e.g., check existsSync(join(path, ".trajectories")) or
normalized.endsWith(".trajectories")), and change resolveTrajectoryDataDir to
return the legacy path when that folder exists on the repo root (before falling
back to join(path, DEFAULT_TRAJECTORY_DATA_DIR)), using the existing function
names isTrajectoryDataDir and resolveTrajectoryDataDir for locating where to
apply the change.
| const dataDir = process.env.TRAJECTORIES_DATA_DIR; | ||
| const trajDir = dataDir ?? join(this.dataDir, ".trajectories"); | ||
| const trajDir = dataDir ?? join(this.dataDir, DEFAULT_TRAJECTORY_DATA_DIR); | ||
| const completedDir = join(trajDir, "completed"); |
There was a problem hiding this comment.
Use the same directory resolver in index/read paths.
rebuildIndex and getTrajectory currently append DEFAULT_TRAJECTORY_DATA_DIR directly when env is unset. If this.dataDir is already a trajectory data dir, this produces an invalid nested path.
Suggested fix
- const dataDir = process.env.TRAJECTORIES_DATA_DIR;
- const trajDir = dataDir ?? join(this.dataDir, DEFAULT_TRAJECTORY_DATA_DIR);
+ const baseDir = process.env.TRAJECTORIES_DATA_DIR ?? this.dataDir;
+ const trajDir = resolveTrajectoryDataDir(baseDir);
@@
- const dataDir = process.env.TRAJECTORIES_DATA_DIR;
- const trajDir = dataDir ?? join(this.dataDir, DEFAULT_TRAJECTORY_DATA_DIR);
+ const baseDir = process.env.TRAJECTORIES_DATA_DIR ?? this.dataDir;
+ const trajDir = resolveTrajectoryDataDir(baseDir);Also applies to: 331-333
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@trail-viewer/server/src/trajectory-service.ts` around lines 92 - 94, The code
builds trajDir by blindly appending DEFAULT_TRAJECTORY_DATA_DIR to this.dataDir
which double-nests when this.dataDir already points to the trajectory directory;
change the resolver to pick the base dir = process.env.TRAJECTORIES_DATA_DIR ??
this.dataDir and then only append DEFAULT_TRAJECTORY_DATA_DIR if the base's
final path segment is not already DEFAULT_TRAJECTORY_DATA_DIR (e.g. check
path.basename(base) !== DEFAULT_TRAJECTORY_DATA_DIR), then set trajDir = base or
join(base, DEFAULT_TRAJECTORY_DATA_DIR) and compute completedDir from trajDir;
apply the same change in rebuildIndex and getTrajectory to use the same
resolution logic.
| // The SDK uses TRAJECTORIES_DATA_DIR directly as the data directory. | ||
| // If the user picked a repo root, append the default data path. | ||
| let selected = URL(fileURLWithPath: trajectoryPath) | ||
| let trajDirURL = selected | ||
| .appendingPathComponent(".agentworkforce", isDirectory: true) | ||
| .appendingPathComponent("trajectories", isDirectory: true) | ||
| let isDefaultDataDir = selected.lastPathComponent == "trajectories" && | ||
| selected.deletingLastPathComponent().lastPathComponent == ".agentworkforce" | ||
| let hasActiveDir = FileManager.default.fileExists( | ||
| atPath: selected.appendingPathComponent("active", isDirectory: true).path | ||
| ) | ||
| let hasCompletedDir = FileManager.default.fileExists( | ||
| atPath: selected.appendingPathComponent("completed", isDirectory: true).path | ||
| ) | ||
| let hasTrajectoryChildren = hasActiveDir && hasCompletedDir | ||
| let resolvedTrajectoryPath: String | ||
| if isDefaultDataDir || hasTrajectoryChildren { | ||
| resolvedTrajectoryPath = trajectoryPath | ||
| } else { | ||
| environment["TRAJECTORIES_DATA_DIR"] = trajectoryPath | ||
| resolvedTrajectoryPath = trajDirURL.path | ||
| } | ||
| environment["TRAJECTORIES_DATA_DIR"] = resolvedTrajectoryPath |
There was a problem hiding this comment.
Preserve repo-root fallback for legacy .trajectories data.
When a repo root is selected and only ./.trajectories exists, this branch resolves to ./.agentworkforce/trajectories, which can hide existing legacy data instead of serving it.
Suggested fix
if let trajectoryPath {
// The SDK uses TRAJECTORIES_DATA_DIR directly as the data directory.
// If the user picked a repo root, append the default data path.
let selected = URL(fileURLWithPath: trajectoryPath)
let trajDirURL = selected
.appendingPathComponent(".agentworkforce", isDirectory: true)
.appendingPathComponent("trajectories", isDirectory: true)
+ let legacyTrajDirURL = selected.appendingPathComponent(".trajectories", isDirectory: true)
let isDefaultDataDir = selected.lastPathComponent == "trajectories" &&
selected.deletingLastPathComponent().lastPathComponent == ".agentworkforce"
let hasActiveDir = FileManager.default.fileExists(
atPath: selected.appendingPathComponent("active", isDirectory: true).path
)
let hasCompletedDir = FileManager.default.fileExists(
atPath: selected.appendingPathComponent("completed", isDirectory: true).path
)
let hasTrajectoryChildren = hasActiveDir && hasCompletedDir
+ let hasLegacyChildren =
+ FileManager.default.fileExists(
+ atPath: legacyTrajDirURL.appendingPathComponent("active", isDirectory: true).path
+ ) &&
+ FileManager.default.fileExists(
+ atPath: legacyTrajDirURL.appendingPathComponent("completed", isDirectory: true).path
+ )
let resolvedTrajectoryPath: String
if isDefaultDataDir || hasTrajectoryChildren {
resolvedTrajectoryPath = trajectoryPath
+ } else if hasLegacyChildren {
+ resolvedTrajectoryPath = legacyTrajDirURL.path
} else {
resolvedTrajectoryPath = trajDirURL.path
}
environment["TRAJECTORIES_DATA_DIR"] = resolvedTrajectoryPath
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // The SDK uses TRAJECTORIES_DATA_DIR directly as the data directory. | |
| // If the user picked a repo root, append the default data path. | |
| let selected = URL(fileURLWithPath: trajectoryPath) | |
| let trajDirURL = selected | |
| .appendingPathComponent(".agentworkforce", isDirectory: true) | |
| .appendingPathComponent("trajectories", isDirectory: true) | |
| let isDefaultDataDir = selected.lastPathComponent == "trajectories" && | |
| selected.deletingLastPathComponent().lastPathComponent == ".agentworkforce" | |
| let hasActiveDir = FileManager.default.fileExists( | |
| atPath: selected.appendingPathComponent("active", isDirectory: true).path | |
| ) | |
| let hasCompletedDir = FileManager.default.fileExists( | |
| atPath: selected.appendingPathComponent("completed", isDirectory: true).path | |
| ) | |
| let hasTrajectoryChildren = hasActiveDir && hasCompletedDir | |
| let resolvedTrajectoryPath: String | |
| if isDefaultDataDir || hasTrajectoryChildren { | |
| resolvedTrajectoryPath = trajectoryPath | |
| } else { | |
| environment["TRAJECTORIES_DATA_DIR"] = trajectoryPath | |
| resolvedTrajectoryPath = trajDirURL.path | |
| } | |
| environment["TRAJECTORIES_DATA_DIR"] = resolvedTrajectoryPath | |
| // The SDK uses TRAJECTORIES_DATA_DIR directly as the data directory. | |
| // If the user picked a repo root, append the default data path. | |
| let selected = URL(fileURLWithPath: trajectoryPath) | |
| let trajDirURL = selected | |
| .appendingPathComponent(".agentworkforce", isDirectory: true) | |
| .appendingPathComponent("trajectories", isDirectory: true) | |
| let legacyTrajDirURL = selected.appendingPathComponent(".trajectories", isDirectory: true) | |
| let isDefaultDataDir = selected.lastPathComponent == "trajectories" && | |
| selected.deletingLastPathComponent().lastPathComponent == ".agentworkforce" | |
| let hasActiveDir = FileManager.default.fileExists( | |
| atPath: selected.appendingPathComponent("active", isDirectory: true).path | |
| ) | |
| let hasCompletedDir = FileManager.default.fileExists( | |
| atPath: selected.appendingPathComponent("completed", isDirectory: true).path | |
| ) | |
| let hasTrajectoryChildren = hasActiveDir && hasCompletedDir | |
| let hasLegacyChildren = | |
| FileManager.default.fileExists( | |
| atPath: legacyTrajDirURL.appendingPathComponent("active", isDirectory: true).path | |
| ) && | |
| FileManager.default.fileExists( | |
| atPath: legacyTrajDirURL.appendingPathComponent("completed", isDirectory: true).path | |
| ) | |
| let resolvedTrajectoryPath: String | |
| if isDefaultDataDir || hasTrajectoryChildren { | |
| resolvedTrajectoryPath = trajectoryPath | |
| } else if hasLegacyChildren { | |
| resolvedTrajectoryPath = legacyTrajDirURL.path | |
| } else { | |
| resolvedTrajectoryPath = trajDirURL.path | |
| } | |
| environment["TRAJECTORIES_DATA_DIR"] = resolvedTrajectoryPath |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@trail-viewer/Sources/Services/LocalServerManager.swift` around lines 125 -
146, The code in LocalServerManager.swift determines TRAJECTORIES_DATA_DIR using
selected, trajDirURL, isDefaultDataDir, hasActiveDir, hasCompletedDir and falls
back to trajDirURL.path, but it doesn't detect legacy repos that have a
top-level ".trajectories" directory; add a check for
selected.appendingPathComponent(".trajectories", isDirectory: true) (e.g., let
hasLegacyTrajectories = FileManager.default.fileExists(atPath:
selected.appendingPathComponent(".trajectories").path)) and incorporate it into
the resolution logic so that if hasLegacyTrajectories is true you set
resolvedTrajectoryPath to selected.appendingPathComponent(".trajectories").path
(preserving the existing behavior for isDefaultDataDir or hasTrajectoryChildren
otherwise) before assigning environment["TRAJECTORIES_DATA_DIR"].
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="trail-viewer/Sources/Services/LocalServerManager.swift">
<violation number="1" location="trail-viewer/Sources/Services/LocalServerManager.swift:139">
P2: Using `&&` here can incorrectly reject a valid trajectories directory when only one child folder exists, causing the resolved path to be rewritten to a nested `.agentworkforce/trajectories` location.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| let hasCompletedDir = FileManager.default.fileExists( | ||
| atPath: selected.appendingPathComponent("completed", isDirectory: true).path | ||
| ) | ||
| let hasTrajectoryChildren = hasActiveDir && hasCompletedDir |
There was a problem hiding this comment.
P2: Using && here can incorrectly reject a valid trajectories directory when only one child folder exists, causing the resolved path to be rewritten to a nested .agentworkforce/trajectories location.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At trail-viewer/Sources/Services/LocalServerManager.swift, line 139:
<comment>Using `&&` here can incorrectly reject a valid trajectories directory when only one child folder exists, causing the resolved path to be rewritten to a nested `.agentworkforce/trajectories` location.</comment>
<file context>
@@ -130,13 +130,13 @@ final class LocalServerManager {
- ) || FileManager.default.fileExists(
- atPath: selected.appendingPathComponent("index.json").path
)
+ let hasTrajectoryChildren = hasActiveDir && hasCompletedDir
let resolvedTrajectoryPath: String
if isDefaultDataDir || hasTrajectoryChildren {
</file context>
| let hasTrajectoryChildren = hasActiveDir && hasCompletedDir | |
| let hasTrajectoryChildren = hasActiveDir || hasCompletedDir |
Summary
Verification
Note: a broader Biome check including package.json reports pre-existing package.json formatting on main; package metadata is not changed in this PR.