fix(sdk): align WorkflowTrajectory writer with canonical layout + fields#730
fix(sdk): align WorkflowTrajectory writer with canonical layout + fields#730khaliqgant wants to merge 1 commit intomainfrom
Conversation
The SDK's WorkflowTrajectory is a standalone writer that emits
trajectory JSON without using the `agent-trajectories` library. Its
output diverged from the canonical layout in two visible ways:
1. Completed files landed in a flat `completed/` root instead of
`completed/YYYY-MM/{id}.json`, forcing downstream readers to grow
legacy-layout fallbacks.
2. Top-level `commits`, `filesChanged`, and `tags` arrays were omitted,
even though the canonical schema declares them. Readers had to
default them at load time.
These are now aligned:
- `moveToCompleted()` computes a `YYYY-MM` bucket from
`completedAt` (falling back to `startedAt`) and writes into
`completed/<bucket>/{id}.json`.
- `TrajectoryFile` interface declares `commits: string[]`,
`filesChanged: string[]`, `tags: string[]`, and `start()` initializes
them to empty arrays.
Canonical output is defense-in-depth — `agent-trajectories` PR #22
(AgentWorkforce/trajectories#22) makes the reader tolerant of the prior
shapes, so this change is not urgent. It does let the reader shed those
fallbacks over time.
Test coverage:
- Existing `readCompletedTrajectoryFile` helper now walks completed/
recursively so tests don't hardcode the bucket name.
- New: asserts the completed file path has exactly one `YYYY-MM`
intermediate directory.
- New: asserts `commits`/`filesChanged`/`tags` are populated as [] on
`start()`.
All 29 tests in `workflow-trajectory.test.ts` pass. Diff scoped to
two files; unrelated full-suite failures (verification.test.ts,
step-executor, etc.) are pre-existing and out of scope.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| const bucketSource = this.trajectory.completedAt ?? this.trajectory.startedAt; | ||
| const bucketDate = new Date(bucketSource); | ||
| const monthBucket = `${bucketDate.getUTCFullYear()}-${String(bucketDate.getUTCMonth() + 1).padStart( | ||
| 2, | ||
| '0' | ||
| )}`; | ||
| const completedDir = path.join(this.dataDir, 'completed', monthBucket); |
There was a problem hiding this comment.
🔴 Completed trajectory path change breaks readCompletedTrajectoryFile in workflow-runner tests
The new moveToCompleted() writes files to completed/YYYY-MM/{id}.json instead of completed/{id}.json, but readCompletedTrajectoryFile in packages/sdk/src/__tests__/workflow-runner.test.ts:204-211 still does a flat readdirSync(completedDir) looking for .json files. It will only find the YYYY-MM subdirectory (not a .json file), return null, and the test at packages/sdk/src/__tests__/workflow-runner.test.ts:848 will crash with Cannot read properties of null (reading 'chapters'). The same pattern affects readLatestTrajectoryFile in tests/integration/broker/utils/workflow-harness.ts:278-302 (flat readdirSync) and the hardcoded flat path assertion at tests/integration/broker/trajectory.test.ts:95.
Prompt for agents
The moveToCompleted() method was changed to write completed trajectory files to completed/YYYY-MM/ subdirectories, but three existing readers of completed trajectories were not updated to walk subdirectories:
1. packages/sdk/src/__tests__/workflow-runner.test.ts (lines 204-211): readCompletedTrajectoryFile does a flat readdirSync on .trajectories/completed/ and filters for .json files. It needs to walk subdirectories like the updated findCompletedTrajectoryJson in workflow-trajectory.test.ts.
2. tests/integration/broker/utils/workflow-harness.ts (lines 278-302): readLatestTrajectoryFile does the same flat scan. When getTrajectory (line 208) passes .trajectories/completed/, it won't find files in YYYY-MM subdirectories.
3. tests/integration/broker/trajectory.test.ts (line 95): Constructs completedPath as .trajectories/completed/{id}.json without the YYYY-MM bucket, so the fs.existsSync check will always fail.
All three need to be updated to handle the new YYYY-MM subdirectory layout, similar to how findCompletedTrajectoryJson was implemented in workflow-trajectory.test.ts.
Was this helpful? React with 👍 or 👎 to provide feedback.
|
Closing in favor of a comprehensive unification approach. This PR was a tactical alignment: make
The right fix is to replace A follow-up PR implementing the unification will land shortly. Companion reader-tolerance fix (ships independently): AgentWorkforce/trajectories#22 |
Summary
Aligns
WorkflowTrajectory(the standalone SDK writer used by the workflow runner) with the canonicalagent-trajectorieslayout and top-level field contract. Defense-in-depth follow-up to AgentWorkforce/trajectories#22, which makes the reader tolerant of the current SDK output.Why
WorkflowTrajectorywrites trajectory JSON directly — it never touches theagent-trajectorieslibrary. Over time its output diverged from whatFileStorage.save()produces:completed/root instead ofcompleted/YYYY-MM/{id}.json. This is why workforce's.trajectories/completed/is a flat pile of files — every one of them came through this writer.commits,filesChanged, andtagsarrays were missing, even though the canonical schema declares them. The reader had to default them on load.Neither is a correctness bug today (trajectories#22 made the reader tolerant), but the divergence forces the reader to carry legacy-layout fallbacks forever. Aligning the writer lets those fallbacks be removed in a future release.
Changes
moveToCompleted()— computes aYYYY-MMbucket fromcompletedAt(falling back tostartedAt) and writes the file tocompleted/<bucket>/{id}.json. Bucket directory is created withmkdir -p.TrajectoryFileinterface — declarescommits: string[],filesChanged: string[],tags: string[].start()— initializes all three to[]so the written trajectory is canonical-complete from the firstflush().Test plan
npm run build(SDK package)npx vitest run src/__tests__/workflow-trajectory.test.ts— 29 passed (27 pre-existing + 2 new)YYYY-MMintermediate directorycommits/filesChanged/tagspopulated as[]afterstart()readCompletedTrajectoryFilehelper now walkscompleted/recursively so tests don't hardcode the bucket nameUnrelated full-suite failures in
verification.test.ts,step-executor.test.ts, etc. are pre-existing onorigin/mainand out of scope for this change.Reader companion
AgentWorkforce/trajectories#22 — relaxes the zod schema on the reader side so ids like
traj_<ts>_<hex>, roles likeworkflow-runner/specialist, and missingcommits/filesChanged/projectId/tagsno longer fail validation. This SDK PR is the upstream "fix it at source" companion — both can ship independently; the reader PR unblocks the immediate bug (trail compact --all→ "No trajectories found"), and this SDK PR makes new trajectories canonical so the reader can stop tolerating legacy shapes in a future release.🤖 Generated with Claude Code