create-agdf 0.8.3 → 0.8.5
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/bin/create-agdf.js +9 -0
- package/generated/.github/skills/agdf-gate-check/SKILL.md +8 -2
- package/generated/.github/skills/agdf-interaction-locales.json +32 -0
- package/generated/.github/skills/agdf-runtime-contract.md +15 -0
- package/generated/.opencode/agdf-interaction-locales.json +32 -0
- package/generated/.opencode/agdf-runtime-contract.md +15 -0
- package/generated/.opencode/skills/agdf-gate-check/SKILL.md +8 -2
- package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/meta/agdf-interaction-locales.json +32 -0
- package/generated/plugins/agdf/meta/agdf-plugin.definition.json +9 -1
- package/generated/plugins/agdf/meta/agdf-runtime-contract.md +15 -0
- package/generated/plugins/agdf/skills/gate-check/SKILL.md +8 -2
- package/lib/interaction-presentation.js +85 -2
- package/package.json +1 -1
package/bin/create-agdf.js
CHANGED
|
@@ -2259,10 +2259,14 @@ function buildStatusCard({
|
|
|
2259
2259
|
const qualityOutlook = deriveQualityOutlook(runState, findings);
|
|
2260
2260
|
const postApproval = postApprovalTransition(missingApproval);
|
|
2261
2261
|
const isUserGateApproval = missingApproval !== "none";
|
|
2262
|
+
const lifecycle = extractField(runState.content ?? "", "lifecycle") || "unknown";
|
|
2263
|
+
const nativeAttemptRequired = status === "open" && isUserGateApproval;
|
|
2262
2264
|
return {
|
|
2263
2265
|
run_id: extractField(runState.content ?? "", "run_id") || "unknown",
|
|
2264
2266
|
presentation_language: chatLanguage,
|
|
2265
2267
|
mode: extractField(runState.content ?? "", "mode") || "unknown",
|
|
2268
|
+
lifecycle,
|
|
2269
|
+
delivery_state: lifecycle === "completed" && currentGate === "OR" ? "completed_closeout_pending" : lifecycle,
|
|
2266
2270
|
status,
|
|
2267
2271
|
current_gate: currentGate,
|
|
2268
2272
|
mode_slice_decision: runState.mode_slice_decision?.decision || "undecided",
|
|
@@ -2280,6 +2284,8 @@ function buildStatusCard({
|
|
|
2280
2284
|
next_skill: nextSkillByGate[currentGate] ?? "gate-check",
|
|
2281
2285
|
next_step: nextAllowedAction,
|
|
2282
2286
|
quality_outlook: qualityOutlook,
|
|
2287
|
+
interaction_kind: nativeAttemptRequired ? "gate_approval" : status === "blocked" ? "blocked" : "status",
|
|
2288
|
+
native_attempt_required: nativeAttemptRequired,
|
|
2283
2289
|
};
|
|
2284
2290
|
}
|
|
2285
2291
|
|
|
@@ -2736,6 +2742,8 @@ function evaluateGateCheck(targetDir, selection = {}) {
|
|
|
2736
2742
|
doctor_summary: doctorReport.summary,
|
|
2737
2743
|
evidence_refs: runState.evidence_refs,
|
|
2738
2744
|
quality_outlook: deriveQualityOutlook(runState, deliveryMap.findings),
|
|
2745
|
+
interaction_kind: statusCard.interaction_kind,
|
|
2746
|
+
native_attempt_required: statusCard.native_attempt_required,
|
|
2739
2747
|
candidate_runs: runState.candidate_runs ?? [],
|
|
2740
2748
|
status_card: statusCard,
|
|
2741
2749
|
delivery_map: {
|
|
@@ -2764,6 +2772,7 @@ function printGateCheckStatusCard(report) {
|
|
|
2764
2772
|
console.log(`${labels.run}: ${card.run_id}`);
|
|
2765
2773
|
console.log(`${labels.humanTitle}: ${presentation.runTitle}`);
|
|
2766
2774
|
console.log(`${labels.gate}: ${card.current_gate} — ${presentation.gateTitle}`);
|
|
2775
|
+
console.log(`${labels.lifecycle}: ${primary.lifecycle[card.delivery_state] ?? primary.lifecycle.unknown}`);
|
|
2767
2776
|
console.log(`${labels.artefacts}: ${formatArtefactRefs(presentation.artefactRefs)}`);
|
|
2768
2777
|
console.log(`${labels.blocked}: ${card.blocking_condition === "none" ? primary.none : primary.blocked}`);
|
|
2769
2778
|
console.log(`${labels.missing}: ${card.missing_approval === "none" ? primary.none : card.missing_approval}`);
|
|
@@ -63,18 +63,24 @@ For a ready `gate_approval`:
|
|
|
63
63
|
1. Resolve exactly one selected run and evaluate the current gate.
|
|
64
64
|
2. Confirm the required durable artefact is present and ready before presenting a question.
|
|
65
65
|
3. Build one immutable, non-authorizing presentation snapshot from the selected evaluated run and current revision identity.
|
|
66
|
-
4. Compose one Approval Orientation Envelope as one immediately preceding assistant message containing two distinct blocks. Render the compact localized approval-time Run Status Card first. It contains exactly selected run, readiness status, current gate, missing exact approval, one next action and quality outlook; omit raw keys, machine statuses, evidence, diagnostics and allowed/forbidden inventories.
|
|
66
|
+
4. Compose one Approval Orientation Envelope as one immediately preceding assistant message containing two distinct blocks. Render the compact localized approval-time Run Status Card first. Its first visible line is the localized action-oriented gate title as a level-two Markdown heading or equivalent accessible host heading; generic AGDF/card labels and raw gate IDs cannot be the primary heading. It contains exactly selected run, readiness status, current gate, missing exact approval, one next action and quality outlook; omit raw keys, machine statuses, evidence, diagnostics and allowed/forbidden inventories.
|
|
67
67
|
5. In the same assistant message, render a separate localized Gate Transition Card second, immediately before any native question or exact-text fallback. It includes the deterministic human run title and `UR · PRD · SD · TP` links from the selected run. Never put either card only into the question, button description or hidden tool context. Do not invoke the native question tool until the complete two-card envelope is visible.
|
|
68
68
|
6. Compose the transition card as one human-readable gate title with `run_id`, one ready-for-decision line, one short approval-effect block and one short next-transition block. It must answer only: where am I, what does this decision do, and what happens next. Run and gate identity may anchor both cards; do not duplicate status rows, action inventories or the native question.
|
|
69
69
|
7. Distinguish internal process steps from user decisions in natural language. For example, after `Approval: TP`, say that pre-implementation Brownfield Analysis runs next and that no further user action is required now; do not expose `next_user_gate: none` or `user_action_required: no`, and do not ask for a second approval for Brownfield Analysis.
|
|
70
70
|
8. Ask exactly one question that names the selected `run_id` and `current_gate`.
|
|
71
71
|
9. Offer options in stable order: exact `Approval: <GateName>`, localized revise and localized decline. Add explicit cancel only where supported after decline; otherwise host dismissal maps to cancel. Never preselect, skip, auto-submit or reorder an option.
|
|
72
72
|
10. Use the surface adapter declared in the canonical plugin definition only when it can wait for deliberate input without auto-resolution; otherwise request the same exact approval in concise text.
|
|
73
|
-
11. Render both cards once in that single complete assistant message. If the native attempt is unavailable or not applied, continue to exact text without repeating either card.
|
|
73
|
+
11. Render both cards once in that single complete assistant message. Never merge, reverse, omit or duplicate them; the action heading does not replace either card. If the native attempt is unavailable or not applied, continue to exact text without repeating either card.
|
|
74
74
|
12. Re-run gate evaluation for the same run and expected gate after the response and immediately before persistence.
|
|
75
75
|
13. Reject a missing artefact, ambiguous or wrong run, wrong gate, stale expected gate, timeout/default, hook-supplied answer, agent message, technical permission outcome or plan approval.
|
|
76
76
|
14. Persist accepted input only through the existing control-state workflow.
|
|
77
77
|
|
|
78
|
+
The ready-gate projection must expose `native_attempt_required: true`. Treat this as a mandatory
|
|
79
|
+
orchestration instruction: after rendering the two cards, invoke the configured native question
|
|
80
|
+
adapter exactly once before using the exact-text fallback. A hook, permission result or session
|
|
81
|
+
context message cannot replace that attempt or supply its answer. For every non-ready interaction,
|
|
82
|
+
the signal is absent or false and no approval control may be shown.
|
|
83
|
+
|
|
78
84
|
Before composing the question, resolve the configured chat locale from `.agdf/control/config.json` through `../agdf-interaction-locales.json`: exact complete pack, language subtag, then English fallback. German and English are initial packs, not a closed language list. Keep `Approval: <GateName>` exactly unchanged in every language, and do not mix presentation languages within one interaction. Labels and descriptions use the same resolved locale; host-owned UI chrome remains host-owned and may use a different language.
|
|
79
85
|
|
|
80
86
|
For the transition card, use the complete resolved locale pack. The initial German pack includes `Bereit für deine Entscheidung`, `Jetzt freigeben` and `Danach`; the initial English pack includes `Ready for your decision`, `Approve now` and `Next`. Keep each card in one locale. Use a localized human-readable gate title rather than an internal status label, while leaving the exact gate identifier and approval value unchanged. Do not render a Markdown table, dashboard rows, raw control-state keys, diagnostic codes, evidence lists, a duplicated gate question or a false user gate in the approval-time card.
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"skill": "Next skill",
|
|
26
26
|
"step": "Next step",
|
|
27
27
|
"quality": "Quality outlook",
|
|
28
|
+
"lifecycle": "Delivery status",
|
|
28
29
|
"allowed": "Allowed now",
|
|
29
30
|
"forbidden": "Forbidden now",
|
|
30
31
|
"artefacts": "Artefacts"
|
|
@@ -64,6 +65,14 @@
|
|
|
64
65
|
"CR": "Code review",
|
|
65
66
|
"OR": "Delivery report"
|
|
66
67
|
},
|
|
68
|
+
"gateActionTitles": {
|
|
69
|
+
"UR": "Approve user requirements",
|
|
70
|
+
"PRD": "Approve product requirements",
|
|
71
|
+
"SD": "Approve solution design",
|
|
72
|
+
"TP": "Approve task and test plan",
|
|
73
|
+
"QA": "Approve quality decision",
|
|
74
|
+
"UAT": "Accept delivery result"
|
|
75
|
+
},
|
|
67
76
|
"interaction": {
|
|
68
77
|
"ready": "Ready for your decision",
|
|
69
78
|
"approveHeading": "Approve now",
|
|
@@ -91,6 +100,13 @@
|
|
|
91
100
|
"none": "none",
|
|
92
101
|
"blocked": "current gate prerequisites",
|
|
93
102
|
"quality": "Keep the presentation contract, evidence and gate authority aligned.",
|
|
103
|
+
"lifecycle": {
|
|
104
|
+
"active": "in progress",
|
|
105
|
+
"completed_closeout_pending": "completed — delivery closeout pending",
|
|
106
|
+
"completed": "completed",
|
|
107
|
+
"blocked": "blocked",
|
|
108
|
+
"unknown": "status not available"
|
|
109
|
+
},
|
|
94
110
|
"actions": {
|
|
95
111
|
"UR": "Clarify and approve the user requirements.",
|
|
96
112
|
"Brownfield Review": "Review existing owners and choose the smallest safe delivery path.",
|
|
@@ -132,6 +148,7 @@
|
|
|
132
148
|
"skill": "Nächster Skill",
|
|
133
149
|
"step": "Nächster Schritt",
|
|
134
150
|
"quality": "Qualitätsausblick",
|
|
151
|
+
"lifecycle": "Lieferstatus",
|
|
135
152
|
"allowed": "Jetzt erlaubt",
|
|
136
153
|
"forbidden": "Aktuell verboten",
|
|
137
154
|
"artefacts": "Artefakte"
|
|
@@ -171,6 +188,14 @@
|
|
|
171
188
|
"CR": "Code-Review",
|
|
172
189
|
"OR": "Lieferbericht"
|
|
173
190
|
},
|
|
191
|
+
"gateActionTitles": {
|
|
192
|
+
"UR": "Nutzeranforderung freigeben",
|
|
193
|
+
"PRD": "Produktanforderungen freigeben",
|
|
194
|
+
"SD": "Lösungsdesign freigeben",
|
|
195
|
+
"TP": "Umsetzungsplan freigeben",
|
|
196
|
+
"QA": "Qualitätsentscheidung freigeben",
|
|
197
|
+
"UAT": "Ergebnis abnehmen"
|
|
198
|
+
},
|
|
174
199
|
"interaction": {
|
|
175
200
|
"ready": "Bereit für deine Entscheidung",
|
|
176
201
|
"approveHeading": "Jetzt freigeben",
|
|
@@ -198,6 +223,13 @@
|
|
|
198
223
|
"none": "keine",
|
|
199
224
|
"blocked": "Voraussetzungen des aktuellen Gates",
|
|
200
225
|
"quality": "Präsentationsvertrag, Nachweise und Gate-Berechtigung konsistent halten.",
|
|
226
|
+
"lifecycle": {
|
|
227
|
+
"active": "in Arbeit",
|
|
228
|
+
"completed_closeout_pending": "abgeschlossen — Abschluss offen",
|
|
229
|
+
"completed": "abgeschlossen",
|
|
230
|
+
"blocked": "blockiert",
|
|
231
|
+
"unknown": "Status nicht verfügbar"
|
|
232
|
+
},
|
|
201
233
|
"actions": {
|
|
202
234
|
"UR": "Nutzeranforderungen klären und freigeben.",
|
|
203
235
|
"Brownfield Review": "Bestehende Owner prüfen und den kleinsten sicheren Lieferweg wählen.",
|
|
@@ -181,6 +181,14 @@ before invoking any native question tool or emitting an exact-text approval
|
|
|
181
181
|
request. Splitting the cards across separate assistant turns or invoking the
|
|
182
182
|
tool after only one card is a failed interaction, not a valid presentation.
|
|
183
183
|
|
|
184
|
+
The first visible line of that envelope is the localized action-oriented title
|
|
185
|
+
of the current user gate, rendered as a level-two Markdown heading or equivalent
|
|
186
|
+
accessible host heading inside the compact approval-time Run Status Card.
|
|
187
|
+
`AGDF Status`, `Run Status Card`, `Gate Transition Card`, raw gate identifiers
|
|
188
|
+
and machine values are semantic or diagnostic labels only and must not become
|
|
189
|
+
the primary heading. The action title appears exactly once and does not replace,
|
|
190
|
+
merge or reorder either required card block.
|
|
191
|
+
|
|
184
192
|
The compact approval-time Run Status Card contains exactly six operational
|
|
185
193
|
fields: selected run, readiness status, current gate, missing exact approval,
|
|
186
194
|
one next action and quality outlook. It uses localized human-readable labels
|
|
@@ -390,6 +398,7 @@ interaction_kind: clarification | tool_permission | gate_approval | blocked | st
|
|
|
390
398
|
surface: codex | claude | opencode | fallback
|
|
391
399
|
run_id: required for gate_approval
|
|
392
400
|
current_gate: required for gate_approval
|
|
401
|
+
native_attempt_required: true only for an eligible gate_approval before the first adapter attempt
|
|
393
402
|
prompt: required
|
|
394
403
|
options: required for a structured question
|
|
395
404
|
effects: required when the interaction can cause side effects
|
|
@@ -420,6 +429,12 @@ Before presenting `gate_approval`, the agent must:
|
|
|
420
429
|
6. reject missing evidence, ambiguous or wrong run, wrong gate, stale state and any response that is no longer valid;
|
|
421
430
|
7. persist an accepted approval only through the existing control-state workflow.
|
|
422
431
|
|
|
432
|
+
For an eligible `gate_approval`, `native_attempt_required` is a mandatory orchestration signal. The
|
|
433
|
+
agent must make exactly one configured native adapter attempt after the two orientation cards and
|
|
434
|
+
before any exact-text fallback. Hooks may prepare context but must not answer, approve or replace
|
|
435
|
+
the adapter attempt. The signal is absent or false for clarification, blocked, status-only,
|
|
436
|
+
internal-step and non-ready interactions.
|
|
437
|
+
|
|
423
438
|
The first eligible native-attempt has a single bounded outcome: the declared
|
|
424
439
|
host control is presented, or the agent immediately uses the exact-text
|
|
425
440
|
fallback. A host that does not apply the control does not trigger a second
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"skill": "Next skill",
|
|
26
26
|
"step": "Next step",
|
|
27
27
|
"quality": "Quality outlook",
|
|
28
|
+
"lifecycle": "Delivery status",
|
|
28
29
|
"allowed": "Allowed now",
|
|
29
30
|
"forbidden": "Forbidden now",
|
|
30
31
|
"artefacts": "Artefacts"
|
|
@@ -64,6 +65,14 @@
|
|
|
64
65
|
"CR": "Code review",
|
|
65
66
|
"OR": "Delivery report"
|
|
66
67
|
},
|
|
68
|
+
"gateActionTitles": {
|
|
69
|
+
"UR": "Approve user requirements",
|
|
70
|
+
"PRD": "Approve product requirements",
|
|
71
|
+
"SD": "Approve solution design",
|
|
72
|
+
"TP": "Approve task and test plan",
|
|
73
|
+
"QA": "Approve quality decision",
|
|
74
|
+
"UAT": "Accept delivery result"
|
|
75
|
+
},
|
|
67
76
|
"interaction": {
|
|
68
77
|
"ready": "Ready for your decision",
|
|
69
78
|
"approveHeading": "Approve now",
|
|
@@ -91,6 +100,13 @@
|
|
|
91
100
|
"none": "none",
|
|
92
101
|
"blocked": "current gate prerequisites",
|
|
93
102
|
"quality": "Keep the presentation contract, evidence and gate authority aligned.",
|
|
103
|
+
"lifecycle": {
|
|
104
|
+
"active": "in progress",
|
|
105
|
+
"completed_closeout_pending": "completed — delivery closeout pending",
|
|
106
|
+
"completed": "completed",
|
|
107
|
+
"blocked": "blocked",
|
|
108
|
+
"unknown": "status not available"
|
|
109
|
+
},
|
|
94
110
|
"actions": {
|
|
95
111
|
"UR": "Clarify and approve the user requirements.",
|
|
96
112
|
"Brownfield Review": "Review existing owners and choose the smallest safe delivery path.",
|
|
@@ -132,6 +148,7 @@
|
|
|
132
148
|
"skill": "Nächster Skill",
|
|
133
149
|
"step": "Nächster Schritt",
|
|
134
150
|
"quality": "Qualitätsausblick",
|
|
151
|
+
"lifecycle": "Lieferstatus",
|
|
135
152
|
"allowed": "Jetzt erlaubt",
|
|
136
153
|
"forbidden": "Aktuell verboten",
|
|
137
154
|
"artefacts": "Artefakte"
|
|
@@ -171,6 +188,14 @@
|
|
|
171
188
|
"CR": "Code-Review",
|
|
172
189
|
"OR": "Lieferbericht"
|
|
173
190
|
},
|
|
191
|
+
"gateActionTitles": {
|
|
192
|
+
"UR": "Nutzeranforderung freigeben",
|
|
193
|
+
"PRD": "Produktanforderungen freigeben",
|
|
194
|
+
"SD": "Lösungsdesign freigeben",
|
|
195
|
+
"TP": "Umsetzungsplan freigeben",
|
|
196
|
+
"QA": "Qualitätsentscheidung freigeben",
|
|
197
|
+
"UAT": "Ergebnis abnehmen"
|
|
198
|
+
},
|
|
174
199
|
"interaction": {
|
|
175
200
|
"ready": "Bereit für deine Entscheidung",
|
|
176
201
|
"approveHeading": "Jetzt freigeben",
|
|
@@ -198,6 +223,13 @@
|
|
|
198
223
|
"none": "keine",
|
|
199
224
|
"blocked": "Voraussetzungen des aktuellen Gates",
|
|
200
225
|
"quality": "Präsentationsvertrag, Nachweise und Gate-Berechtigung konsistent halten.",
|
|
226
|
+
"lifecycle": {
|
|
227
|
+
"active": "in Arbeit",
|
|
228
|
+
"completed_closeout_pending": "abgeschlossen — Abschluss offen",
|
|
229
|
+
"completed": "abgeschlossen",
|
|
230
|
+
"blocked": "blockiert",
|
|
231
|
+
"unknown": "Status nicht verfügbar"
|
|
232
|
+
},
|
|
201
233
|
"actions": {
|
|
202
234
|
"UR": "Nutzeranforderungen klären und freigeben.",
|
|
203
235
|
"Brownfield Review": "Bestehende Owner prüfen und den kleinsten sicheren Lieferweg wählen.",
|
|
@@ -181,6 +181,14 @@ before invoking any native question tool or emitting an exact-text approval
|
|
|
181
181
|
request. Splitting the cards across separate assistant turns or invoking the
|
|
182
182
|
tool after only one card is a failed interaction, not a valid presentation.
|
|
183
183
|
|
|
184
|
+
The first visible line of that envelope is the localized action-oriented title
|
|
185
|
+
of the current user gate, rendered as a level-two Markdown heading or equivalent
|
|
186
|
+
accessible host heading inside the compact approval-time Run Status Card.
|
|
187
|
+
`AGDF Status`, `Run Status Card`, `Gate Transition Card`, raw gate identifiers
|
|
188
|
+
and machine values are semantic or diagnostic labels only and must not become
|
|
189
|
+
the primary heading. The action title appears exactly once and does not replace,
|
|
190
|
+
merge or reorder either required card block.
|
|
191
|
+
|
|
184
192
|
The compact approval-time Run Status Card contains exactly six operational
|
|
185
193
|
fields: selected run, readiness status, current gate, missing exact approval,
|
|
186
194
|
one next action and quality outlook. It uses localized human-readable labels
|
|
@@ -390,6 +398,7 @@ interaction_kind: clarification | tool_permission | gate_approval | blocked | st
|
|
|
390
398
|
surface: codex | claude | opencode | fallback
|
|
391
399
|
run_id: required for gate_approval
|
|
392
400
|
current_gate: required for gate_approval
|
|
401
|
+
native_attempt_required: true only for an eligible gate_approval before the first adapter attempt
|
|
393
402
|
prompt: required
|
|
394
403
|
options: required for a structured question
|
|
395
404
|
effects: required when the interaction can cause side effects
|
|
@@ -420,6 +429,12 @@ Before presenting `gate_approval`, the agent must:
|
|
|
420
429
|
6. reject missing evidence, ambiguous or wrong run, wrong gate, stale state and any response that is no longer valid;
|
|
421
430
|
7. persist an accepted approval only through the existing control-state workflow.
|
|
422
431
|
|
|
432
|
+
For an eligible `gate_approval`, `native_attempt_required` is a mandatory orchestration signal. The
|
|
433
|
+
agent must make exactly one configured native adapter attempt after the two orientation cards and
|
|
434
|
+
before any exact-text fallback. Hooks may prepare context but must not answer, approve or replace
|
|
435
|
+
the adapter attempt. The signal is absent or false for clarification, blocked, status-only,
|
|
436
|
+
internal-step and non-ready interactions.
|
|
437
|
+
|
|
423
438
|
The first eligible native-attempt has a single bounded outcome: the declared
|
|
424
439
|
host control is presented, or the agent immediately uses the exact-text
|
|
425
440
|
fallback. A host that does not apply the control does not trigger a second
|
|
@@ -63,18 +63,24 @@ For a ready `gate_approval`:
|
|
|
63
63
|
1. Resolve exactly one selected run and evaluate the current gate.
|
|
64
64
|
2. Confirm the required durable artefact is present and ready before presenting a question.
|
|
65
65
|
3. Build one immutable, non-authorizing presentation snapshot from the selected evaluated run and current revision identity.
|
|
66
|
-
4. Compose one Approval Orientation Envelope as one immediately preceding assistant message containing two distinct blocks. Render the compact localized approval-time Run Status Card first. It contains exactly selected run, readiness status, current gate, missing exact approval, one next action and quality outlook; omit raw keys, machine statuses, evidence, diagnostics and allowed/forbidden inventories.
|
|
66
|
+
4. Compose one Approval Orientation Envelope as one immediately preceding assistant message containing two distinct blocks. Render the compact localized approval-time Run Status Card first. Its first visible line is the localized action-oriented gate title as a level-two Markdown heading or equivalent accessible host heading; generic AGDF/card labels and raw gate IDs cannot be the primary heading. It contains exactly selected run, readiness status, current gate, missing exact approval, one next action and quality outlook; omit raw keys, machine statuses, evidence, diagnostics and allowed/forbidden inventories.
|
|
67
67
|
5. In the same assistant message, render a separate localized Gate Transition Card second, immediately before any native question or exact-text fallback. It includes the deterministic human run title and `UR · PRD · SD · TP` links from the selected run. Never put either card only into the question, button description or hidden tool context. Do not invoke the native question tool until the complete two-card envelope is visible.
|
|
68
68
|
6. Compose the transition card as one human-readable gate title with `run_id`, one ready-for-decision line, one short approval-effect block and one short next-transition block. It must answer only: where am I, what does this decision do, and what happens next. Run and gate identity may anchor both cards; do not duplicate status rows, action inventories or the native question.
|
|
69
69
|
7. Distinguish internal process steps from user decisions in natural language. For example, after `Approval: TP`, say that pre-implementation Brownfield Analysis runs next and that no further user action is required now; do not expose `next_user_gate: none` or `user_action_required: no`, and do not ask for a second approval for Brownfield Analysis.
|
|
70
70
|
8. Ask exactly one question that names the selected `run_id` and `current_gate`.
|
|
71
71
|
9. Offer options in stable order: exact `Approval: <GateName>`, localized revise and localized decline. Add explicit cancel only where supported after decline; otherwise host dismissal maps to cancel. Never preselect, skip, auto-submit or reorder an option.
|
|
72
72
|
10. Use the surface adapter declared in the canonical plugin definition only when it can wait for deliberate input without auto-resolution; otherwise request the same exact approval in concise text.
|
|
73
|
-
11. Render both cards once in that single complete assistant message. If the native attempt is unavailable or not applied, continue to exact text without repeating either card.
|
|
73
|
+
11. Render both cards once in that single complete assistant message. Never merge, reverse, omit or duplicate them; the action heading does not replace either card. If the native attempt is unavailable or not applied, continue to exact text without repeating either card.
|
|
74
74
|
12. Re-run gate evaluation for the same run and expected gate after the response and immediately before persistence.
|
|
75
75
|
13. Reject a missing artefact, ambiguous or wrong run, wrong gate, stale expected gate, timeout/default, hook-supplied answer, agent message, technical permission outcome or plan approval.
|
|
76
76
|
14. Persist accepted input only through the existing control-state workflow.
|
|
77
77
|
|
|
78
|
+
The ready-gate projection must expose `native_attempt_required: true`. Treat this as a mandatory
|
|
79
|
+
orchestration instruction: after rendering the two cards, invoke the configured native question
|
|
80
|
+
adapter exactly once before using the exact-text fallback. A hook, permission result or session
|
|
81
|
+
context message cannot replace that attempt or supply its answer. For every non-ready interaction,
|
|
82
|
+
the signal is absent or false and no approval control may be shown.
|
|
83
|
+
|
|
78
84
|
Before composing the question, resolve the configured chat locale from `.agdf/control/config.json` through `../../agdf-interaction-locales.json`: exact complete pack, language subtag, then English fallback. German and English are initial packs, not a closed language list. Keep `Approval: <GateName>` exactly unchanged in every language, and do not mix presentation languages within one interaction. Labels and descriptions use the same resolved locale; host-owned UI chrome remains host-owned and may use a different language.
|
|
79
85
|
|
|
80
86
|
For the transition card, use the complete resolved locale pack. The initial German pack includes `Bereit für deine Entscheidung`, `Jetzt freigeben` and `Danach`; the initial English pack includes `Ready for your decision`, `Approve now` and `Next`. Keep each card in one locale. Use a localized human-readable gate title rather than an internal status label, while leaving the exact gate identifier and approval value unchanged. Do not render a Markdown table, dashboard rows, raw control-state keys, diagnostic codes, evidence lists, a duplicated gate question or a false user gate in the approval-time card.
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"skill": "Next skill",
|
|
26
26
|
"step": "Next step",
|
|
27
27
|
"quality": "Quality outlook",
|
|
28
|
+
"lifecycle": "Delivery status",
|
|
28
29
|
"allowed": "Allowed now",
|
|
29
30
|
"forbidden": "Forbidden now",
|
|
30
31
|
"artefacts": "Artefacts"
|
|
@@ -64,6 +65,14 @@
|
|
|
64
65
|
"CR": "Code review",
|
|
65
66
|
"OR": "Delivery report"
|
|
66
67
|
},
|
|
68
|
+
"gateActionTitles": {
|
|
69
|
+
"UR": "Approve user requirements",
|
|
70
|
+
"PRD": "Approve product requirements",
|
|
71
|
+
"SD": "Approve solution design",
|
|
72
|
+
"TP": "Approve task and test plan",
|
|
73
|
+
"QA": "Approve quality decision",
|
|
74
|
+
"UAT": "Accept delivery result"
|
|
75
|
+
},
|
|
67
76
|
"interaction": {
|
|
68
77
|
"ready": "Ready for your decision",
|
|
69
78
|
"approveHeading": "Approve now",
|
|
@@ -91,6 +100,13 @@
|
|
|
91
100
|
"none": "none",
|
|
92
101
|
"blocked": "current gate prerequisites",
|
|
93
102
|
"quality": "Keep the presentation contract, evidence and gate authority aligned.",
|
|
103
|
+
"lifecycle": {
|
|
104
|
+
"active": "in progress",
|
|
105
|
+
"completed_closeout_pending": "completed — delivery closeout pending",
|
|
106
|
+
"completed": "completed",
|
|
107
|
+
"blocked": "blocked",
|
|
108
|
+
"unknown": "status not available"
|
|
109
|
+
},
|
|
94
110
|
"actions": {
|
|
95
111
|
"UR": "Clarify and approve the user requirements.",
|
|
96
112
|
"Brownfield Review": "Review existing owners and choose the smallest safe delivery path.",
|
|
@@ -132,6 +148,7 @@
|
|
|
132
148
|
"skill": "Nächster Skill",
|
|
133
149
|
"step": "Nächster Schritt",
|
|
134
150
|
"quality": "Qualitätsausblick",
|
|
151
|
+
"lifecycle": "Lieferstatus",
|
|
135
152
|
"allowed": "Jetzt erlaubt",
|
|
136
153
|
"forbidden": "Aktuell verboten",
|
|
137
154
|
"artefacts": "Artefakte"
|
|
@@ -171,6 +188,14 @@
|
|
|
171
188
|
"CR": "Code-Review",
|
|
172
189
|
"OR": "Lieferbericht"
|
|
173
190
|
},
|
|
191
|
+
"gateActionTitles": {
|
|
192
|
+
"UR": "Nutzeranforderung freigeben",
|
|
193
|
+
"PRD": "Produktanforderungen freigeben",
|
|
194
|
+
"SD": "Lösungsdesign freigeben",
|
|
195
|
+
"TP": "Umsetzungsplan freigeben",
|
|
196
|
+
"QA": "Qualitätsentscheidung freigeben",
|
|
197
|
+
"UAT": "Ergebnis abnehmen"
|
|
198
|
+
},
|
|
174
199
|
"interaction": {
|
|
175
200
|
"ready": "Bereit für deine Entscheidung",
|
|
176
201
|
"approveHeading": "Jetzt freigeben",
|
|
@@ -198,6 +223,13 @@
|
|
|
198
223
|
"none": "keine",
|
|
199
224
|
"blocked": "Voraussetzungen des aktuellen Gates",
|
|
200
225
|
"quality": "Präsentationsvertrag, Nachweise und Gate-Berechtigung konsistent halten.",
|
|
226
|
+
"lifecycle": {
|
|
227
|
+
"active": "in Arbeit",
|
|
228
|
+
"completed_closeout_pending": "abgeschlossen — Abschluss offen",
|
|
229
|
+
"completed": "abgeschlossen",
|
|
230
|
+
"blocked": "blockiert",
|
|
231
|
+
"unknown": "Status nicht verfügbar"
|
|
232
|
+
},
|
|
201
233
|
"actions": {
|
|
202
234
|
"UR": "Nutzeranforderungen klären und freigeben.",
|
|
203
235
|
"Brownfield Review": "Bestehende Owner prüfen und den kleinsten sicheren Lieferweg wählen.",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"id": "agdf",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.5",
|
|
5
5
|
"displayName": "AI Governance & Delivery Framework",
|
|
6
6
|
"developerName": "Arndt Gold",
|
|
7
7
|
"description": "Codex-first operating system for governed AI-assisted delivery.",
|
|
@@ -97,6 +97,8 @@
|
|
|
97
97
|
"decline"
|
|
98
98
|
],
|
|
99
99
|
"dismissOutcome": "cancel",
|
|
100
|
+
"canonicalValueTransport": false,
|
|
101
|
+
"authorizationPath": "exact_text",
|
|
100
102
|
"technicalPermissionOwner": "codex_host"
|
|
101
103
|
},
|
|
102
104
|
"claude": {
|
|
@@ -109,6 +111,8 @@
|
|
|
109
111
|
"decline"
|
|
110
112
|
],
|
|
111
113
|
"dismissOutcome": "cancel",
|
|
114
|
+
"canonicalValueTransport": false,
|
|
115
|
+
"authorizationPath": "exact_text",
|
|
112
116
|
"technicalPermissionOwner": "claude_host"
|
|
113
117
|
},
|
|
114
118
|
"opencode": {
|
|
@@ -121,6 +125,8 @@
|
|
|
121
125
|
"decline"
|
|
122
126
|
],
|
|
123
127
|
"dismissOutcome": "cancel",
|
|
128
|
+
"canonicalValueTransport": false,
|
|
129
|
+
"authorizationPath": "exact_text",
|
|
124
130
|
"technicalPermissionOwner": "opencode_host"
|
|
125
131
|
},
|
|
126
132
|
"fallback": {
|
|
@@ -133,6 +139,8 @@
|
|
|
133
139
|
"decline",
|
|
134
140
|
"cancel"
|
|
135
141
|
],
|
|
142
|
+
"canonicalValueTransport": true,
|
|
143
|
+
"authorizationPath": "exact_text",
|
|
136
144
|
"technicalPermissionOwner": "host_specific"
|
|
137
145
|
}
|
|
138
146
|
}
|
|
@@ -181,6 +181,14 @@ before invoking any native question tool or emitting an exact-text approval
|
|
|
181
181
|
request. Splitting the cards across separate assistant turns or invoking the
|
|
182
182
|
tool after only one card is a failed interaction, not a valid presentation.
|
|
183
183
|
|
|
184
|
+
The first visible line of that envelope is the localized action-oriented title
|
|
185
|
+
of the current user gate, rendered as a level-two Markdown heading or equivalent
|
|
186
|
+
accessible host heading inside the compact approval-time Run Status Card.
|
|
187
|
+
`AGDF Status`, `Run Status Card`, `Gate Transition Card`, raw gate identifiers
|
|
188
|
+
and machine values are semantic or diagnostic labels only and must not become
|
|
189
|
+
the primary heading. The action title appears exactly once and does not replace,
|
|
190
|
+
merge or reorder either required card block.
|
|
191
|
+
|
|
184
192
|
The compact approval-time Run Status Card contains exactly six operational
|
|
185
193
|
fields: selected run, readiness status, current gate, missing exact approval,
|
|
186
194
|
one next action and quality outlook. It uses localized human-readable labels
|
|
@@ -390,6 +398,7 @@ interaction_kind: clarification | tool_permission | gate_approval | blocked | st
|
|
|
390
398
|
surface: codex | claude | opencode | fallback
|
|
391
399
|
run_id: required for gate_approval
|
|
392
400
|
current_gate: required for gate_approval
|
|
401
|
+
native_attempt_required: true only for an eligible gate_approval before the first adapter attempt
|
|
393
402
|
prompt: required
|
|
394
403
|
options: required for a structured question
|
|
395
404
|
effects: required when the interaction can cause side effects
|
|
@@ -420,6 +429,12 @@ Before presenting `gate_approval`, the agent must:
|
|
|
420
429
|
6. reject missing evidence, ambiguous or wrong run, wrong gate, stale state and any response that is no longer valid;
|
|
421
430
|
7. persist an accepted approval only through the existing control-state workflow.
|
|
422
431
|
|
|
432
|
+
For an eligible `gate_approval`, `native_attempt_required` is a mandatory orchestration signal. The
|
|
433
|
+
agent must make exactly one configured native adapter attempt after the two orientation cards and
|
|
434
|
+
before any exact-text fallback. Hooks may prepare context but must not answer, approve or replace
|
|
435
|
+
the adapter attempt. The signal is absent or false for clarification, blocked, status-only,
|
|
436
|
+
internal-step and non-ready interactions.
|
|
437
|
+
|
|
423
438
|
The first eligible native-attempt has a single bounded outcome: the declared
|
|
424
439
|
host control is presented, or the agent immediately uses the exact-text
|
|
425
440
|
fallback. A host that does not apply the control does not trigger a second
|
|
@@ -63,18 +63,24 @@ For a ready `gate_approval`:
|
|
|
63
63
|
1. Resolve exactly one selected run and evaluate the current gate.
|
|
64
64
|
2. Confirm the required durable artefact is present and ready before presenting a question.
|
|
65
65
|
3. Build one immutable, non-authorizing presentation snapshot from the selected evaluated run and current revision identity.
|
|
66
|
-
4. Compose one Approval Orientation Envelope as one immediately preceding assistant message containing two distinct blocks. Render the compact localized approval-time Run Status Card first. It contains exactly selected run, readiness status, current gate, missing exact approval, one next action and quality outlook; omit raw keys, machine statuses, evidence, diagnostics and allowed/forbidden inventories.
|
|
66
|
+
4. Compose one Approval Orientation Envelope as one immediately preceding assistant message containing two distinct blocks. Render the compact localized approval-time Run Status Card first. Its first visible line is the localized action-oriented gate title as a level-two Markdown heading or equivalent accessible host heading; generic AGDF/card labels and raw gate IDs cannot be the primary heading. It contains exactly selected run, readiness status, current gate, missing exact approval, one next action and quality outlook; omit raw keys, machine statuses, evidence, diagnostics and allowed/forbidden inventories.
|
|
67
67
|
5. In the same assistant message, render a separate localized Gate Transition Card second, immediately before any native question or exact-text fallback. It includes the deterministic human run title and `UR · PRD · SD · TP` links from the selected run. Never put either card only into the question, button description or hidden tool context. Do not invoke the native question tool until the complete two-card envelope is visible.
|
|
68
68
|
6. Compose the transition card as one human-readable gate title with `run_id`, one ready-for-decision line, one short approval-effect block and one short next-transition block. It must answer only: where am I, what does this decision do, and what happens next. Run and gate identity may anchor both cards; do not duplicate status rows, action inventories or the native question.
|
|
69
69
|
7. Distinguish internal process steps from user decisions in natural language. For example, after `Approval: TP`, say that pre-implementation Brownfield Analysis runs next and that no further user action is required now; do not expose `next_user_gate: none` or `user_action_required: no`, and do not ask for a second approval for Brownfield Analysis.
|
|
70
70
|
8. Ask exactly one question that names the selected `run_id` and `current_gate`.
|
|
71
71
|
9. Offer options in stable order: exact `Approval: <GateName>`, localized revise and localized decline. Add explicit cancel only where supported after decline; otherwise host dismissal maps to cancel. Never preselect, skip, auto-submit or reorder an option.
|
|
72
72
|
10. Use the surface adapter declared in the canonical plugin definition only when it can wait for deliberate input without auto-resolution; otherwise request the same exact approval in concise text.
|
|
73
|
-
11. Render both cards once in that single complete assistant message. If the native attempt is unavailable or not applied, continue to exact text without repeating either card.
|
|
73
|
+
11. Render both cards once in that single complete assistant message. Never merge, reverse, omit or duplicate them; the action heading does not replace either card. If the native attempt is unavailable or not applied, continue to exact text without repeating either card.
|
|
74
74
|
12. Re-run gate evaluation for the same run and expected gate after the response and immediately before persistence.
|
|
75
75
|
13. Reject a missing artefact, ambiguous or wrong run, wrong gate, stale expected gate, timeout/default, hook-supplied answer, agent message, technical permission outcome or plan approval.
|
|
76
76
|
14. Persist accepted input only through the existing control-state workflow.
|
|
77
77
|
|
|
78
|
+
The ready-gate projection must expose `native_attempt_required: true`. Treat this as a mandatory
|
|
79
|
+
orchestration instruction: after rendering the two cards, invoke the configured native question
|
|
80
|
+
adapter exactly once before using the exact-text fallback. A hook, permission result or session
|
|
81
|
+
context message cannot replace that attempt or supply its answer. For every non-ready interaction,
|
|
82
|
+
the signal is absent or false and no approval control may be shown.
|
|
83
|
+
|
|
78
84
|
Before composing the question, resolve the configured chat locale from `.agdf/control/config.json` through `plugin/meta/agdf-interaction-locales.json`: exact complete pack, language subtag, then English fallback. German and English are initial packs, not a closed language list. Keep `Approval: <GateName>` exactly unchanged in every language, and do not mix presentation languages within one interaction. Labels and descriptions use the same resolved locale; host-owned UI chrome remains host-owned and may use a different language.
|
|
79
85
|
|
|
80
86
|
For the transition card, use the complete resolved locale pack. The initial German pack includes `Bereit für deine Entscheidung`, `Jetzt freigeben` and `Danach`; the initial English pack includes `Ready for your decision`, `Approve now` and `Next`. Keep each card in one locale. Use a localized human-readable gate title rather than an internal status label, while leaving the exact gate identifier and approval value unchanged. Do not render a Markdown table, dashboard rows, raw control-state keys, diagnostic codes, evidence lists, a duplicated gate question or a false user gate in the approval-time card.
|
|
@@ -70,6 +70,7 @@ export function validateLocaleRegistry(registry) {
|
|
|
70
70
|
}
|
|
71
71
|
for (const key of REQUIRED_GATES) {
|
|
72
72
|
if (typeof pack.gateTitles?.[key] !== "string" || !pack.gateTitles[key].trim()) errors.push(`missing_gate_title:${locale}:${key}`);
|
|
73
|
+
if (typeof pack.gateActionTitles?.[key] !== "string" || !pack.gateActionTitles[key].trim()) errors.push(`missing_gate_action_title:${locale}:${key}`);
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
if (JSON.stringify(registry?.optionOrder) !== JSON.stringify(["approve", "revise", "decline", "cancel"])) errors.push("option_order");
|
|
@@ -147,6 +148,59 @@ export function buildRunCandidates(runs) {
|
|
|
147
148
|
.sort((left, right) => left.display_title.localeCompare(right.display_title) || left.run_id.localeCompare(right.run_id));
|
|
148
149
|
}
|
|
149
150
|
|
|
151
|
+
export function normalizeReconciliationText(value) {
|
|
152
|
+
return String(value ?? "")
|
|
153
|
+
.normalize("NFKC")
|
|
154
|
+
.toLowerCase()
|
|
155
|
+
.replace(/[._:/\\-]+/g, " ")
|
|
156
|
+
.replace(/[^\p{L}\p{N}\s]+/gu, " ")
|
|
157
|
+
.replace(/\s+/g, " ")
|
|
158
|
+
.trim();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function reconcileRunScope({ requestText = "", scopeKey = "", runs = [] } = {}) {
|
|
162
|
+
const requested = normalizeReconciliationText(scopeKey || requestText);
|
|
163
|
+
if (!requested) return Object.freeze({ outcome: "match_uncertain", matches: [], reason: "scope_input_missing" });
|
|
164
|
+
const matches = [...(runs ?? [])]
|
|
165
|
+
.filter((run) => run?.valid && ["active", "completed"].includes(run?.meta?.lifecycle) && typeof run.run_id === "string")
|
|
166
|
+
.filter((run) => {
|
|
167
|
+
const keys = [run.scope_key, run.display_title, run.current_artefact_heading, run.ur_heading]
|
|
168
|
+
.map(normalizeReconciliationText)
|
|
169
|
+
.filter(Boolean);
|
|
170
|
+
return keys.includes(requested);
|
|
171
|
+
})
|
|
172
|
+
.map((run) => Object.freeze({
|
|
173
|
+
run_id: run.run_id,
|
|
174
|
+
lifecycle: run.meta.lifecycle,
|
|
175
|
+
display_title: displaySafeTitle(run.display_title || run.ur_heading || run.run_id, normalizedRunTitle(run.run_id)),
|
|
176
|
+
current_gate: run.control_state?.current_gate || run.meta?.current_gate || "unknown",
|
|
177
|
+
}))
|
|
178
|
+
.sort((left, right) => left.run_id.localeCompare(right.run_id));
|
|
179
|
+
if (matches.length !== 1) return Object.freeze({ outcome: matches.length ? "match_uncertain" : "no_match", matches, reason: matches.length > 1 ? "multiple_exact_matches" : "no_exact_match" });
|
|
180
|
+
return Object.freeze({ outcome: matches[0].lifecycle === "active" ? "active_match" : "completed_match", matches, reason: "exact_match" });
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function executeNativeApprovalAttempt({ ready = false, orientationSnapshot, invokeNative, fallback } = {}) {
|
|
184
|
+
if (!ready) return Object.freeze({ attempted: false, outcome: "unavailable_before_invocation", reason: "gate_not_ready", authorizes: false });
|
|
185
|
+
const preflight = validateApprovalOrientationSnapshot(orientationSnapshot);
|
|
186
|
+
if (!preflight.valid) return Object.freeze({ attempted: false, outcome: "unavailable_before_invocation", reason: "orientation_preflight_failed", authorizes: false });
|
|
187
|
+
if (typeof invokeNative !== "function") return Object.freeze({ attempted: false, outcome: "unavailable_before_invocation", reason: "adapter_unavailable", authorizes: false });
|
|
188
|
+
let adapterResult;
|
|
189
|
+
try {
|
|
190
|
+
adapterResult = invokeNative();
|
|
191
|
+
} catch (error) {
|
|
192
|
+
adapterResult = { outcome: "attempted_not_applied", reason: String(error?.message || "adapter_error") };
|
|
193
|
+
}
|
|
194
|
+
const outcome = ATTEMPT_OUTCOMES.has(adapterResult?.outcome)
|
|
195
|
+
? adapterResult.outcome
|
|
196
|
+
: adapterResult?.presented === true || adapterResult?.applied === true
|
|
197
|
+
? "presented"
|
|
198
|
+
: "attempted_not_applied";
|
|
199
|
+
const reason = String(adapterResult?.reason || "").trim();
|
|
200
|
+
if (outcome !== "presented" && typeof fallback === "function") fallback({ outcome, reason });
|
|
201
|
+
return Object.freeze({ attempted: true, outcome, reason, authorizes: false });
|
|
202
|
+
}
|
|
203
|
+
|
|
150
204
|
export function buildInteractionAttempt({ interactionId, runId, currentGate, surface, attemptOutcome, expectedApproval, fallbackReason = "" }) {
|
|
151
205
|
if (!ATTEMPT_OUTCOMES.has(attemptOutcome)) throw new Error("invalid interaction attempt outcome");
|
|
152
206
|
if (!String(interactionId ?? "").trim() || !String(runId ?? "").trim() || !String(currentGate ?? "").trim())
|
|
@@ -229,6 +283,7 @@ export function buildApprovalOrientationSnapshot({
|
|
|
229
283
|
const pack = localePack(registry, locale);
|
|
230
284
|
const runTitle = String(humanPresentation.runTitle ?? "").trim() || normalizedRunTitle(runId);
|
|
231
285
|
const currentGateTitle = String(humanPresentation.gateTitle ?? "").trim() || gateTitle(registry, locale, gate);
|
|
286
|
+
const primaryHeading = String(pack.gateActionTitles?.[gate] ?? "").trim();
|
|
232
287
|
const artefactRefs = Object.freeze([...(humanPresentation.artefactRefs ?? [])].map((ref) => Object.freeze({ ...ref })));
|
|
233
288
|
const statusFields = Object.freeze([
|
|
234
289
|
Object.freeze({ id: "selected_run", label: pack.statusCard.run, value: `${runTitle} · ${runId}` }),
|
|
@@ -247,8 +302,15 @@ export function buildApprovalOrientationSnapshot({
|
|
|
247
302
|
current_gate: gate,
|
|
248
303
|
presentation_language: locale,
|
|
249
304
|
sequence: APPROVAL_SEQUENCE,
|
|
250
|
-
compact_status_card: Object.freeze({
|
|
305
|
+
compact_status_card: Object.freeze({
|
|
306
|
+
semantic_block: "run_status_card",
|
|
307
|
+
title: primaryHeading,
|
|
308
|
+
primary_heading: primaryHeading,
|
|
309
|
+
primary_heading_level: 2,
|
|
310
|
+
fields: statusFields,
|
|
311
|
+
}),
|
|
251
312
|
gate_transition_card: Object.freeze({
|
|
313
|
+
semantic_block: "gate_transition_card",
|
|
252
314
|
title: `${currentGateTitle} · ${runTitle} · ${runId}`,
|
|
253
315
|
artefact_refs: artefactRefs,
|
|
254
316
|
ready: pack.interaction.ready,
|
|
@@ -258,11 +320,32 @@ export function buildApprovalOrientationSnapshot({
|
|
|
258
320
|
next_heading: pack.interaction.nextHeading,
|
|
259
321
|
next_gate: String(statusCard.next_gate_after_approval ?? "none"),
|
|
260
322
|
}),
|
|
261
|
-
approval_interaction: Object.freeze({ expected_approval: expectedApproval, options, authorizes: false }),
|
|
323
|
+
approval_interaction: Object.freeze({ semantic_block: "approval_interaction", expected_approval: expectedApproval, options, authorizes: false }),
|
|
262
324
|
authorizes: false,
|
|
263
325
|
});
|
|
264
326
|
}
|
|
265
327
|
|
|
328
|
+
export function validateApprovalOrientationSnapshot(snapshot) {
|
|
329
|
+
const errors = [];
|
|
330
|
+
if (!plainObject(snapshot)) return Object.freeze({ valid: false, errors: Object.freeze(["snapshot_missing"]) });
|
|
331
|
+
if (JSON.stringify(snapshot.sequence) !== JSON.stringify(APPROVAL_SEQUENCE)) errors.push("sequence");
|
|
332
|
+
if (snapshot.compact_status_card?.semantic_block !== "run_status_card") errors.push("run_status_card");
|
|
333
|
+
if (snapshot.gate_transition_card?.semantic_block !== "gate_transition_card") errors.push("gate_transition_card");
|
|
334
|
+
if (snapshot.approval_interaction?.semantic_block !== "approval_interaction") errors.push("approval_interaction");
|
|
335
|
+
const heading = String(snapshot.compact_status_card?.primary_heading ?? "").trim();
|
|
336
|
+
if (!heading || snapshot.compact_status_card?.primary_heading_level !== 2) errors.push("primary_heading");
|
|
337
|
+
if (String(snapshot.compact_status_card?.title ?? "").trim() !== heading) errors.push("primary_heading_owner");
|
|
338
|
+
if (/^(?:(?:agdf[- ]?status(?: card|karte)?|run status card|gate transition card)(?:\b|\s*[-:—·])|(?:UR|PRD|SD|TP|QA|UAT)(?:\s*[-:—·]|$))/i.test(heading)) errors.push("generic_primary_heading");
|
|
339
|
+
const fieldIds = snapshot.compact_status_card?.fields?.map((field) => field?.id);
|
|
340
|
+
if (JSON.stringify(fieldIds) !== JSON.stringify(["selected_run", "readiness_status", "current_gate", "missing_approval", "next_action", "quality_outlook"])) errors.push("status_fields");
|
|
341
|
+
const expectedApproval = `Approval: ${String(snapshot.current_gate ?? "").trim()}`;
|
|
342
|
+
if (!REQUIRED_GATES.includes(snapshot.current_gate)
|
|
343
|
+
|| snapshot.gate_transition_card?.exact_approval !== expectedApproval
|
|
344
|
+
|| snapshot.approval_interaction?.expected_approval !== expectedApproval
|
|
345
|
+
|| snapshot.approval_interaction?.options?.[0]?.value !== expectedApproval) errors.push("canonical_approval");
|
|
346
|
+
return Object.freeze({ valid: errors.length === 0, errors: Object.freeze(errors) });
|
|
347
|
+
}
|
|
348
|
+
|
|
266
349
|
export function attachApprovalOrientationSnapshot(statusCard, options = {}) {
|
|
267
350
|
if (!plainObject(statusCard)) throw new Error("approval orientation status card missing");
|
|
268
351
|
const snapshot = buildApprovalOrientationSnapshot({ ...options, statusCard });
|
package/package.json
CHANGED