create-agdf 0.13.1 → 0.13.3

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 (78) hide show
  1. package/generated/.agdf/control/templates/RUN_STATE.md +22 -0
  2. package/generated/.agdf/control/templates/artefacts/OR.md +22 -0
  3. package/generated/.github/skills/agdf-delivery-closeout/SKILL.md +7 -0
  4. package/generated/.github/skills/agdf-interaction-locales.json +2 -4
  5. package/generated/.github/skills/agdf-release-or/SKILL.md +14 -4
  6. package/generated/.github/skills/contracts/closeout.md +49 -0
  7. package/generated/.github/skills/contracts/control-scaffold.md +2 -0
  8. package/generated/.github/skills/contracts/interaction.md +23 -12
  9. package/generated/.opencode/agdf-interaction-locales.json +2 -4
  10. package/generated/.opencode/contracts/closeout.md +49 -0
  11. package/generated/.opencode/contracts/control-scaffold.md +2 -0
  12. package/generated/.opencode/contracts/interaction.md +23 -12
  13. package/generated/.opencode/skills/agdf-delivery-closeout/SKILL.md +7 -0
  14. package/generated/.opencode/skills/agdf-release-or/SKILL.md +14 -4
  15. package/generated/plugins/agdf/.claude-plugin/plugin.json +1 -1
  16. package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
  17. package/generated/plugins/agdf/control/templates/RUN_STATE.md +22 -0
  18. package/generated/plugins/agdf/control/templates/artefacts/OR.md +22 -0
  19. package/generated/plugins/agdf/meta/agdf-interaction-locales.json +2 -4
  20. package/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  21. package/generated/plugins/agdf/meta/agent-skills-conformance.json +86 -0
  22. package/generated/plugins/agdf/meta/contracts/closeout.md +49 -0
  23. package/generated/plugins/agdf/meta/contracts/control-scaffold.md +2 -0
  24. package/generated/plugins/agdf/meta/contracts/interaction.md +23 -12
  25. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +2 -4
  26. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  27. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/delivery-map.js +13 -3
  28. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/doctor.js +9 -2
  29. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/gate-check.js +6 -1
  30. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/gate-policy.js +16 -1
  31. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/parent-reconciliation.js +284 -0
  32. package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/run-state-parser.js +39 -1
  33. package/generated/plugins/agdf/runtime/create-agdf/lib/interaction-presentation.js +31 -14
  34. package/generated/plugins/agdf/runtime/create-agdf/package.json +1 -1
  35. package/generated/plugins/agdf/runtime/runtime-manifest.json +2 -2
  36. package/generated/plugins/agdf/scripts/agent-skills-conformance.mjs +543 -0
  37. package/generated/plugins/agdf/scripts/check-runtime-integrity.mjs +31 -17
  38. package/generated/plugins/agdf/skills/delivery-closeout/SKILL.md +7 -0
  39. package/generated/plugins/agdf/skills/release-or/SKILL.md +14 -4
  40. package/generated/plugins/agdf/submission/openai/availability.md +1 -1
  41. package/generated/plugins/agdf/submission/openai/capability-matrix.json +2 -2
  42. package/generated/plugins/agdf/submission/openai/release-notes.md +1 -1
  43. package/generated/plugins/agdf/submission/openai/reviewer-cases.json +1 -1
  44. package/generated/submissions/openai/agdf/.codex-plugin/plugin.json +1 -1
  45. package/generated/submissions/openai/agdf/meta/agdf-interaction-locales.json +2 -4
  46. package/generated/submissions/openai/agdf/meta/agdf-plugin.definition.json +1 -1
  47. package/generated/submissions/openai/agdf/meta/agent-skills-conformance.json +86 -0
  48. package/generated/submissions/openai/agdf/meta/contracts/closeout.md +49 -0
  49. package/generated/submissions/openai/agdf/meta/contracts/control-scaffold.md +2 -0
  50. package/generated/submissions/openai/agdf/meta/contracts/interaction.md +23 -12
  51. package/generated/submissions/openai/agdf/skills/delivery-closeout/SKILL.md +7 -0
  52. package/generated/submissions/openai/agdf/skills/release-or/SKILL.md +14 -4
  53. package/generated/submissions/openai/agdf/submission/openai/availability.md +1 -1
  54. package/generated/submissions/openai/agdf/submission/openai/capability-matrix.json +2 -2
  55. package/generated/submissions/openai/agdf/submission/openai/inventory.json +19 -15
  56. package/generated/submissions/openai/agdf/submission/openai/listing.json +1 -1
  57. package/generated/submissions/openai/agdf/submission/openai/readiness.json +2 -2
  58. package/generated/submissions/openai/agdf/submission/openai/readiness.md +2 -2
  59. package/generated/submissions/openai/agdf/submission/openai/release-notes.md +1 -1
  60. package/generated/submissions/openai/agdf/submission/openai/reviewer-cases.json +1 -1
  61. package/lib/control-evaluation/delivery-map.js +13 -3
  62. package/lib/control-evaluation/doctor.js +9 -2
  63. package/lib/control-evaluation/gate-check.js +6 -1
  64. package/lib/control-evaluation/gate-policy.js +16 -1
  65. package/lib/control-evaluation/parent-reconciliation.js +284 -0
  66. package/lib/control-state/run-state-parser.js +39 -1
  67. package/lib/interaction-presentation.js +31 -14
  68. package/lib/proportionality-benchmark/blind-prompt.js +2 -1
  69. package/lib/proportionality-benchmark/contracts.js +16 -7
  70. package/lib/proportionality-benchmark/corpus-loader.js +132 -21
  71. package/lib/proportionality-benchmark/evaluator.js +25 -15
  72. package/lib/proportionality-benchmark/index.js +1 -0
  73. package/lib/proportionality-benchmark/live-recorder.js +32 -10
  74. package/lib/proportionality-benchmark/profiles.js +102 -0
  75. package/lib/proportionality-benchmark/report.js +10 -1
  76. package/lib/proportionality-benchmark/source-fingerprint.js +6 -2
  77. package/lib/release/version-coherence.js +79 -0
  78. package/package.json +7 -3
@@ -0,0 +1,284 @@
1
+ import { RUN_ID_PATTERN } from "../control-state/run-state-parser.js";
2
+
3
+ const parentPrefix = "parent_run:";
4
+ const childPrefix = "child_run:";
5
+
6
+ function filled(value) {
7
+ const text = String(value ?? "").trim();
8
+ return Boolean(text && text !== "none" && !text.includes("<"));
9
+ }
10
+
11
+ function exactTarget(value, prefix) {
12
+ const text = String(value ?? "").trim();
13
+ if (!text.startsWith(prefix)) return "";
14
+ const runId = text.slice(prefix.length);
15
+ return RUN_ID_PATTERN.test(runId) && `${prefix}${runId}` === text ? runId : "";
16
+ }
17
+
18
+ function warning(runState, code, message, nextStep) {
19
+ return {
20
+ severity: "warn",
21
+ code,
22
+ message,
23
+ path: runState.path,
24
+ next_step: nextStep,
25
+ };
26
+ }
27
+
28
+ function invalidDispositionInput(runState) {
29
+ const input = runState.parent_reconciliation ?? {};
30
+ if (!input.present) return false;
31
+ const counts = input.field_counts ?? {};
32
+ return counts.disposition !== 1
33
+ || counts.next_action !== 1
34
+ || !["action_required", "accepted_open"].includes(input.disposition)
35
+ || !filled(input.next_action);
36
+ }
37
+
38
+ function openResult(runState, {
39
+ code = "AGDF_PARENT_RECONCILIATION_OPEN",
40
+ targetRunId = "",
41
+ evidence = "",
42
+ missingEvidence,
43
+ nextAction,
44
+ } = {}) {
45
+ const input = runState.parent_reconciliation ?? {};
46
+ const fieldCounts = input.field_counts ?? {};
47
+ const validDisposition = ["action_required", "accepted_open"].includes(input.disposition)
48
+ && fieldCounts.disposition === 1;
49
+ const validNextAction = filled(input.next_action) && fieldCounts.next_action === 1;
50
+ const declaredInputInvalid = input.present && (!validDisposition || !validNextAction);
51
+ const action = declaredInputInvalid ? nextAction : validNextAction ? input.next_action : nextAction;
52
+ const finalCode = declaredInputInvalid
53
+ ? "AGDF_PARENT_RECONCILIATION_EVIDENCE_INVALID"
54
+ : code;
55
+ return {
56
+ result: {
57
+ outcome: "open",
58
+ target_run_id: targetRunId,
59
+ disposition: validDisposition ? input.disposition : "action_required",
60
+ evidence,
61
+ missing_evidence: missingEvidence,
62
+ next_action: action,
63
+ },
64
+ findings: [warning(
65
+ runState,
66
+ finalCode,
67
+ targetRunId
68
+ ? `Child ${runState.run_id || "run"} has an open reconciliation handoff to Parent ${targetRunId}.`
69
+ : `Child ${runState.run_id || "run"} has invalid or ambiguous explicit Parent relationship evidence.`,
70
+ action,
71
+ )],
72
+ };
73
+ }
74
+
75
+ export function evaluateParentReconciliation(runState, { loadRun } = {}) {
76
+ const rows = runState.artefact_chain.filter((row) =>
77
+ row.from === "OR" && row.relationship === "reconciles_with"
78
+ );
79
+
80
+ if (!rows.length) {
81
+ return {
82
+ result: {
83
+ outcome: "not_applicable",
84
+ target_run_id: "",
85
+ disposition: "not_applicable",
86
+ evidence: "",
87
+ missing_evidence: "none",
88
+ next_action: "none",
89
+ },
90
+ findings: [],
91
+ };
92
+ }
93
+
94
+ if (rows.length !== 1) {
95
+ return openResult(runState, {
96
+ code: "AGDF_PARENT_RECONCILIATION_EVIDENCE_INVALID",
97
+ missingEvidence: "Exactly one explicit Parent relationship is required.",
98
+ nextAction: "Retain exactly one evidenced OR reconciles_with parent_run:<run_id> relationship.",
99
+ });
100
+ }
101
+
102
+ if (invalidDispositionInput(runState)) {
103
+ return openResult(runState, {
104
+ code: "AGDF_PARENT_RECONCILIATION_EVIDENCE_INVALID",
105
+ missingEvidence: "The Parent reconciliation disposition or next action is invalid or duplicated.",
106
+ nextAction: "Record one valid disposition and exactly one concrete Parent reconciliation next action.",
107
+ });
108
+ }
109
+
110
+ const row = rows[0];
111
+ const parentRunId = exactTarget(row.to, parentPrefix);
112
+ if (!parentRunId || parentRunId === runState.run_id || !filled(row.evidence)) {
113
+ return openResult(runState, {
114
+ code: "AGDF_PARENT_RECONCILIATION_EVIDENCE_INVALID",
115
+ targetRunId: parentRunId,
116
+ evidence: row.evidence,
117
+ missingEvidence: "The Parent run ID and concrete relationship evidence must be valid and unambiguous.",
118
+ nextAction: "Repair the explicit OR reconciles_with parent_run:<run_id> row and its evidence.",
119
+ });
120
+ }
121
+
122
+ let parent;
123
+ try {
124
+ parent = loadRun?.(parentRunId);
125
+ } catch {
126
+ parent = null;
127
+ }
128
+ if (!parent?.content || parent.resolution_error) {
129
+ return openResult(runState, {
130
+ targetRunId: parentRunId,
131
+ evidence: row.evidence,
132
+ missingEvidence: "The named Parent run is unavailable.",
133
+ nextAction: `Restore or select Parent run ${parentRunId}, then re-run Delivery Map.`,
134
+ });
135
+ }
136
+
137
+ const reciprocalRows = parent.artefact_chain.filter((candidate) =>
138
+ candidate.from === "Aggregate"
139
+ && candidate.relationship === "includes"
140
+ && candidate.to === `${childPrefix}${runState.run_id}`
141
+ );
142
+ if (reciprocalRows.length !== 1 || !filled(reciprocalRows[0]?.evidence)) {
143
+ return openResult(runState, {
144
+ targetRunId: parentRunId,
145
+ evidence: row.evidence,
146
+ missingEvidence: reciprocalRows.length > 1
147
+ ? "The Parent contains ambiguous reciprocal Child relationships."
148
+ : "The Parent lacks one evidenced reciprocal Aggregate includes Child relationship.",
149
+ nextAction: `Reconcile Child ${runState.run_id} in Parent ${parentRunId} through the Parent's governed scope.`,
150
+ });
151
+ }
152
+
153
+ return {
154
+ result: {
155
+ outcome: "resolved",
156
+ target_run_id: parentRunId,
157
+ disposition: "not_applicable",
158
+ evidence: reciprocalRows[0].evidence,
159
+ missing_evidence: "none",
160
+ next_action: "none",
161
+ },
162
+ findings: [],
163
+ };
164
+ }
165
+
166
+ function completedChild(runState, resolveFile) {
167
+ const orArtefact = runState?.artefacts?.get("OR");
168
+ return Boolean(
169
+ runState?.content
170
+ && orArtefact?.status === "done"
171
+ && filled(orArtefact.path)
172
+ && resolveFile?.(orArtefact.path),
173
+ );
174
+ }
175
+
176
+ export function evaluateProgrammeAggregation(runState, { loadRun, resolveFile } = {}) {
177
+ const rows = runState.artefact_chain.filter((row) =>
178
+ row.from === "Aggregate" && row.relationship === "includes"
179
+ );
180
+ const input = runState.programme_aggregation ?? {};
181
+ const explicitInput = filled(input.acceptance_ref)
182
+ || filled(input.evidence)
183
+ || (Boolean(input.missing_evidence) && input.missing_evidence !== "none");
184
+ const applicable = explicitInput || rows.length > 0;
185
+ if (!applicable) {
186
+ return {
187
+ result: {
188
+ applicable: false,
189
+ startable: false,
190
+ final_ready: false,
191
+ acceptance_ref: "",
192
+ evidence: [],
193
+ missing_evidence: [],
194
+ next_action: "none",
195
+ },
196
+ findings: [],
197
+ };
198
+ }
199
+
200
+ const invalidRows = [];
201
+ const seenChildRunIds = new Set();
202
+ for (const row of rows) {
203
+ const childRunId = exactTarget(row.to, childPrefix);
204
+ if (!childRunId || !filled(row.evidence) || seenChildRunIds.has(childRunId)) invalidRows.push(row);
205
+ else seenChildRunIds.add(childRunId);
206
+ }
207
+ const completedEvidence = [];
208
+ for (const row of rows) {
209
+ const childRunId = exactTarget(row.to, childPrefix);
210
+ if (!childRunId || !filled(row.evidence)) continue;
211
+ let child;
212
+ try {
213
+ child = loadRun?.(childRunId);
214
+ } catch {
215
+ child = null;
216
+ }
217
+ if (completedChild(child, resolveFile)) completedEvidence.push({ child_run_id: childRunId, evidence: row.evidence });
218
+ }
219
+
220
+ const fieldCounts = input.field_counts ?? {};
221
+ const acceptanceRefValid = fieldCounts.acceptance_ref === 1
222
+ && filled(input.acceptance_ref)
223
+ && input.acceptance_ref.startsWith(".agdf/control/artefacts/")
224
+ && Boolean(resolveFile?.(input.acceptance_ref));
225
+ const missingEvidenceValid = fieldCounts.missing_evidence === 1
226
+ && input.missing_evidence === "none";
227
+ const evidenceFieldValid = fieldCounts.evidence === 1 && filled(input.evidence);
228
+ const startable = completedEvidence.length > 0;
229
+ const finalReady = startable && acceptanceRefValid && missingEvidenceValid && evidenceFieldValid && invalidRows.length === 0;
230
+ const missingEvidence = [];
231
+ if (!startable) missingEvidence.push("No evidenced included Child has a completed OR.");
232
+ if (!acceptanceRefValid) missingEvidence.push("The declared programme acceptance artefact is missing or invalid.");
233
+ if (!evidenceFieldValid) missingEvidence.push("Programme aggregation evidence is missing or invalid.");
234
+ if (!missingEvidenceValid) missingEvidence.push(input.missing_evidence || "Programme missing-evidence state is not resolved.");
235
+ if (invalidRows.length) missingEvidence.push("One or more Aggregate includes Child rows are invalid.");
236
+ const nextAction = !startable
237
+ ? "Link at least one evidenced completed Child OR in the Parent Artefact Chain."
238
+ : !acceptanceRefValid
239
+ ? "Provide one valid repository-relative programme acceptance artefact reference."
240
+ : !evidenceFieldValid || !missingEvidenceValid
241
+ ? "Resolve the declared programme evidence gaps before final readiness."
242
+ : invalidRows.length
243
+ ? "Repair invalid Aggregate includes child_run:<run_id> rows."
244
+ : "none";
245
+ const findings = [];
246
+ if (invalidRows.length) {
247
+ findings.push(warning(
248
+ runState,
249
+ "AGDF_PROGRAMME_AGGREGATION_EVIDENCE_INVALID",
250
+ "Programme aggregation contains invalid explicit Child evidence.",
251
+ nextAction,
252
+ ));
253
+ }
254
+ if (!finalReady) {
255
+ findings.push(warning(
256
+ runState,
257
+ "AGDF_PROGRAMME_AGGREGATION_NOT_FINAL",
258
+ `Programme aggregation for ${runState.run_id || "run"} is ${startable ? "startable but not final-ready" : "not startable"}.`,
259
+ nextAction,
260
+ ));
261
+ }
262
+ return {
263
+ result: {
264
+ applicable: true,
265
+ startable,
266
+ final_ready: finalReady,
267
+ acceptance_ref: input.acceptance_ref,
268
+ evidence: completedEvidence,
269
+ missing_evidence: missingEvidence,
270
+ next_action: nextAction,
271
+ },
272
+ findings,
273
+ };
274
+ }
275
+
276
+ export function evaluateReconciliationState(runState, dependencies = {}) {
277
+ const parent = evaluateParentReconciliation(runState, dependencies);
278
+ const programme = evaluateProgrammeAggregation(runState, dependencies);
279
+ return {
280
+ parent_reconciliation: parent.result,
281
+ programme_aggregation: programme.result,
282
+ findings: [...parent.findings, ...programme.findings],
283
+ };
284
+ }
@@ -105,6 +105,15 @@ function field(c, k) {
105
105
  function sectionField(c, h, k) {
106
106
  return field(section(c, h), k);
107
107
  }
108
+ function sectionScalar(c, h, k) {
109
+ const body = section(c, h);
110
+ const escaped = k.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
111
+ const matches = [...body.matchAll(new RegExp(`^- ${escaped}:[^\\S\\r\\n]*(.*)$`, "gm"))];
112
+ return {
113
+ value: clean(matches[0]?.[1] ?? ""),
114
+ count: matches.length,
115
+ };
116
+ }
108
117
  function dataRows(c, h, header) {
109
118
  return rows(section(c, h)).filter(
110
119
  (r) =>
@@ -116,6 +125,7 @@ export function parseControlState(
116
125
  content,
117
126
  { userGates = [], internalSteps = [], closeoutArtefacts = [] } = {},
118
127
  ) {
128
+ const metaValues = scalarFields(content).values;
119
129
  const approvals = new Map();
120
130
  for (const h of ["Approvals", "Gate Checklist"])
121
131
  for (const [g, s, e] of rows(section(content, h))) {
@@ -146,8 +156,16 @@ export function parseControlState(
146
156
  ? "Mode/Slice Decision"
147
157
  : "Mode / Slice Decision",
148
158
  source = "Source And Scope State",
149
- memory = "Knowledge Persistence Decision";
159
+ memory = "Knowledge Persistence Decision",
160
+ handoff = "Parent Reconciliation Handoff",
161
+ aggregation = "Programme Aggregation Readiness";
162
+ const disposition = sectionScalar(content, handoff, "parent_reconciliation_disposition");
163
+ const reconciliationNextAction = sectionScalar(content, handoff, "parent_reconciliation_next_action");
164
+ const acceptanceRef = sectionScalar(content, aggregation, "programme_acceptance_ref");
165
+ const aggregationEvidence = sectionScalar(content, aggregation, "programme_aggregation_evidence");
166
+ const aggregationMissingEvidence = sectionScalar(content, aggregation, "programme_aggregation_missing_evidence");
150
167
  return {
168
+ run_id: clean(metaValues.get("run_id") ?? ""),
151
169
  current_gate: field(content, "current_gate"),
152
170
  next_allowed_action: field(content, "next_allowed_action"),
153
171
  approvals,
@@ -223,5 +241,25 @@ export function parseControlState(
223
241
  reason: sectionField(content, memory, "memory_reason"),
224
242
  refs: sectionField(content, memory, "memory_refs"),
225
243
  },
244
+ parent_reconciliation: {
245
+ present: Boolean(section(content, handoff).trim()),
246
+ disposition: disposition.value,
247
+ next_action: reconciliationNextAction.value,
248
+ field_counts: {
249
+ disposition: disposition.count,
250
+ next_action: reconciliationNextAction.count,
251
+ },
252
+ },
253
+ programme_aggregation: {
254
+ present: Boolean(section(content, aggregation).trim()),
255
+ acceptance_ref: acceptanceRef.value,
256
+ evidence: aggregationEvidence.value,
257
+ missing_evidence: aggregationMissingEvidence.value,
258
+ field_counts: {
259
+ acceptance_ref: acceptanceRef.count,
260
+ evidence: aggregationEvidence.count,
261
+ missing_evidence: aggregationMissingEvidence.count,
262
+ },
263
+ },
226
264
  };
227
265
  }
@@ -11,11 +11,28 @@ const QUALITY_DIMENSIONS = Object.freeze([
11
11
  Object.freeze({ id: "code_quality", owner: "code-review" }),
12
12
  Object.freeze({ id: "qa_decision", owner: "qa-gate" }),
13
13
  ]);
14
+ const SCOPE_CLASSIFICATION_LIMITS = Object.freeze({
15
+ maxCodePointsPerField: 240,
16
+ minEscalationTriggers: 1,
17
+ maxEscalationTriggers: 3,
18
+ });
19
+ const SCOPE_CLASSIFICATION_MARKDOWN_CONTROL = /[\\`*_\[\]<>|]/;
20
+ const SCOPE_CLASSIFICATION_LINE_STRUCTURE = /^(?:[#>+\-]\s|\d+\.\s)/;
14
21
 
15
22
  function plainObject(value) {
16
23
  return value && typeof value === "object" && !Array.isArray(value);
17
24
  }
18
25
 
26
+ function scopeClassificationText(value) {
27
+ if (typeof value !== "string") return null;
28
+ const normalized = value.trim();
29
+ if (!normalized || /[\r\n\u2028\u2029]/.test(value)) return null;
30
+ if (Array.from(normalized).length > SCOPE_CLASSIFICATION_LIMITS.maxCodePointsPerField) return null;
31
+ if (SCOPE_CLASSIFICATION_MARKDOWN_CONTROL.test(normalized)) return null;
32
+ if (SCOPE_CLASSIFICATION_LINE_STRUCTURE.test(normalized)) return null;
33
+ return normalized;
34
+ }
35
+
19
36
  function flattenKeys(value, prefix = "") {
20
37
  const keys = [];
21
38
  for (const [key, child] of Object.entries(value ?? {})) {
@@ -439,22 +456,22 @@ export function renderScopeClassificationCard(classification, {
439
456
  requestedLocale,
440
457
  } = {}) {
441
458
  if (!plainObject(classification)) return null;
442
- const outcome = String(classification.outcome ?? "").trim();
443
- const mode = String(classification.mode ?? "").trim();
444
- const trivialBoundary = String(classification.trivial_boundary ?? "").trim();
445
- const urTriggerEvaluation = String(classification.ur_trigger_evaluation ?? "").trim();
446
- const allowedSummary = String(classification.allowed_summary ?? "").trim();
447
- const forbiddenSummary = String(classification.forbidden_summary ?? "").trim();
448
- const escalationTriggers = Array.isArray(classification.escalation_triggers)
449
- ? classification.escalation_triggers.map((item) => String(item ?? "").trim()).filter(Boolean)
450
- : [];
451
- const challengePath = String(classification.challenge_path ?? "").trim();
452
-
453
- if (outcome !== "ungated") return null;
454
- if (!["quick_task", "verified_change"].includes(mode)) return null;
459
+ const { outcome, mode, trivial_boundary: trivialBoundary } = classification;
460
+ if (outcome !== "ungated" || mode !== "quick_task") return null;
455
461
  if (!["inside", "outside"].includes(trivialBoundary)) return null;
462
+
463
+ const urTriggerEvaluation = scopeClassificationText(classification.ur_trigger_evaluation);
464
+ const allowedSummary = scopeClassificationText(classification.allowed_summary);
465
+ const forbiddenSummary = scopeClassificationText(classification.forbidden_summary);
466
+ const challengePath = scopeClassificationText(classification.challenge_path);
456
467
  if (!urTriggerEvaluation || !allowedSummary || !forbiddenSummary || !challengePath) return null;
457
- if (escalationTriggers.length === 0) return null;
468
+
469
+ if (!Array.isArray(classification.escalation_triggers)
470
+ || classification.escalation_triggers.length < SCOPE_CLASSIFICATION_LIMITS.minEscalationTriggers
471
+ || classification.escalation_triggers.length > SCOPE_CLASSIFICATION_LIMITS.maxEscalationTriggers) return null;
472
+ const escalationTriggers = classification.escalation_triggers.map(scopeClassificationText);
473
+ if (escalationTriggers.some((item) => !item)) return null;
474
+ if (new Set(escalationTriggers).size !== escalationTriggers.length) return null;
458
475
 
459
476
  let locale;
460
477
  try {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agdf/local-validator-runtime",
3
- "version": "0.13.1",
3
+ "version": "0.13.3",
4
4
  "private": true,
5
5
  "type": "module"
6
6
  }
@@ -2,7 +2,7 @@
2
2
  "schema_version": "1",
3
3
  "generated": true,
4
4
  "source": "create-agdf/cli",
5
- "version": "0.13.1",
5
+ "version": "0.13.3",
6
6
  "entrypoint": "create-agdf/bin/agdf-validator.js",
7
- "digest": "891fac7000205be2f6ca0d257ad553ad52651a167737726f8bf3ea9d106c03e7"
7
+ "digest": "496bee4ca4033209f031b78a3041f7d056c6a046cd069b29752f118ef01da266"
8
8
  }