fraim 2.0.240 → 2.0.241

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.
@@ -152,6 +152,38 @@ function validateDelegationLedger(value) {
152
152
  else if (!Array.isArray(obj.tasks)) {
153
153
  errors.push('evidence.delegationLedger.tasks must be an array');
154
154
  }
155
+ else {
156
+ // Issue #1021: validate each task, not just the array's existence. A task naming
157
+ // no job used to pass here and then be skipped by a bare `continue` in
158
+ // maybeStartDelegatedChildRuns, so a whole workstream could vanish with nothing
159
+ // manager-visible. Rejecting at emission lets the agent retry in one round.
160
+ //
161
+ // Deliberately NOT required: `taskId`. normalizeDelegationLedger synthesizes one
162
+ // from the title, so requiring it would reject ledgers the runtime handles fine.
163
+ // The title requirement accepts `briefing`/`instructions` because the group schema
164
+ // supplies those instead — this mirrors exactly what the normalizer needs to keep
165
+ // a task, so the contract can no longer be weaker than the parser.
166
+ obj.tasks.forEach((task, index) => {
167
+ const label = `evidence.delegationLedger.tasks[${index}]`;
168
+ if (!task || typeof task !== 'object' || Array.isArray(task)) {
169
+ errors.push(`${label} must be an object`);
170
+ return;
171
+ }
172
+ const entry = task;
173
+ const nonEmpty = (value) => typeof value === 'string' && value.trim().length > 0;
174
+ if (!nonEmpty(entry.title) && !nonEmpty(entry.briefing) && !nonEmpty(entry.instructions)) {
175
+ errors.push(`${label} needs a title (or briefing/instructions to derive one from)`);
176
+ }
177
+ if (!nonEmpty(entry.jobId) && !nonEmpty(entry.job) && !nonEmpty(entry.job_id)) {
178
+ errors.push(`${label}.jobId is missing; every delegated task must name an existing FRAIM job`);
179
+ }
180
+ const issueNumber = entry.issueNumber ?? entry.issue_number ?? entry.issue;
181
+ if (issueNumber !== undefined && issueNumber !== null
182
+ && typeof issueNumber !== 'string' && typeof issueNumber !== 'number') {
183
+ errors.push(`${label}.issueNumber must be a string or number when present (got ${typeof issueNumber})`);
184
+ }
185
+ });
186
+ }
155
187
  return errors.length > 0 ? errors : null;
156
188
  }
157
189
  // ---------------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim",
3
- "version": "2.0.240",
3
+ "version": "2.0.241",
4
4
  "description": "FRAIM core CLI and MCP package.",
5
5
  "main": "index.js",
6
6
  "bin": {