create-agdf 0.13.2 → 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.
- package/generated/.agdf/control/templates/RUN_STATE.md +22 -0
- package/generated/.agdf/control/templates/artefacts/OR.md +22 -0
- package/generated/.github/skills/agdf-delivery-closeout/SKILL.md +7 -0
- package/generated/.github/skills/agdf-interaction-locales.json +2 -4
- package/generated/.github/skills/agdf-release-or/SKILL.md +14 -4
- package/generated/.github/skills/contracts/closeout.md +49 -0
- package/generated/.github/skills/contracts/control-scaffold.md +2 -0
- package/generated/.github/skills/contracts/interaction.md +23 -12
- package/generated/.opencode/agdf-interaction-locales.json +2 -4
- package/generated/.opencode/contracts/closeout.md +49 -0
- package/generated/.opencode/contracts/control-scaffold.md +2 -0
- package/generated/.opencode/contracts/interaction.md +23 -12
- package/generated/.opencode/skills/agdf-delivery-closeout/SKILL.md +7 -0
- package/generated/.opencode/skills/agdf-release-or/SKILL.md +14 -4
- package/generated/plugins/agdf/.claude-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/control/templates/RUN_STATE.md +22 -0
- package/generated/plugins/agdf/control/templates/artefacts/OR.md +22 -0
- package/generated/plugins/agdf/meta/agdf-interaction-locales.json +2 -4
- package/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
- package/generated/plugins/agdf/meta/agent-skills-conformance.json +86 -0
- package/generated/plugins/agdf/meta/contracts/closeout.md +49 -0
- package/generated/plugins/agdf/meta/contracts/control-scaffold.md +2 -0
- package/generated/plugins/agdf/meta/contracts/interaction.md +23 -12
- package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +2 -4
- package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/delivery-map.js +13 -3
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/doctor.js +9 -2
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/gate-check.js +6 -1
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/gate-policy.js +16 -1
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/parent-reconciliation.js +284 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/run-state-parser.js +39 -1
- package/generated/plugins/agdf/runtime/create-agdf/lib/interaction-presentation.js +31 -14
- package/generated/plugins/agdf/runtime/create-agdf/package.json +1 -1
- package/generated/plugins/agdf/runtime/runtime-manifest.json +2 -2
- package/generated/plugins/agdf/scripts/agent-skills-conformance.mjs +543 -0
- package/generated/plugins/agdf/scripts/check-runtime-integrity.mjs +31 -17
- package/generated/plugins/agdf/skills/delivery-closeout/SKILL.md +7 -0
- package/generated/plugins/agdf/skills/release-or/SKILL.md +14 -4
- package/generated/plugins/agdf/submission/openai/availability.md +1 -1
- package/generated/plugins/agdf/submission/openai/capability-matrix.json +2 -2
- package/generated/plugins/agdf/submission/openai/release-notes.md +1 -1
- package/generated/plugins/agdf/submission/openai/reviewer-cases.json +1 -1
- package/generated/submissions/openai/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/submissions/openai/agdf/meta/agdf-interaction-locales.json +2 -4
- package/generated/submissions/openai/agdf/meta/agdf-plugin.definition.json +1 -1
- package/generated/submissions/openai/agdf/meta/agent-skills-conformance.json +86 -0
- package/generated/submissions/openai/agdf/meta/contracts/closeout.md +49 -0
- package/generated/submissions/openai/agdf/meta/contracts/control-scaffold.md +2 -0
- package/generated/submissions/openai/agdf/meta/contracts/interaction.md +23 -12
- package/generated/submissions/openai/agdf/skills/delivery-closeout/SKILL.md +7 -0
- package/generated/submissions/openai/agdf/skills/release-or/SKILL.md +14 -4
- package/generated/submissions/openai/agdf/submission/openai/availability.md +1 -1
- package/generated/submissions/openai/agdf/submission/openai/capability-matrix.json +2 -2
- package/generated/submissions/openai/agdf/submission/openai/inventory.json +19 -15
- package/generated/submissions/openai/agdf/submission/openai/listing.json +1 -1
- package/generated/submissions/openai/agdf/submission/openai/readiness.json +2 -2
- package/generated/submissions/openai/agdf/submission/openai/readiness.md +2 -2
- package/generated/submissions/openai/agdf/submission/openai/release-notes.md +1 -1
- package/generated/submissions/openai/agdf/submission/openai/reviewer-cases.json +1 -1
- package/lib/control-evaluation/delivery-map.js +13 -3
- package/lib/control-evaluation/doctor.js +9 -2
- package/lib/control-evaluation/gate-check.js +6 -1
- package/lib/control-evaluation/gate-policy.js +16 -1
- package/lib/control-evaluation/parent-reconciliation.js +284 -0
- package/lib/control-state/run-state-parser.js +39 -1
- package/lib/interaction-presentation.js +31 -14
- package/lib/proportionality-benchmark/blind-prompt.js +2 -1
- package/lib/proportionality-benchmark/contracts.js +16 -7
- package/lib/proportionality-benchmark/corpus-loader.js +132 -21
- package/lib/proportionality-benchmark/evaluator.js +25 -15
- package/lib/proportionality-benchmark/index.js +1 -0
- package/lib/proportionality-benchmark/live-recorder.js +32 -10
- package/lib/proportionality-benchmark/profiles.js +102 -0
- package/lib/proportionality-benchmark/report.js +10 -1
- package/lib/proportionality-benchmark/source-fingerprint.js +6 -2
- package/package.json +4 -2
|
@@ -3,8 +3,9 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { aggregate, resolveRuns } from '../control-state/index.js';
|
|
4
4
|
import { resolveConfiguredChatLanguage } from '../cli/runtime-context.js';
|
|
5
5
|
import { isGateSatisfied, transitionDecisionForRunState } from './gate-policy.js';
|
|
6
|
-
import { gateApprovalStatus, readRunState } from './run-state.js';
|
|
6
|
+
import { gateApprovalStatus, readRunState, resolvedArtefactFile } from './run-state.js';
|
|
7
7
|
import { allowNoActiveRuns, filled, isPlaceholderValue, markdownSection, parseBacklogSection, readTargetFile } from './shared.js';
|
|
8
|
+
import { evaluateReconciliationState } from './parent-reconciliation.js';
|
|
8
9
|
|
|
9
10
|
const deliveryRelationships = [
|
|
10
11
|
{ from: "UR", relationship: "approved_by", to: "Approval: UR", requiredBy: "UR" },
|
|
@@ -38,8 +39,10 @@ function severityFromImpact(value) {
|
|
|
38
39
|
return null;
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
export function analyzeDeliveryMap(runState) {
|
|
42
|
+
export function analyzeDeliveryMap(runState, dependencies = {}) {
|
|
42
43
|
const findings = [];
|
|
44
|
+
const reconciliation = evaluateReconciliationState(runState, dependencies);
|
|
45
|
+
findings.push(...reconciliation.findings);
|
|
43
46
|
const relationships = deliveryRelationships.map((expected) => {
|
|
44
47
|
const row = findRelationship(runState, expected);
|
|
45
48
|
const required = relationshipRequired(runState, expected.requiredBy);
|
|
@@ -142,6 +145,8 @@ export function analyzeDeliveryMap(runState) {
|
|
|
142
145
|
context_graph: runState.context_graph,
|
|
143
146
|
source_scope: runState.source_scope,
|
|
144
147
|
memory: runState.memory,
|
|
148
|
+
parent_reconciliation: reconciliation.parent_reconciliation,
|
|
149
|
+
programme_aggregation: reconciliation.programme_aggregation,
|
|
145
150
|
findings,
|
|
146
151
|
};
|
|
147
152
|
}
|
|
@@ -215,7 +220,10 @@ export function evaluateDeliveryMap(targetDir, selection = {}, dependencies = {}
|
|
|
215
220
|
}
|
|
216
221
|
const doctorReport = evaluateDoctor(targetDir, selection);
|
|
217
222
|
const runState = readRunState(targetDir, selection);
|
|
218
|
-
const map = analyzeDeliveryMap(runState
|
|
223
|
+
const map = analyzeDeliveryMap(runState, {
|
|
224
|
+
loadRun: (runId) => readRunState(targetDir, { runId }),
|
|
225
|
+
resolveFile: (path) => resolvedArtefactFile(targetDir, path),
|
|
226
|
+
});
|
|
219
227
|
const gateDecision = transitionDecisionForRunState(runState);
|
|
220
228
|
const currentGate = gateDecision.current_gate;
|
|
221
229
|
|
|
@@ -262,6 +270,8 @@ export function evaluateDeliveryMap(targetDir, selection = {}, dependencies = {}
|
|
|
262
270
|
context_graph: map.context_graph,
|
|
263
271
|
source_scope: map.source_scope,
|
|
264
272
|
memory: map.memory,
|
|
273
|
+
parent_reconciliation: map.parent_reconciliation,
|
|
274
|
+
programme_aggregation: map.programme_aggregation,
|
|
265
275
|
findings: map.findings,
|
|
266
276
|
doctor_status: doctorReport.status,
|
|
267
277
|
doctor_summary: doctorReport.summary,
|
|
@@ -4,7 +4,7 @@ import { aggregate, resolveRuns, verifyLegacyProjection } from '../control-state
|
|
|
4
4
|
import { doctorRequiredFiles } from './required-files.js';
|
|
5
5
|
import { analyzeDeliveryMap } from './delivery-map.js';
|
|
6
6
|
import { evaluateVerifiedChange } from './verified-change.js';
|
|
7
|
-
import { analyzeArtefactRoleConsistency, analyzeDurableGateArtefactConsistency, modeSliceDecision, readRunState } from './run-state.js';
|
|
7
|
+
import { analyzeArtefactRoleConsistency, analyzeDurableGateArtefactConsistency, modeSliceDecision, readRunState, resolvedArtefactFile } from './run-state.js';
|
|
8
8
|
import { addFinding, allowNoActiveRuns, filled, hasFilledEvidenceRow, hasFilledTableRow, isPlaceholderValue, markdownSection, nonEmptyTableRows, parseBacklogSection, parseQualityContracts, readTargetFile, runSelectionRecovery, tableRows } from './shared.js';
|
|
9
9
|
|
|
10
10
|
export function evaluateDoctor(targetDir, selection = {}) {
|
|
@@ -45,6 +45,7 @@ export function evaluateDoctor(targetDir, selection = {}) {
|
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
const findings = [];
|
|
48
|
+
let reconciliation;
|
|
48
49
|
const hasCanonicalRuns = existsSync(join(targetDir, ".agdf", "control", "runs"));
|
|
49
50
|
if (hasCanonicalRuns) {
|
|
50
51
|
const projection = verifyLegacyProjection(targetDir);
|
|
@@ -221,7 +222,11 @@ export function evaluateDoctor(targetDir, selection = {}) {
|
|
|
221
222
|
for (const finding of analyzeArtefactRoleConsistency(targetDir, runState)) {
|
|
222
223
|
addFinding(findings, finding.severity, finding.code, finding.message, finding.path, finding.next_step);
|
|
223
224
|
}
|
|
224
|
-
|
|
225
|
+
reconciliation = analyzeDeliveryMap(runState, {
|
|
226
|
+
loadRun: (runId) => readRunState(targetDir, { runId }),
|
|
227
|
+
resolveFile: (path) => resolvedArtefactFile(targetDir, path),
|
|
228
|
+
});
|
|
229
|
+
for (const finding of reconciliation.findings) {
|
|
225
230
|
addFinding(findings, finding.severity, finding.code, finding.message, finding.path, finding.next_step);
|
|
226
231
|
}
|
|
227
232
|
}
|
|
@@ -241,6 +246,8 @@ export function evaluateDoctor(targetDir, selection = {}) {
|
|
|
241
246
|
revise: findings.filter((finding) => finding.severity === "revise").length,
|
|
242
247
|
warn: findings.filter((finding) => finding.severity === "warn").length,
|
|
243
248
|
},
|
|
249
|
+
parent_reconciliation: reconciliation?.parent_reconciliation,
|
|
250
|
+
programme_aggregation: reconciliation?.programme_aggregation,
|
|
244
251
|
findings,
|
|
245
252
|
};
|
|
246
253
|
}
|
|
@@ -225,7 +225,10 @@ export function evaluateGateCheck(targetDir, selection = {}) {
|
|
|
225
225
|
? evaluateVerifiedChange(targetDir, runState)
|
|
226
226
|
: null;
|
|
227
227
|
const transitionDecision = transitionDecisionForRunState(runState, verifiedChange);
|
|
228
|
-
const deliveryMap = analyzeDeliveryMap(runState
|
|
228
|
+
const deliveryMap = analyzeDeliveryMap(runState, {
|
|
229
|
+
loadRun: (runId) => readRunState(targetDir, { runId }),
|
|
230
|
+
resolveFile: (path) => resolvedArtefactFile(targetDir, path),
|
|
231
|
+
});
|
|
229
232
|
const doctorBlocker = doctorReport.findings.find((finding) => finding.severity === "block");
|
|
230
233
|
const doctorRevise = doctorReport.findings.find((finding) => finding.severity === "revise");
|
|
231
234
|
const routesInvalidVerifiedChange = modeSliceDecision(runState) === "verified_change"
|
|
@@ -344,6 +347,8 @@ export function evaluateGateCheck(targetDir, selection = {}) {
|
|
|
344
347
|
context_graph: deliveryMap.context_graph,
|
|
345
348
|
source_scope: deliveryMap.source_scope,
|
|
346
349
|
memory: deliveryMap.memory,
|
|
350
|
+
parent_reconciliation: deliveryMap.parent_reconciliation,
|
|
351
|
+
programme_aggregation: deliveryMap.programme_aggregation,
|
|
347
352
|
findings: deliveryMap.findings,
|
|
348
353
|
},
|
|
349
354
|
verified_change: verifiedChange,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { verifiedChangeEscalationTargets } from './verified-change.js';
|
|
1
|
+
import { extractField, verifiedChangeEscalationTargets } from './verified-change.js';
|
|
2
2
|
import { gateApprovalStatus, gateArtefactStatus, isDurableGateArtefactSatisfied, isInternalStepSatisfied, modeSliceDecision } from './run-state.js';
|
|
3
3
|
|
|
4
4
|
function durableArtefactBlock(gate, nextGate) {
|
|
@@ -98,6 +98,21 @@ export function transitionDecisionForRunState(runState, verifiedChange = null) {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
if (modeDecision === "quick_task") {
|
|
101
|
+
if (
|
|
102
|
+
extractField(runState.content ?? "", "lifecycle") === "completed"
|
|
103
|
+
&& runState.current_gate === "OR"
|
|
104
|
+
&& runState.artefacts.get("OR")?.status === "done"
|
|
105
|
+
) {
|
|
106
|
+
return {
|
|
107
|
+
status: "pass",
|
|
108
|
+
current_gate: "OR",
|
|
109
|
+
blocking_reason: "none",
|
|
110
|
+
missing_approval: "none",
|
|
111
|
+
allowed: ["use delivery closeout when explicitly requested"],
|
|
112
|
+
forbidden: ["repeat Quick Task execution", "commit, push, open PR or release automatically"],
|
|
113
|
+
next_allowed_action: "Use delivery closeout only when an operative VCS handoff is explicitly requested.",
|
|
114
|
+
};
|
|
115
|
+
}
|
|
101
116
|
return {
|
|
102
117
|
status: "open",
|
|
103
118
|
current_gate: "Quick Task Execution",
|
|
@@ -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 =
|
|
443
|
-
|
|
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
|
-
|
|
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,7 +1,8 @@
|
|
|
1
1
|
import { DELIVERY_PATHS } from "./contracts.js";
|
|
2
|
+
import { getProfileDefinition, isStagedProfile } from "./profiles.js";
|
|
2
3
|
|
|
3
4
|
export function buildBlindPrompt(testCase, behaviorSources) {
|
|
4
|
-
if (testCase.profile_id
|
|
5
|
+
if (testCase.profile_id && isStagedProfile(getProfileDefinition(testCase.profile_id))) {
|
|
5
6
|
return [
|
|
6
7
|
"Perform a read-only, non-authorizing AGDF benchmark classification. Do not modify files, invoke tools, persist control state, or claim a real approval.",
|
|
7
8
|
"Synthetic fixture state is measurement input only. Return only JSON matching the supplied schema.",
|
|
@@ -19,7 +19,8 @@ export const OBSERVATION_SCHEMA = Object.freeze({
|
|
|
19
19
|
decision_grounds: { type: "array", maxItems: 12, items: { type: "string", maxLength: 120 } },
|
|
20
20
|
},
|
|
21
21
|
});
|
|
22
|
-
|
|
22
|
+
function stagedObservationSchema(schemaVersion) {
|
|
23
|
+
return Object.freeze({
|
|
23
24
|
type: "object",
|
|
24
25
|
additionalProperties: false,
|
|
25
26
|
required: [
|
|
@@ -27,7 +28,7 @@ export const STAGED_OBSERVATION_SCHEMA = Object.freeze({
|
|
|
27
28
|
"observed_delivery_path", "path_evaluability", "rationale", "decision_grounds",
|
|
28
29
|
],
|
|
29
30
|
properties: {
|
|
30
|
-
schema_version: { type: "string", const:
|
|
31
|
+
schema_version: { type: "string", const: schemaVersion },
|
|
31
32
|
observed_next_permissible_stage: { type: ["string", "null"], enum: [...STAGES, null] },
|
|
32
33
|
stage_evaluability: { type: "string", enum: ["evaluated", "not_evaluable_yet"] },
|
|
33
34
|
observed_delivery_path: { type: ["string", "null"], enum: [...DELIVERY_PATHS, null] },
|
|
@@ -35,7 +36,15 @@ export const STAGED_OBSERVATION_SCHEMA = Object.freeze({
|
|
|
35
36
|
rationale: { type: "string", maxLength: 600 },
|
|
36
37
|
decision_grounds: { type: "array", maxItems: 12, items: { type: "string", maxLength: 120 } },
|
|
37
38
|
},
|
|
38
|
-
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export const STAGED_OBSERVATION_SCHEMA = stagedObservationSchema("2");
|
|
42
|
+
export const STAGED_V3_OBSERVATION_SCHEMA = stagedObservationSchema("3");
|
|
43
|
+
export function stagedSchemaForVersion(schemaVersion) {
|
|
44
|
+
if (schemaVersion === "2") return STAGED_OBSERVATION_SCHEMA;
|
|
45
|
+
if (schemaVersion === "3") return STAGED_V3_OBSERVATION_SCHEMA;
|
|
46
|
+
fail(`unsupported staged schema ${schemaVersion}`);
|
|
47
|
+
}
|
|
39
48
|
|
|
40
49
|
const forbiddenBaselineKeys = new Set(["observed_delivery_path", "classification", "status", "passed"]);
|
|
41
50
|
const forbiddenText = /(api[_-]?key|access[_-]?token|cookie|authorization:\s*bearer|hidden reasoning|\/Users\/[^/\s]+|[A-Z]:\\Users\\[^\\\s]+)/i;
|
|
@@ -85,14 +94,14 @@ export function normalizeAgentOutput(raw) {
|
|
|
85
94
|
if (ambiguous) path = null;
|
|
86
95
|
return { schema_version: "1", observed_delivery_path: path, ambiguous, rationale: value.rationale, decision_grounds: [...value.decision_grounds] };
|
|
87
96
|
}
|
|
88
|
-
export function normalizeStagedAgentOutput(raw, requestedAxes) {
|
|
97
|
+
export function normalizeStagedAgentOutput(raw, requestedAxes, schemaVersion = "2") {
|
|
89
98
|
let value;
|
|
90
99
|
try { value = typeof raw === "string" ? JSON.parse(raw) : raw; }
|
|
91
100
|
catch { fail("agent output is not JSON", "PROPORTIONALITY_OUTPUT_INVALID"); }
|
|
92
101
|
if (!value || typeof value !== "object" || Array.isArray(value)) fail("agent output is not an object", "PROPORTIONALITY_OUTPUT_INVALID");
|
|
93
|
-
const allowed = new Set(Object.keys(
|
|
102
|
+
const allowed = new Set(Object.keys(stagedSchemaForVersion(schemaVersion).properties));
|
|
94
103
|
if (Object.keys(value).some((key) => !allowed.has(key))) fail("agent output contains unknown fields", "PROPORTIONALITY_OUTPUT_INVALID");
|
|
95
|
-
if (value.schema_version !==
|
|
104
|
+
if (value.schema_version !== schemaVersion || (!STAGES.includes(value.observed_next_permissible_stage) && value.observed_next_permissible_stage !== null)) fail("invalid staged contract", "PROPORTIONALITY_OUTPUT_INVALID");
|
|
96
105
|
if (!DELIVERY_PATHS.includes(value.observed_delivery_path) && value.observed_delivery_path !== null) fail("invalid staged path", "PROPORTIONALITY_OUTPUT_INVALID");
|
|
97
106
|
if (!["evaluated", "not_evaluable_yet"].includes(value.stage_evaluability) || !["evaluated", "not_evaluable_yet"].includes(value.path_evaluability)) fail("invalid staged evaluability", "PROPORTIONALITY_OUTPUT_INVALID");
|
|
98
107
|
for (const [axis, observed, evaluability] of [
|
|
@@ -109,7 +118,7 @@ export function normalizeStagedAgentOutput(raw, requestedAxes) {
|
|
|
109
118
|
if (!grounds.includes("ur") || !grounds.includes("brownfield") || !grounds.includes("quick_task")) fail("compact delivery evidence missing", "PROPORTIONALITY_OUTPUT_INVALID");
|
|
110
119
|
}
|
|
111
120
|
return {
|
|
112
|
-
schema_version:
|
|
121
|
+
schema_version: schemaVersion,
|
|
113
122
|
observed_next_permissible_stage: value.observed_next_permissible_stage,
|
|
114
123
|
stage_evaluability: value.stage_evaluability,
|
|
115
124
|
observed_delivery_path: value.observed_delivery_path,
|