@exaudeus/workrail 3.41.0 → 3.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/cli-worktrain.js +40 -11
  2. package/dist/console-ui/assets/{index-CQt4UhPB.js → index-DwfWMKvv.js} +1 -1
  3. package/dist/console-ui/index.html +1 -1
  4. package/dist/context-assembly/deps.d.ts +8 -0
  5. package/dist/context-assembly/deps.js +2 -0
  6. package/dist/context-assembly/index.d.ts +6 -0
  7. package/dist/context-assembly/index.js +50 -0
  8. package/dist/context-assembly/infra.d.ts +3 -0
  9. package/dist/context-assembly/infra.js +154 -0
  10. package/dist/context-assembly/types.d.ts +30 -0
  11. package/dist/context-assembly/types.js +2 -0
  12. package/dist/coordinators/pr-review.d.ts +3 -1
  13. package/dist/coordinators/pr-review.js +25 -4
  14. package/dist/daemon/workflow-runner.js +11 -0
  15. package/dist/domain/execution/state.d.ts +6 -6
  16. package/dist/manifest.json +64 -32
  17. package/dist/mcp/handlers/v2-workflow.d.ts +2 -2
  18. package/dist/mcp/output-schemas.d.ts +234 -234
  19. package/dist/mcp/tools.d.ts +2 -2
  20. package/dist/mcp/v2/tools.d.ts +24 -24
  21. package/dist/v2/durable-core/schemas/artifacts/assessment.d.ts +2 -2
  22. package/dist/v2/durable-core/schemas/artifacts/coordinator-signal.d.ts +2 -2
  23. package/dist/v2/durable-core/schemas/artifacts/loop-control.d.ts +6 -6
  24. package/dist/v2/durable-core/schemas/artifacts/review-verdict.d.ts +6 -6
  25. package/dist/v2/durable-core/schemas/compiled-workflow/index.d.ts +56 -56
  26. package/dist/v2/durable-core/schemas/execution-snapshot/blocked-snapshot.d.ts +83 -83
  27. package/dist/v2/durable-core/schemas/execution-snapshot/execution-snapshot.v1.d.ts +1024 -1024
  28. package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +2336 -2336
  29. package/dist/v2/durable-core/schemas/session/dag-topology.d.ts +6 -6
  30. package/dist/v2/durable-core/schemas/session/events.d.ts +339 -339
  31. package/dist/v2/durable-core/schemas/session/gaps.d.ts +30 -30
  32. package/dist/v2/durable-core/schemas/session/manifest.d.ts +6 -6
  33. package/dist/v2/durable-core/schemas/session/outputs.d.ts +8 -8
  34. package/dist/v2/durable-core/schemas/session/validation-event.d.ts +3 -3
  35. package/docs/design/context-assembly-design-candidates.md +199 -0
  36. package/docs/design/context-assembly-implementation-plan.md +211 -0
  37. package/docs/design/context-assembly-review-findings.md +112 -0
  38. package/docs/ideas/backlog.md +64 -0
  39. package/package.json +1 -1
@@ -0,0 +1,211 @@
1
+ # Implementation Plan: Context Assembly Layer v1
2
+
3
+ *Authored during coding-task-workflow-agentic session | 2026-04-19*
4
+
5
+ ---
6
+
7
+ ## Problem Statement
8
+
9
+ When the PR review coordinator dispatches a session, it passes only a goal string.
10
+ The agent spends its first 2-3 turns re-establishing context (re-reading PR diff,
11
+ starting cold with no memory of prior reviews). Before spawning each session, the
12
+ coordinator should assemble a context bundle (git diff summary + prior session notes)
13
+ and inject it into the system prompt via the existing `trigger.context` map.
14
+
15
+ ---
16
+
17
+ ## Acceptance Criteria
18
+
19
+ 1. `npm run build` compiles clean with zero TypeScript errors
20
+ 2. `npx vitest run tests/unit/context-assembly.test.ts` -- all 5 tests pass
21
+ 3. `npx vitest run` -- no regressions in full test suite
22
+ 4. New module `src/context-assembly/` exists with 4 files
23
+ 5. `CoordinatorDeps.contextAssembler?` optional field present
24
+ 6. `CoordinatorDeps.spawnSession` accepts optional 4th `context?` arg
25
+ 7. `buildSystemPrompt()` injects `## Prior Context` section when `assembledContextSummary` present in trigger context
26
+ 8. `src/cli-worktrain.ts` wires `contextAssembler` and forwards `context` through HTTP body
27
+
28
+ ---
29
+
30
+ ## Non-Goals
31
+
32
+ 1. No URL fetching from PR description bodies (v2)
33
+ 2. No full git diff content -- `--stat` output only
34
+ 3. No changes to `src/mcp/` or `src/v2/durable-core/`
35
+ 4. No knowledge graph source (`ts-morph` stays in devDependencies)
36
+ 5. No per-coordinator custom renderers
37
+ 6. No context window budget trimming
38
+ 7. No context assembly in `TriggerRouter.route()`
39
+ 8. No HTTP endpoint schema changes
40
+ 9. No workspace-scoped session filtering (v1 returns global sessions)
41
+
42
+ ---
43
+
44
+ ## Philosophy-Driven Constraints
45
+
46
+ - All interface fields `readonly` (immutability by default)
47
+ - `Result<T,E>` (`kind: 'ok'|'err'`) for all new code; bridge from neverthrow at `infra.ts` boundary
48
+ - Factory function `createContextAssembler()`, not a class
49
+ - `renderContextBundle()` is pure (no I/O)
50
+ - `ContextAssemblerDeps` interface contains all I/O; core has zero direct imports of `fs`/`execFile`
51
+ - Tests: fake deps, no `vi.mock()`, vitest `describe/it/expect`
52
+ - Import paths use `.js` extension (TypeScript ESM)
53
+ - JSDoc `WHY` comments on non-obvious decisions
54
+
55
+ ---
56
+
57
+ ## Invariants
58
+
59
+ 1. **Partial failure**: `gitDiff` and `priorSessionNotes` fail independently; session always starts
60
+ 2. **Zero agent turn cost**: Context injected in system prompt before turn 1
61
+ 3. **Coordinator-controlled opt-in**: Only coordinators calling `assembler.assemble()` get enriched context
62
+ 4. **Prior notes recency limit**: At most 3 prior sessions
63
+ 5. **Git summary size**: File names and change counts only (no full diff)
64
+ 6. **No engine/MCP changes**: `src/v2/durable-core/` and `src/mcp/` untouched
65
+ 7. **Optional contextAssembler**: Backward-compatible -- all existing `CoordinatorDeps` fakes work
66
+
67
+ ---
68
+
69
+ ## Selected Approach
70
+
71
+ Use 4 new files in `src/context-assembly/` + 3 modified files, exactly as specified in
72
+ the pitch. `infra.ts` uses a direct `SessionEventLogReadonlyStorePortV2` adapter
73
+ (implementing only `load()` and `loadValidatedPrefix()` via `fs.promises` + existing Zod
74
+ schemas) instead of `LocalSessionEventLogStoreV2`. This eliminates the need for
75
+ `FileSystemPortV2` and `Sha256PortV2` shims.
76
+
77
+ **Runner-up**: Use `LocalSessionEventLogStoreV2` with write stubs. Rejected: write stubs
78
+ partially violate the interface contract even though they're provably unreachable.
79
+
80
+ ---
81
+
82
+ ## Vertical Slices
83
+
84
+ ### Slice 1: New module `src/context-assembly/` (4 files)
85
+
86
+ **Files to create**:
87
+ - `src/context-assembly/types.ts` (~60 LOC) - AssemblyTask, SessionNote, ContextBundle, RenderOpts, ContextAssembler interface
88
+ - `src/context-assembly/deps.ts` (~35 LOC) - ContextAssemblerDeps interface (execGit, execGh, listRecentSessions, nowIso)
89
+ - `src/context-assembly/index.ts` (~80 LOC) - createContextAssembler(), renderContextBundle(), private helpers
90
+ - `src/context-assembly/infra.ts` (~80 LOC) - createListRecentSessions() adapter
91
+
92
+ **Done when**: All 4 files compile clean in isolation
93
+
94
+ **Key implementation details**:
95
+ - `types.ts`: import `Result` from `'../runtime/result.js'`
96
+ - `index.ts`: factory function `createContextAssembler(deps)`, pure `renderContextBundle(bundle, _opts?)`
97
+ - `infra.ts`: `SessionEventLogReadonlyStorePortV2` adapter using `fs.promises.readFile` + `ManifestRecordV1Schema` + `DomainEventV1Schema`; `LocalDataDirV2(process.env)` + `LocalDirectoryListingV2(directoryListingOpsAdapter)` wired into `LocalSessionSummaryProviderV2`
98
+ - `infra.ts`: `_workspacePath` named with leading underscore (intentional non-use in v1)
99
+ - Bridge: `const result = await provider.loadHealthySummaries(); if (result.isErr()) return err(...);`
100
+
101
+ ### Slice 2: Modify `src/coordinators/pr-review.ts`
102
+
103
+ **Changes**:
104
+ 1. Add `contextAssembler?: ContextAssembler` field to `CoordinatorDeps` (after `spawnSession`, before `awaitSessions`)
105
+ 2. Extend `spawnSession` signature: add optional 4th arg `context?: Readonly<Record<string, unknown>>`
106
+ 3. Add context assembly block before `spawnSession` call in `runPrReviewCoordinator` for-loop
107
+ 4. Forward same `spawnContext` to re-review spawn (~line 1265)
108
+ 5. Do NOT add context assembly to fix-agent spawn
109
+
110
+ **Done when**: File compiles clean; existing tests still pass
111
+
112
+ ### Slice 3: Modify `src/daemon/workflow-runner.ts`
113
+
114
+ **Change**: Insert 6-line block in `buildSystemPrompt()` BEFORE the `referenceUrls` block
115
+
116
+ **Done when**: File compiles clean; `buildSystemPrompt` tests (if any) still pass
117
+
118
+ ### Slice 4: Modify `src/cli-worktrain.ts`
119
+
120
+ **Changes**:
121
+ 1. Add imports for `createContextAssembler` and `createListRecentSessions`
122
+ 2. Extend `spawnSession` lambda to accept optional 4th `context?` arg and include it in JSON body
123
+ 3. Add `contextAssembler: createContextAssembler({...})` to deps object (after `spawnSession`)
124
+ 4. Wire `execGit`, `execGh` inline using `promisify(execFile)`
125
+
126
+ **Done when**: File compiles clean
127
+
128
+ ### Slice 5: Tests
129
+
130
+ **File**: `tests/unit/context-assembly.test.ts`
131
+
132
+ **Tests**:
133
+ 1. `assemble()` with both sources succeeding -- returns bundle with `kind: 'ok'` on both fields
134
+ 2. `assemble()` with gitSummary failing -- priorNotes still returns `ok([])`; gitDiff returns `kind: 'err'`
135
+ 3. `renderBundle()` produces correct markdown sections (headings, git diff in code block)
136
+ 4. `renderBundle()` with all-failed bundle -- returns empty string
137
+ 5. `listRecentSessions` with no sessions directory -- returns `ok([])`
138
+
139
+ **Done when**: All 5 tests pass with `npx vitest run tests/unit/context-assembly.test.ts`
140
+
141
+ ---
142
+
143
+ ## Test Design
144
+
145
+ ### Fake deps for context assembler
146
+
147
+ ```typescript
148
+ function makeFakeDeps(overrides: Partial<ContextAssemblerDeps> = {}): ContextAssemblerDeps {
149
+ return {
150
+ execGit: async () => ({ kind: 'ok', value: 'src/foo.ts | 5 ++\n' }),
151
+ execGh: async () => ({ kind: 'err', error: 'gh not available' }),
152
+ listRecentSessions: async () => ({ kind: 'ok', value: [] }),
153
+ nowIso: () => '2026-04-19T00:00:00.000Z',
154
+ ...overrides,
155
+ };
156
+ }
157
+ ```
158
+
159
+ ### Test for no sessions directory
160
+
161
+ Uses a real temp directory (with no sessions subdirectory) to test the graceful `ok([])` return.
162
+
163
+ ---
164
+
165
+ ## Risk Register
166
+
167
+ | Risk | Likelihood | Impact | Mitigation |
168
+ |---|---|---|---|
169
+ | `infra.ts` SessionEventLogReadonlyStorePortV2 adapter has parsing bugs | Low | Medium | Tests verify behavior with fake data; manifest format well-documented |
170
+ | `spawnSession` 4th arg breaks existing tests | Very Low | Low | Optional arg; TypeScript enforces backward compat |
171
+ | `contextAssembler` not optional breaks existing tests | Very Low | Low | `?` makes it optional; existing fakes compile without it |
172
+ | Full test suite regressions | Very Low | High | Run `npx vitest run` before PR |
173
+
174
+ ---
175
+
176
+ ## PR Packaging Strategy
177
+
178
+ **Single PR**: `feat/context-assembly-layer`
179
+
180
+ All 7 file changes ship together. They form a single coherent feature -- the 4 new
181
+ files are meaningless without the 3 modifications, and the modifications are safe
182
+ with or without the new files.
183
+
184
+ **Commit message**: `feat(coordinator): add context assembly layer -- git summary and prior session notes`
185
+
186
+ ---
187
+
188
+ ## Philosophy Alignment Per Slice
189
+
190
+ | Slice | Principle | Status |
191
+ |---|---|---|
192
+ | Slice 1 (types) | Make illegal states unrepresentable (discriminated union) | Satisfied |
193
+ | Slice 1 (index) | Functional/declarative (factory fn, pure render) | Satisfied |
194
+ | Slice 1 (infra) | DI for boundaries (all I/O behind interface) | Satisfied |
195
+ | Slice 1 (infra) | Architectural fixes over patches (reuse projection code) | Satisfied |
196
+ | Slice 2 (coordinator) | Errors are data (Result<T,E>) | Satisfied |
197
+ | Slice 2 (coordinator) | Backward compatibility via optional fields | Satisfied |
198
+ | Slice 3 (workflow-runner) | Validate at boundaries (type check before inject) | Satisfied |
199
+ | Slice 5 (tests) | Prefer fakes over mocks | Satisfied |
200
+
201
+ ---
202
+
203
+ ## Open Questions (Residual)
204
+
205
+ None. All questions were resolved during design review:
206
+ 1. `ManifestRecordV1Schema` and `DomainEventV1Schema` -- confirmed exported
207
+ 2. `asSessionId` -- confirmed exported from ids/index.ts
208
+ 3. Re-review spawn context -- confirmed: forward same `spawnContext` from outer scope
209
+
210
+ **`unresolvedUnknownCount`**: 0
211
+ **`planConfidenceBand`**: High
@@ -0,0 +1,112 @@
1
+ # Design Review Findings: Context Assembly Layer v1
2
+
3
+ *Generated during coding-task-workflow-agentic session | 2026-04-19*
4
+
5
+ ---
6
+
7
+ ## Tradeoff Review
8
+
9
+ | Tradeoff | Verdict | Condition for Failure |
10
+ |---|---|---|
11
+ | FileSystemPortV2 write stubs | RESOLVED -- switching to direct SessionEventLogReadonlyStorePortV2 adapter | N/A |
12
+ | Inline DI wiring in infra.ts | Acceptable -- explicitly endorsed by pitch R1 mitigation | N/A |
13
+ | Global session list (no workspace filtering) | Accepted -- v2 improvement documented in pitch | Multiple simultaneous workspaces with unrelated sessions |
14
+ | Promise.all partial failure | Adequately handled -- each source returns Result, try/catch in infra | N/A |
15
+
16
+ ---
17
+
18
+ ## Failure Mode Review
19
+
20
+ | Failure Mode | Covered By | Residual Risk |
21
+ |---|---|---|
22
+ | Write stubs called | Outer try/catch in createListRecentSessions | Low -- FM1 resolved by switching adapter |
23
+ | Git commands fail | execGit/execGh return err(), fallback chain | None |
24
+ | Sessions dir missing | LocalDirectoryListingV2 returns [] for FS_NOT_FOUND | None |
25
+ | Corrupt session data | LocalSessionSummaryProviderV2 graceful skip | None |
26
+ | spawnSession backward compat | Optional 4th arg | None |
27
+ | contextAssembler not provided | Optional field + if-guard in coordinator | None |
28
+
29
+ ---
30
+
31
+ ## Runner-Up / Simpler Alternative Review
32
+
33
+ **Runner-up** (Candidate B: raw fs.promises) has no elements worth borrowing into the
34
+ selected design.
35
+
36
+ **Simpler variant identified**: Use a direct `SessionEventLogReadonlyStorePortV2`
37
+ adapter in `infra.ts` instead of `LocalSessionEventLogStoreV2`. This eliminates all
38
+ `FileSystemPortV2` and `Sha256PortV2` stubs while still reusing `LocalSessionSummaryProviderV2`
39
+ and the full projection pipeline. The adapter implements only `load()` and
40
+ `loadValidatedPrefix()` using `fs.promises` + existing Zod schemas (`ManifestRecordV1Schema`,
41
+ `DomainEventV1Schema`). Approximately 40 lines. This is strictly cleaner.
42
+
43
+ **Recommendation**: Use the simpler `SessionEventLogReadonlyStorePortV2` adapter approach.
44
+
45
+ ---
46
+
47
+ ## Philosophy Alignment
48
+
49
+ | Principle | Alignment |
50
+ |---|---|
51
+ | Errors are data | STRONG -- Result<T,E> throughout |
52
+ | DI for boundaries | STRONG -- ContextAssemblerDeps, inline construction accepted for separate process |
53
+ | Prefer fakes over mocks | STRONG -- test spec uses fake deps |
54
+ | Immutability by default | STRONG -- readonly everywhere |
55
+ | Make illegal states unrepresentable | STRONG -- AssemblyTask discriminated union |
56
+ | Functional/declarative | STRONG -- factory functions, pure renderContextBundle |
57
+ | YAGNI | MILD TENSION -- empty RenderOpts interface (accepted, documented) |
58
+ | Architectural fixes over patches | STRONG with adapter approach |
59
+
60
+ ---
61
+
62
+ ## Findings
63
+
64
+ ### YELLOW: Empty RenderOpts interface
65
+
66
+ **Severity**: Yellow (minor)
67
+
68
+ **Finding**: `RenderOpts` is an empty interface in v1. TypeScript `{}` and `interface RenderOpts {}` are equivalent. The empty interface is only useful as a future extension point.
69
+
70
+ **Assessment**: Acceptable -- the pitch explicitly documents this as an intentional v1 placeholder. No action needed.
71
+
72
+ ### YELLOW: Global session filtering (no workspace scope)
73
+
74
+ **Severity**: Yellow (minor, known)
75
+
76
+ **Finding**: `listRecentSessions` returns the 3 most recent sessions globally, not scoped to the coordinator's workspace path. For users running multiple workspaces, prior notes from unrelated workspaces may appear.
77
+
78
+ **Assessment**: Acceptable for v1 -- pitch explicitly documents this as a known limitation. The `_workspacePath` parameter should be named with a leading `_` to signal intentional non-use.
79
+
80
+ ### GREEN: All other design elements
81
+
82
+ Tradeoffs, failure modes, and philosophy alignment are all sound. No RED or ORANGE findings.
83
+
84
+ ---
85
+
86
+ ## Recommended Revisions
87
+
88
+ 1. **Use direct `SessionEventLogReadonlyStorePortV2` adapter** in `infra.ts` instead of
89
+ `LocalSessionEventLogStoreV2`. Eliminates all write stubs. Implements `load()` using
90
+ `fs.promises.readFile` + `ManifestRecordV1Schema` + `DomainEventV1Schema` parsing.
91
+ Approximately 40 lines.
92
+
93
+ 2. **Name unused parameter** `_workspacePath` in `createListRecentSessions` to signal
94
+ intentional non-use.
95
+
96
+ ---
97
+
98
+ ## Residual Concerns
99
+
100
+ 1. **Import path for Zod schemas**: `ManifestRecordV1Schema` and `DomainEventV1Schema`
101
+ must be importable from `src/v2/durable-core/schemas/session/index.ts`. Verify before
102
+ implementing that these schemas are exported.
103
+
104
+ 2. **`asSessionId` usage**: The `SessionEventLogReadonlyStorePortV2.load()` adapter
105
+ receives a `SessionId` branded type. When reading session directories and mapping
106
+ entry names to `SessionId`, the `asSessionId` import from
107
+ `src/v2/durable-core/ids/index.ts` must be used correctly.
108
+
109
+ 3. **Neverthrow ResultAsync wrapping**: The adapter must return neverthrow `ResultAsync`
110
+ objects (using `okAsync`/`errAsync` from neverthrow), not the custom `Result` type.
111
+ The bridge from neverthrow to custom Result happens in the `createListRecentSessions`
112
+ function via `const result = await resultAsync; if (result.isErr()) return err(...)`.
@@ -6183,3 +6183,67 @@ The daemon tool approach is only better for ad-hoc mid-session queries the agent
6183
6183
  ### Anti-pattern to avoid
6184
6184
 
6185
6185
  Adding knowledge graph calls directly into `pr-review.ts` or any other coordinator script. That immediately creates the god class we're trying to avoid and couples the orchestration layer to a specific context source.
6186
+
6187
+ ---
6188
+
6189
+ ## Scheduled tasks (Apr 19, 2026)
6190
+
6191
+ **The idea:** WorkTrain runs tasks on a schedule -- not triggered by an external event, but by time. "Every Monday morning, run the code health scan." "Every night at 2am, check for new GitHub issues and triage them." "First of the month, run the production readiness audit."
6192
+
6193
+ ### Why this matters for the autonomous pipeline vision
6194
+
6195
+ The full autonomous pipeline (prioritize → discover → shape → implement → test → PR → review → fix → merge) needs a way to start without a human pushing a button. Scheduled tasks are the trigger layer for proactive, time-driven work. Without them, WorkTrain is purely reactive -- it only acts when a webhook fires or a human dispatches it.
6196
+
6197
+ ### What exists today
6198
+
6199
+ The trigger system (`src/trigger/`) supports `generic` (webhook) and polling providers (`gitlab_poll`, `github_issues_poll`, `github_prs_poll`). There is no native cron/schedule provider. The workaround today is OS crontab calling `curl` to fire a webhook.
6200
+
6201
+ ### What to build
6202
+
6203
+ A `schedule` provider in triggers.yml:
6204
+
6205
+ ```yaml
6206
+ triggers:
6207
+ - id: weekly-code-health
6208
+ provider: schedule
6209
+ cron: "0 9 * * 1" # every Monday at 9am
6210
+ workflowId: architecture-scalability-audit
6211
+ workspacePath: /path/to/repo
6212
+ goal: "Run weekly code health scan -- identify coupling violations, complexity hotspots, and performance anti-patterns introduced this week"
6213
+
6214
+ - id: nightly-issue-triage
6215
+ provider: schedule
6216
+ cron: "0 2 * * *" # every night at 2am
6217
+ workflowId: wr.discovery
6218
+ workspacePath: /path/to/repo
6219
+ goal: "Review open GitHub issues created in the last 24 hours and triage them: classify severity, identify duplicates, suggest which to prioritize"
6220
+
6221
+ - id: backlog-next-task
6222
+ provider: schedule
6223
+ cron: "0 8 * * 1-5" # weekday mornings at 8am
6224
+ workflowId: coding-task-workflow-agentic
6225
+ workspacePath: /path/to/repo
6226
+ goal: "Pick the highest-priority unstarted task from docs/ideas/backlog.md and implement it"
6227
+ ```
6228
+
6229
+ ### Key design decisions
6230
+
6231
+ - **Cron syntax**: standard 5-field cron (`min hour dom month dow`). Parsed by `node-cron` or equivalent -- already a pattern in the codebase (backlog mentions cron).
6232
+ - **Timezone**: configurable per trigger, defaults to system timezone. Important for "weekday morning" schedules that need to fire in the user's timezone.
6233
+ - **Missed runs**: if the daemon was down when a scheduled run should have fired, it does NOT catch up on missed runs by default. "Run at 9am Monday" means "run the next time 9am Monday arrives." Optional `catchUp: true` flag for cases where missing a run should be recovered.
6234
+ - **Overlap prevention**: if a scheduled run fires while the previous run is still active, it should be skipped (not queued). A `coding-task` that takes 2 hours should not spawn a second instance at the next cron tick.
6235
+ - **Manual trigger**: `worktrain run schedule <trigger-id>` to fire a scheduled trigger immediately without waiting for the cron time. Useful for testing.
6236
+
6237
+ ### Integration with the autonomous pipeline
6238
+
6239
+ Scheduled tasks are the entry point for fully autonomous work:
6240
+ - "Every weekday morning, pick the next backlog item and run the full pipeline" -- this is how WorkTrain improves WorkTrain without any human input.
6241
+ - "Every time a PR is opened, run the MR review pipeline" -- this is github_prs_poll, already exists.
6242
+ - "Every Monday, run the architecture audit and file GitHub issues for findings" -- new scheduled capability.
6243
+
6244
+ ### Implementation notes
6245
+
6246
+ - The `PollingScheduler` in `src/trigger/polling-scheduler.ts` already runs time-based loops for GitLab/GitHub polling. The schedule provider would be a similar loop, using cron expression matching instead of API polling.
6247
+ - `node-cron` or `croner` npm package for cron expression parsing and next-fire-time calculation. Lightweight, no daemon dependencies.
6248
+ - Scheduled triggers have no webhook payload -- `contextMapping` is empty, `goalTemplate` uses only static text or env vars.
6249
+ - The schedule state (last-fired-at per trigger) persists to `~/.workrail/schedule-state.json` so the daemon can detect missed runs on restart.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exaudeus/workrail",
3
- "version": "3.41.0",
3
+ "version": "3.42.0",
4
4
  "description": "Step-by-step workflow enforcement for AI agents via MCP",
5
5
  "license": "MIT",
6
6
  "repository": {