create-agdf 0.10.2 → 0.10.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/.github/skills/agdf-gate-check/SKILL.md +11 -18
- package/generated/.github/skills/agdf-interaction-locales.json +8 -0
- package/generated/.github/skills/agdf-release-or/SKILL.md +1 -1
- package/generated/.github/skills/contracts/interaction.md +23 -0
- package/generated/.opencode/agdf-interaction-locales.json +8 -0
- package/generated/.opencode/contracts/interaction.md +23 -0
- package/generated/.opencode/skills/agdf-gate-check/SKILL.md +11 -18
- package/generated/.opencode/skills/agdf-release-or/SKILL.md +1 -1
- package/generated/plugins/agdf/.claude-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/meta/agdf-interaction-locales.json +8 -0
- package/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
- package/generated/plugins/agdf/meta/contracts/interaction.md +23 -0
- package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +8 -0
- 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/cli/validation-handlers.js +2 -1
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/gate-check.js +16 -25
- package/generated/plugins/agdf/runtime/create-agdf/lib/interaction-presentation.js +80 -0
- 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/check-runtime-integrity.mjs +20 -5
- package/generated/plugins/agdf/skills/gate-check/SKILL.md +11 -18
- package/generated/plugins/agdf/skills/release-or/SKILL.md +1 -1
- package/lib/cli/validation-handlers.js +2 -1
- package/lib/control-evaluation/gate-check.js +16 -25
- package/lib/installers/opencode.js +52 -4
- package/lib/interaction-presentation.js +80 -0
- package/package.json +1 -1
|
@@ -85,10 +85,11 @@ This skill owns only these operational responsibilities:
|
|
|
85
85
|
5. Revalidate the same run, gate and revision immediately after the response and before persistence.
|
|
86
86
|
6. Persist only a currently valid exact approval through the existing control-state workflow.
|
|
87
87
|
|
|
88
|
-
For status, blocked, read-only or rationale interactions, consume the
|
|
89
|
-
locale contract without asking for approval.
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
For status, blocked, read-only or rationale interactions, consume the canonical
|
|
89
|
+
`status_presentation.markdown` verbatim and apply the locale contract without asking for approval.
|
|
90
|
+
Do not reconstruct its fields, ordering, labels or Markdown from `status_card` JSON. Internal process
|
|
91
|
+
steps remain distinct from user decisions. If the projection, adapter evidence or revalidation is
|
|
92
|
+
missing, follow the contract's fail-closed outcome and leave authority unchanged.
|
|
92
93
|
|
|
93
94
|
## Rules
|
|
94
95
|
1. Fail closed when a required approval or artefact status is missing.
|
|
@@ -163,23 +164,15 @@ If a status is not explicit, do not assume it is satisfied.
|
|
|
163
164
|
12. If branch, workspace and durable artefacts disagree, do not choose a scope silently; report the ambiguity and ask for the smallest clarifying gate action.
|
|
164
165
|
|
|
165
166
|
## Output
|
|
166
|
-
Keep the result short and operational.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
| Current gate | `<GateName or internal step>` |
|
|
172
|
-
| Allowed now | `<currently allowed outputs>` |
|
|
173
|
-
| Blocked by | `<blocking condition or none>` |
|
|
174
|
-
| Missing approval | `Approval: <GateName> | none` |
|
|
175
|
-
| Next step | `<single permissible next step>` |
|
|
176
|
-
| Quality outlook | `<next meaningful quality focus or none>` |
|
|
177
|
-
|
|
178
|
-
When an approval is missing, also include `Next gate after approval` and `Allowed after approval` exactly as constrained by the Runtime Contract. Keep forbidden outputs, evidence and next-skill detail in the concise surrounding text when relevant; they are not extra status-card rows.
|
|
167
|
+
Keep the result short and operational. For a status-only response, consume the Runtime Contract's
|
|
168
|
+
code-owned `status_presentation.markdown` verbatim. It is the compact localized operational Run
|
|
169
|
+
Status Card and already includes current and post-approval authority, next-step and quality fields.
|
|
170
|
+
Keep full raw evidence in `status_card` JSON or concise surrounding detail when relevant; do not turn
|
|
171
|
+
the primary chat card into an audit dump. Do not maintain or render a skill-local table template.
|
|
179
172
|
|
|
180
173
|
This complete operational status table remains the status-reporting and detail
|
|
181
174
|
surface. When the same response immediately requests a ready gate approval,
|
|
182
|
-
render its
|
|
175
|
+
render its five-field compact approval-time projection first, then the Gate
|
|
183
176
|
Transition Card in the same immediately preceding assistant message, then
|
|
184
177
|
invoke exactly one native question or exact-text fallback. Both cards derive
|
|
185
178
|
from the same snapshot and are shown exactly once.
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
"en": {
|
|
12
12
|
"statusCard": {
|
|
13
13
|
"title": "AGDF status-card",
|
|
14
|
+
"status": "Status",
|
|
15
|
+
"field": "Status field",
|
|
16
|
+
"value": "Value",
|
|
14
17
|
"run": "Selected run",
|
|
15
18
|
"humanTitle": "Run title",
|
|
16
19
|
"gate": "Current gate",
|
|
@@ -107,6 +110,7 @@
|
|
|
107
110
|
"exactTextRequest": "To approve, reply exactly with `{approval}`; otherwise request revision or decline.",
|
|
108
111
|
"decisionFollows": "will be the next user decision",
|
|
109
112
|
"presentationFailure": "Approval cards could not be rendered safely.",
|
|
113
|
+
"statusPresentationFailure": "Status card could not render safely.",
|
|
110
114
|
"nonReadyDecision": "No decision requested: {reason}.",
|
|
111
115
|
"approveDescription": "Approve this gate with the exact authorization value.",
|
|
112
116
|
"reviseLabel": "Revise",
|
|
@@ -214,6 +218,9 @@
|
|
|
214
218
|
"de": {
|
|
215
219
|
"statusCard": {
|
|
216
220
|
"title": "AGDF-Statuskarte",
|
|
221
|
+
"status": "Status",
|
|
222
|
+
"field": "Statusfeld",
|
|
223
|
+
"value": "Wert",
|
|
217
224
|
"run": "Ausgewählter Run",
|
|
218
225
|
"humanTitle": "Run-Titel",
|
|
219
226
|
"gate": "Aktuelles Gate",
|
|
@@ -310,6 +317,7 @@
|
|
|
310
317
|
"exactTextRequest": "Zur Freigabe exakt mit `{approval}` antworten; andernfalls Überarbeitung anfordern oder ablehnen.",
|
|
311
318
|
"decisionFollows": "ist die nächste Nutzerentscheidung",
|
|
312
319
|
"presentationFailure": "Die Freigabekarten konnten nicht sicher dargestellt werden.",
|
|
320
|
+
"statusPresentationFailure": "Statuskarte nicht sicher darstellbar.",
|
|
313
321
|
"nonReadyDecision": "Keine Entscheidung angefordert: {reason}.",
|
|
314
322
|
"approveDescription": "Dieses Gate mit dem exakten Freigabewert autorisieren.",
|
|
315
323
|
"reviseLabel": "Überarbeiten",
|
|
@@ -52,7 +52,7 @@ Use the Runtime Contract definition of `Relevant Run` to decide whether OR is ma
|
|
|
52
52
|
13. Keep machine status normalization in the CLI; do not write internal snake_case status values into the human-facing backlog.
|
|
53
53
|
14. Reconcile Context Graph impact before clean closeout: report `context_graph_reconciliation: resolved | not_applicable | open_gap`.
|
|
54
54
|
15. If Context Graph work remains unresolved, report it as an explicit open gap and do not describe the run as cleanly handoff-ready.
|
|
55
|
-
|
|
55
|
+
16. After writing or updating `MASTER_BACKLOG.md`, run `doctor --json` (or the locally available equivalent) and resolve any `AGDF_BACKLOG_STATUS_UNKNOWN` or `AGDF_BACKLOG_ARTEFACT_LABEL_UNKNOWN` finding before closeout. See the AGDF control scaffold's `MASTER_BACKLOG.md` template Rules section for the canonical status/artefact label vocabulary.
|
|
56
56
|
|
|
57
57
|
## When To Use
|
|
58
58
|
- at the end of every relevant run
|
|
@@ -40,6 +40,29 @@ It must not introduce a second gate model or override `agdf-gate-check`, `delive
|
|
|
40
40
|
|
|
41
41
|
`next_gate_after_approval` and `allowed_after_approval` must be `none` when no approval is missing, when the current step is internal, or when the run is in OR/completed handoff. They must not imply implementation, QA, release, commit, push or PR authority unless the existing gate model already allows that authority.
|
|
42
42
|
|
|
43
|
+
### Deterministic Operational Presentation
|
|
44
|
+
|
|
45
|
+
`status_card` is the canonical machine/audit projection. The same evaluation also exposes an additive
|
|
46
|
+
`status_presentation` object with selected run/revision/gate/locale identity, `semantic_block:
|
|
47
|
+
run_status_card`, one compact Markdown block with localized labels and canonical evaluated values,
|
|
48
|
+
and `authorizes: false`. The full raw and evidence-bearing projection remains available unchanged in
|
|
49
|
+
`status_card` JSON for audit and automation; it is not duplicated into the primary chat card.
|
|
50
|
+
Fresh legacy-compatible control state without a durable revision uses the explicit non-authorizing
|
|
51
|
+
identity `unversioned`; ready approval presentation remains impossible without a real revision.
|
|
52
|
+
|
|
53
|
+
`interaction-presentation.js` is the only owner that may select, label, order, localize and render the
|
|
54
|
+
operational status fields. The gate-check CLI, skills and surface adapters consume
|
|
55
|
+
`status_presentation.markdown` verbatim. They may add concise surrounding explanation, but they must
|
|
56
|
+
not reconstruct a table, omit or rename fields, substitute generic gate actions for evaluated
|
|
57
|
+
`allowed_now`/`forbidden_now`, or maintain another status-card template. If the projection is missing
|
|
58
|
+
or its selected run, revision, gate or locale identity is stale, presentation fails closed rather
|
|
59
|
+
than falling back to model-generated status Markdown.
|
|
60
|
+
|
|
61
|
+
This determinism protects semantic parity, permission boundaries and evidence visibility across
|
|
62
|
+
hosts. It does not standardize host chrome or unrelated conversational prose. `approval_presentation`
|
|
63
|
+
remains the separate decision-time projection and is present only for a ready user gate; both public
|
|
64
|
+
presentations derive from the same `status_card` snapshot and neither grants approval.
|
|
65
|
+
|
|
43
66
|
|
|
44
67
|
### Breadcrumb
|
|
45
68
|
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
"en": {
|
|
12
12
|
"statusCard": {
|
|
13
13
|
"title": "AGDF status-card",
|
|
14
|
+
"status": "Status",
|
|
15
|
+
"field": "Status field",
|
|
16
|
+
"value": "Value",
|
|
14
17
|
"run": "Selected run",
|
|
15
18
|
"humanTitle": "Run title",
|
|
16
19
|
"gate": "Current gate",
|
|
@@ -107,6 +110,7 @@
|
|
|
107
110
|
"exactTextRequest": "To approve, reply exactly with `{approval}`; otherwise request revision or decline.",
|
|
108
111
|
"decisionFollows": "will be the next user decision",
|
|
109
112
|
"presentationFailure": "Approval cards could not be rendered safely.",
|
|
113
|
+
"statusPresentationFailure": "Status card could not render safely.",
|
|
110
114
|
"nonReadyDecision": "No decision requested: {reason}.",
|
|
111
115
|
"approveDescription": "Approve this gate with the exact authorization value.",
|
|
112
116
|
"reviseLabel": "Revise",
|
|
@@ -214,6 +218,9 @@
|
|
|
214
218
|
"de": {
|
|
215
219
|
"statusCard": {
|
|
216
220
|
"title": "AGDF-Statuskarte",
|
|
221
|
+
"status": "Status",
|
|
222
|
+
"field": "Statusfeld",
|
|
223
|
+
"value": "Wert",
|
|
217
224
|
"run": "Ausgewählter Run",
|
|
218
225
|
"humanTitle": "Run-Titel",
|
|
219
226
|
"gate": "Aktuelles Gate",
|
|
@@ -310,6 +317,7 @@
|
|
|
310
317
|
"exactTextRequest": "Zur Freigabe exakt mit `{approval}` antworten; andernfalls Überarbeitung anfordern oder ablehnen.",
|
|
311
318
|
"decisionFollows": "ist die nächste Nutzerentscheidung",
|
|
312
319
|
"presentationFailure": "Die Freigabekarten konnten nicht sicher dargestellt werden.",
|
|
320
|
+
"statusPresentationFailure": "Statuskarte nicht sicher darstellbar.",
|
|
313
321
|
"nonReadyDecision": "Keine Entscheidung angefordert: {reason}.",
|
|
314
322
|
"approveDescription": "Dieses Gate mit dem exakten Freigabewert autorisieren.",
|
|
315
323
|
"reviseLabel": "Überarbeiten",
|
|
@@ -40,6 +40,29 @@ It must not introduce a second gate model or override `agdf-gate-check`, `delive
|
|
|
40
40
|
|
|
41
41
|
`next_gate_after_approval` and `allowed_after_approval` must be `none` when no approval is missing, when the current step is internal, or when the run is in OR/completed handoff. They must not imply implementation, QA, release, commit, push or PR authority unless the existing gate model already allows that authority.
|
|
42
42
|
|
|
43
|
+
### Deterministic Operational Presentation
|
|
44
|
+
|
|
45
|
+
`status_card` is the canonical machine/audit projection. The same evaluation also exposes an additive
|
|
46
|
+
`status_presentation` object with selected run/revision/gate/locale identity, `semantic_block:
|
|
47
|
+
run_status_card`, one compact Markdown block with localized labels and canonical evaluated values,
|
|
48
|
+
and `authorizes: false`. The full raw and evidence-bearing projection remains available unchanged in
|
|
49
|
+
`status_card` JSON for audit and automation; it is not duplicated into the primary chat card.
|
|
50
|
+
Fresh legacy-compatible control state without a durable revision uses the explicit non-authorizing
|
|
51
|
+
identity `unversioned`; ready approval presentation remains impossible without a real revision.
|
|
52
|
+
|
|
53
|
+
`interaction-presentation.js` is the only owner that may select, label, order, localize and render the
|
|
54
|
+
operational status fields. The gate-check CLI, skills and surface adapters consume
|
|
55
|
+
`status_presentation.markdown` verbatim. They may add concise surrounding explanation, but they must
|
|
56
|
+
not reconstruct a table, omit or rename fields, substitute generic gate actions for evaluated
|
|
57
|
+
`allowed_now`/`forbidden_now`, or maintain another status-card template. If the projection is missing
|
|
58
|
+
or its selected run, revision, gate or locale identity is stale, presentation fails closed rather
|
|
59
|
+
than falling back to model-generated status Markdown.
|
|
60
|
+
|
|
61
|
+
This determinism protects semantic parity, permission boundaries and evidence visibility across
|
|
62
|
+
hosts. It does not standardize host chrome or unrelated conversational prose. `approval_presentation`
|
|
63
|
+
remains the separate decision-time projection and is present only for a ready user gate; both public
|
|
64
|
+
presentations derive from the same `status_card` snapshot and neither grants approval.
|
|
65
|
+
|
|
43
66
|
|
|
44
67
|
### Breadcrumb
|
|
45
68
|
|
|
@@ -85,10 +85,11 @@ This skill owns only these operational responsibilities:
|
|
|
85
85
|
5. Revalidate the same run, gate and revision immediately after the response and before persistence.
|
|
86
86
|
6. Persist only a currently valid exact approval through the existing control-state workflow.
|
|
87
87
|
|
|
88
|
-
For status, blocked, read-only or rationale interactions, consume the
|
|
89
|
-
locale contract without asking for approval.
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
For status, blocked, read-only or rationale interactions, consume the canonical
|
|
89
|
+
`status_presentation.markdown` verbatim and apply the locale contract without asking for approval.
|
|
90
|
+
Do not reconstruct its fields, ordering, labels or Markdown from `status_card` JSON. Internal process
|
|
91
|
+
steps remain distinct from user decisions. If the projection, adapter evidence or revalidation is
|
|
92
|
+
missing, follow the contract's fail-closed outcome and leave authority unchanged.
|
|
92
93
|
|
|
93
94
|
## Rules
|
|
94
95
|
1. Fail closed when a required approval or artefact status is missing.
|
|
@@ -163,23 +164,15 @@ If a status is not explicit, do not assume it is satisfied.
|
|
|
163
164
|
12. If branch, workspace and durable artefacts disagree, do not choose a scope silently; report the ambiguity and ask for the smallest clarifying gate action.
|
|
164
165
|
|
|
165
166
|
## Output
|
|
166
|
-
Keep the result short and operational.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
| Current gate | `<GateName or internal step>` |
|
|
172
|
-
| Allowed now | `<currently allowed outputs>` |
|
|
173
|
-
| Blocked by | `<blocking condition or none>` |
|
|
174
|
-
| Missing approval | `Approval: <GateName> | none` |
|
|
175
|
-
| Next step | `<single permissible next step>` |
|
|
176
|
-
| Quality outlook | `<next meaningful quality focus or none>` |
|
|
177
|
-
|
|
178
|
-
When an approval is missing, also include `Next gate after approval` and `Allowed after approval` exactly as constrained by the Runtime Contract. Keep forbidden outputs, evidence and next-skill detail in the concise surrounding text when relevant; they are not extra status-card rows.
|
|
167
|
+
Keep the result short and operational. For a status-only response, consume the Runtime Contract's
|
|
168
|
+
code-owned `status_presentation.markdown` verbatim. It is the compact localized operational Run
|
|
169
|
+
Status Card and already includes current and post-approval authority, next-step and quality fields.
|
|
170
|
+
Keep full raw evidence in `status_card` JSON or concise surrounding detail when relevant; do not turn
|
|
171
|
+
the primary chat card into an audit dump. Do not maintain or render a skill-local table template.
|
|
179
172
|
|
|
180
173
|
This complete operational status table remains the status-reporting and detail
|
|
181
174
|
surface. When the same response immediately requests a ready gate approval,
|
|
182
|
-
render its
|
|
175
|
+
render its five-field compact approval-time projection first, then the Gate
|
|
183
176
|
Transition Card in the same immediately preceding assistant message, then
|
|
184
177
|
invoke exactly one native question or exact-text fallback. Both cards derive
|
|
185
178
|
from the same snapshot and are shown exactly once.
|
|
@@ -52,7 +52,7 @@ Use the Runtime Contract definition of `Relevant Run` to decide whether OR is ma
|
|
|
52
52
|
13. Keep machine status normalization in the CLI; do not write internal snake_case status values into the human-facing backlog.
|
|
53
53
|
14. Reconcile Context Graph impact before clean closeout: report `context_graph_reconciliation: resolved | not_applicable | open_gap`.
|
|
54
54
|
15. If Context Graph work remains unresolved, report it as an explicit open gap and do not describe the run as cleanly handoff-ready.
|
|
55
|
-
|
|
55
|
+
16. After writing or updating `MASTER_BACKLOG.md`, run `doctor --json` (or the locally available equivalent) and resolve any `AGDF_BACKLOG_STATUS_UNKNOWN` or `AGDF_BACKLOG_ARTEFACT_LABEL_UNKNOWN` finding before closeout. See the AGDF control scaffold's `MASTER_BACKLOG.md` template Rules section for the canonical status/artefact label vocabulary.
|
|
56
56
|
|
|
57
57
|
## When To Use
|
|
58
58
|
- at the end of every relevant run
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agdf",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"description": "AI Governance & Delivery Framework (AGDF) brings an operating system for governed AI-assisted delivery to Claude Code, combining workflow skills, control templates and machine-readable repository checks for gate decisions, Brownfield analysis, task-plan review, QA decisions, durable run state and auditable closeout.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agentic",
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
"en": {
|
|
12
12
|
"statusCard": {
|
|
13
13
|
"title": "AGDF status-card",
|
|
14
|
+
"status": "Status",
|
|
15
|
+
"field": "Status field",
|
|
16
|
+
"value": "Value",
|
|
14
17
|
"run": "Selected run",
|
|
15
18
|
"humanTitle": "Run title",
|
|
16
19
|
"gate": "Current gate",
|
|
@@ -107,6 +110,7 @@
|
|
|
107
110
|
"exactTextRequest": "To approve, reply exactly with `{approval}`; otherwise request revision or decline.",
|
|
108
111
|
"decisionFollows": "will be the next user decision",
|
|
109
112
|
"presentationFailure": "Approval cards could not be rendered safely.",
|
|
113
|
+
"statusPresentationFailure": "Status card could not render safely.",
|
|
110
114
|
"nonReadyDecision": "No decision requested: {reason}.",
|
|
111
115
|
"approveDescription": "Approve this gate with the exact authorization value.",
|
|
112
116
|
"reviseLabel": "Revise",
|
|
@@ -214,6 +218,9 @@
|
|
|
214
218
|
"de": {
|
|
215
219
|
"statusCard": {
|
|
216
220
|
"title": "AGDF-Statuskarte",
|
|
221
|
+
"status": "Status",
|
|
222
|
+
"field": "Statusfeld",
|
|
223
|
+
"value": "Wert",
|
|
217
224
|
"run": "Ausgewählter Run",
|
|
218
225
|
"humanTitle": "Run-Titel",
|
|
219
226
|
"gate": "Aktuelles Gate",
|
|
@@ -310,6 +317,7 @@
|
|
|
310
317
|
"exactTextRequest": "Zur Freigabe exakt mit `{approval}` antworten; andernfalls Überarbeitung anfordern oder ablehnen.",
|
|
311
318
|
"decisionFollows": "ist die nächste Nutzerentscheidung",
|
|
312
319
|
"presentationFailure": "Die Freigabekarten konnten nicht sicher dargestellt werden.",
|
|
320
|
+
"statusPresentationFailure": "Statuskarte nicht sicher darstellbar.",
|
|
313
321
|
"nonReadyDecision": "Keine Entscheidung angefordert: {reason}.",
|
|
314
322
|
"approveDescription": "Dieses Gate mit dem exakten Freigabewert autorisieren.",
|
|
315
323
|
"reviseLabel": "Überarbeiten",
|
|
@@ -40,6 +40,29 @@ It must not introduce a second gate model or override `gate-check`, `delivery-ma
|
|
|
40
40
|
|
|
41
41
|
`next_gate_after_approval` and `allowed_after_approval` must be `none` when no approval is missing, when the current step is internal, or when the run is in OR/completed handoff. They must not imply implementation, QA, release, commit, push or PR authority unless the existing gate model already allows that authority.
|
|
42
42
|
|
|
43
|
+
### Deterministic Operational Presentation
|
|
44
|
+
|
|
45
|
+
`status_card` is the canonical machine/audit projection. The same evaluation also exposes an additive
|
|
46
|
+
`status_presentation` object with selected run/revision/gate/locale identity, `semantic_block:
|
|
47
|
+
run_status_card`, one compact Markdown block with localized labels and canonical evaluated values,
|
|
48
|
+
and `authorizes: false`. The full raw and evidence-bearing projection remains available unchanged in
|
|
49
|
+
`status_card` JSON for audit and automation; it is not duplicated into the primary chat card.
|
|
50
|
+
Fresh legacy-compatible control state without a durable revision uses the explicit non-authorizing
|
|
51
|
+
identity `unversioned`; ready approval presentation remains impossible without a real revision.
|
|
52
|
+
|
|
53
|
+
`interaction-presentation.js` is the only owner that may select, label, order, localize and render the
|
|
54
|
+
operational status fields. The gate-check CLI, skills and surface adapters consume
|
|
55
|
+
`status_presentation.markdown` verbatim. They may add concise surrounding explanation, but they must
|
|
56
|
+
not reconstruct a table, omit or rename fields, substitute generic gate actions for evaluated
|
|
57
|
+
`allowed_now`/`forbidden_now`, or maintain another status-card template. If the projection is missing
|
|
58
|
+
or its selected run, revision, gate or locale identity is stale, presentation fails closed rather
|
|
59
|
+
than falling back to model-generated status Markdown.
|
|
60
|
+
|
|
61
|
+
This determinism protects semantic parity, permission boundaries and evidence visibility across
|
|
62
|
+
hosts. It does not standardize host chrome or unrelated conversational prose. `approval_presentation`
|
|
63
|
+
remains the separate decision-time projection and is present only for a ready user gate; both public
|
|
64
|
+
presentations derive from the same `status_card` snapshot and neither grants approval.
|
|
65
|
+
|
|
43
66
|
|
|
44
67
|
### Breadcrumb
|
|
45
68
|
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
"en": {
|
|
12
12
|
"statusCard": {
|
|
13
13
|
"title": "AGDF status-card",
|
|
14
|
+
"status": "Status",
|
|
15
|
+
"field": "Status field",
|
|
16
|
+
"value": "Value",
|
|
14
17
|
"run": "Selected run",
|
|
15
18
|
"humanTitle": "Run title",
|
|
16
19
|
"gate": "Current gate",
|
|
@@ -107,6 +110,7 @@
|
|
|
107
110
|
"exactTextRequest": "To approve, reply exactly with `{approval}`; otherwise request revision or decline.",
|
|
108
111
|
"decisionFollows": "will be the next user decision",
|
|
109
112
|
"presentationFailure": "Approval cards could not be rendered safely.",
|
|
113
|
+
"statusPresentationFailure": "Status card could not render safely.",
|
|
110
114
|
"nonReadyDecision": "No decision requested: {reason}.",
|
|
111
115
|
"approveDescription": "Approve this gate with the exact authorization value.",
|
|
112
116
|
"reviseLabel": "Revise",
|
|
@@ -214,6 +218,9 @@
|
|
|
214
218
|
"de": {
|
|
215
219
|
"statusCard": {
|
|
216
220
|
"title": "AGDF-Statuskarte",
|
|
221
|
+
"status": "Status",
|
|
222
|
+
"field": "Statusfeld",
|
|
223
|
+
"value": "Wert",
|
|
217
224
|
"run": "Ausgewählter Run",
|
|
218
225
|
"humanTitle": "Run-Titel",
|
|
219
226
|
"gate": "Aktuelles Gate",
|
|
@@ -310,6 +317,7 @@
|
|
|
310
317
|
"exactTextRequest": "Zur Freigabe exakt mit `{approval}` antworten; andernfalls Überarbeitung anfordern oder ablehnen.",
|
|
311
318
|
"decisionFollows": "ist die nächste Nutzerentscheidung",
|
|
312
319
|
"presentationFailure": "Die Freigabekarten konnten nicht sicher dargestellt werden.",
|
|
320
|
+
"statusPresentationFailure": "Statuskarte nicht sicher darstellbar.",
|
|
313
321
|
"nonReadyDecision": "Keine Entscheidung angefordert: {reason}.",
|
|
314
322
|
"approveDescription": "Dieses Gate mit dem exakten Freigabewert autorisieren.",
|
|
315
323
|
"reviseLabel": "Überarbeiten",
|
|
@@ -24,7 +24,8 @@ export function createValidationHandlers(io = console) {
|
|
|
24
24
|
const output = printApprovalEnvelope(report, { io, reEvaluate: () => evaluateGateCheck(options.dir, options) });
|
|
25
25
|
return output.status === "blocked" ? 2 : 0;
|
|
26
26
|
}
|
|
27
|
-
printGateCheckReport(report, options.json, options.statusCard, io);
|
|
27
|
+
const presentationRendered = printGateCheckReport(report, options.json, options.statusCard, io);
|
|
28
|
+
if (presentationRendered === false) return 2;
|
|
28
29
|
return report.status === "blocked" ? 2 : 0;
|
|
29
30
|
}],
|
|
30
31
|
["delivery-map", (options) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { attachApprovalOrientationSnapshot, buildArtefactRefs, buildQualityReadiness,
|
|
1
|
+
import { attachApprovalOrientationSnapshot, buildArtefactRefs, buildQualityReadiness, gateTitle, localePack, renderApprovalOrientationSnapshot, renderOperationalStatusCard, resolveHumanRunTitle } from '../interaction-presentation.js';
|
|
2
2
|
import { interactionLocales, resolveConfiguredChatLanguage } from '../cli/runtime-context.js';
|
|
3
3
|
import { evaluateDoctor } from './doctor.js';
|
|
4
4
|
import { analyzeDeliveryMap, deriveQualityOutlook } from './delivery-map.js';
|
|
@@ -292,6 +292,11 @@ export function evaluateGateCheck(targetDir, selection = {}) {
|
|
|
292
292
|
enumerable: false,
|
|
293
293
|
});
|
|
294
294
|
const revisionId = extractField(runState.content ?? "", "revision_id");
|
|
295
|
+
const statusPresentation = renderOperationalStatusCard(statusCard, {
|
|
296
|
+
registry: interactionLocales,
|
|
297
|
+
humanPresentation,
|
|
298
|
+
revisionId,
|
|
299
|
+
});
|
|
295
300
|
const approvalOrientation = attachApprovalOrientationSnapshot(statusCard, {
|
|
296
301
|
ready: isReadyUserGateApproval({ status, currentGate, missingApproval }),
|
|
297
302
|
humanPresentation,
|
|
@@ -323,6 +328,7 @@ export function evaluateGateCheck(targetDir, selection = {}) {
|
|
|
323
328
|
native_attempt_required: statusCard.native_attempt_required,
|
|
324
329
|
candidate_runs: runState.candidate_runs ?? [],
|
|
325
330
|
status_card: statusCard,
|
|
331
|
+
status_presentation: statusPresentation,
|
|
326
332
|
approval_presentation: renderApprovalOrientationSnapshot(approvalOrientation, {
|
|
327
333
|
registry: interactionLocales,
|
|
328
334
|
expectedIdentity: {
|
|
@@ -392,28 +398,12 @@ export function printApprovalEnvelope(report, { io = console, reEvaluate } = {})
|
|
|
392
398
|
|
|
393
399
|
function printGateCheckStatusCard(report, io) {
|
|
394
400
|
const card = report.status_card;
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
runTitle: card.run_id,
|
|
399
|
-
gateTitle: gateTitle(interactionLocales, card.presentation_language, card.current_gate),
|
|
400
|
-
artefactRefs: buildArtefactRefs({}, interactionLocales, card.presentation_language),
|
|
401
|
-
};
|
|
402
|
-
io.log(`${labels.title}: ${primary.status[card.status] ?? card.status}`);
|
|
403
|
-
io.log(`${labels.run}: ${card.run_id}`);
|
|
404
|
-
io.log(`${labels.humanTitle}: ${presentation.runTitle}`);
|
|
405
|
-
io.log(`${labels.gate}: ${card.current_gate} — ${presentation.gateTitle}`);
|
|
406
|
-
io.log(`${labels.lifecycle}: ${primary.lifecycle[card.delivery_state] ?? primary.lifecycle.unknown}`);
|
|
407
|
-
io.log(`${labels.artefacts}: ${formatArtefactRefs(presentation.artefactRefs)}`);
|
|
408
|
-
io.log(`${labels.blocked}: ${card.blocking_condition === "none" ? primary.none : primary.blocked}`);
|
|
409
|
-
io.log(`${labels.missing}: ${card.missing_approval === "none" ? primary.none : card.missing_approval}`);
|
|
410
|
-
if (card.next_gate_after_approval !== "none") {
|
|
411
|
-
io.log(`${labels.nextGate}: ${card.next_gate_after_approval} — ${gateTitle(interactionLocales, card.presentation_language, card.next_gate_after_approval)}`);
|
|
412
|
-
io.log(`${labels.allowedAfter}: ${primary.afterApproval[card.current_gate] ?? primary.actions[card.current_gate]}`);
|
|
401
|
+
if (!report.status_presentation?.markdown) {
|
|
402
|
+
io.log(localePack(interactionLocales, card?.presentation_language || "en").interaction.statusPresentationFailure);
|
|
403
|
+
return false;
|
|
413
404
|
}
|
|
414
|
-
io.log(
|
|
415
|
-
|
|
416
|
-
io.log(`${labels.quality}: ${primary.quality}`);
|
|
405
|
+
io.log(report.status_presentation.markdown);
|
|
406
|
+
const primary = localePack(interactionLocales, card.presentation_language).primary;
|
|
417
407
|
const readiness = qualityReadinessForRunState(card.runState, report.next_allowed_action);
|
|
418
408
|
if (readiness) {
|
|
419
409
|
const quality = localePack(interactionLocales, card.presentation_language).qualityReadiness;
|
|
@@ -429,17 +419,17 @@ function printGateCheckStatusCard(report, io) {
|
|
|
429
419
|
io.log(`${quality.nextAction}: ${readiness.next_action || primary.none}`);
|
|
430
420
|
io.log(`${quality.decisionOwner}: ${quality.decisionOwnerValue}`);
|
|
431
421
|
}
|
|
422
|
+
return true;
|
|
432
423
|
}
|
|
433
424
|
|
|
434
425
|
export function printGateCheckReport(report, json, statusCard = false, io = console) {
|
|
435
426
|
if (json) {
|
|
436
427
|
io.log(JSON.stringify(report, null, 2));
|
|
437
|
-
return;
|
|
428
|
+
return true;
|
|
438
429
|
}
|
|
439
430
|
|
|
440
431
|
if (statusCard) {
|
|
441
|
-
printGateCheckStatusCard(report, io);
|
|
442
|
-
return;
|
|
432
|
+
return printGateCheckStatusCard(report, io);
|
|
443
433
|
}
|
|
444
434
|
|
|
445
435
|
io.log(`AGDF gate-check: ${report.status}`);
|
|
@@ -456,4 +446,5 @@ export function printGateCheckReport(report, json, statusCard = false, io = cons
|
|
|
456
446
|
for (const item of report.forbidden) io.log(`- ${item}`);
|
|
457
447
|
io.log("");
|
|
458
448
|
io.log(`Next allowed action: ${report.next_allowed_action}`);
|
|
449
|
+
return true;
|
|
459
450
|
}
|
|
@@ -269,6 +269,86 @@ export function formatArtefactRefs(refs) {
|
|
|
269
269
|
return refs.map((ref) => ref.exists ? `[${ref.label}](${ref.path})` : ref.missingLabel).join(" · ");
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
+
function markdownCell(value) {
|
|
273
|
+
return String(value ?? "")
|
|
274
|
+
.replaceAll("\\", "\\\\")
|
|
275
|
+
.replaceAll("|", "\\|")
|
|
276
|
+
.replaceAll("&", "&")
|
|
277
|
+
.replaceAll("<", "<")
|
|
278
|
+
.replaceAll(">", ">")
|
|
279
|
+
.replace(/\r?\n/g, "<br>");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function operationalList(value, none) {
|
|
283
|
+
if (!Array.isArray(value) || value.length === 0) return none;
|
|
284
|
+
return value.map((item) => String(item ?? "").trim()).filter(Boolean).join("\n") || none;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function operationalBreadcrumb(value, labels) {
|
|
288
|
+
if (!Array.isArray(value) || value.length === 0) return "";
|
|
289
|
+
const marker = {
|
|
290
|
+
fulfilled: labels.breadcrumbFulfilled,
|
|
291
|
+
current: labels.breadcrumbCurrent,
|
|
292
|
+
open: labels.breadcrumbOpen,
|
|
293
|
+
};
|
|
294
|
+
return value.map((item) => `${marker[item?.status] ?? labels.breadcrumbOpen} ${String(item?.gate ?? "").trim()}`)
|
|
295
|
+
.filter((item) => item.trim())
|
|
296
|
+
.join(labels.breadcrumbSeparator);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function renderOperationalStatusCard(statusCard, {
|
|
300
|
+
registry,
|
|
301
|
+
humanPresentation,
|
|
302
|
+
revisionId = "",
|
|
303
|
+
} = {}) {
|
|
304
|
+
if (!plainObject(statusCard) || !plainObject(humanPresentation)) return null;
|
|
305
|
+
const runId = String(statusCard.run_id ?? "").trim();
|
|
306
|
+
const currentGate = String(statusCard.current_gate ?? "").trim();
|
|
307
|
+
const revision = String(revisionId ?? "").trim() || "unversioned";
|
|
308
|
+
const locale = resolvePresentationLocale(registry, statusCard.presentation_language);
|
|
309
|
+
if (!runId || !currentGate || !locale) return null;
|
|
310
|
+
|
|
311
|
+
const pack = localePack(registry, locale);
|
|
312
|
+
const labels = pack.statusCard;
|
|
313
|
+
const primary = pack.primary;
|
|
314
|
+
const none = primary.none;
|
|
315
|
+
const gateLabel = String(humanPresentation.gateTitle ?? "").trim() || gateTitle(registry, locale, currentGate);
|
|
316
|
+
const runTitle = displaySafeTitle(humanPresentation.runTitle, normalizedRunTitle(runId));
|
|
317
|
+
const breadcrumb = operationalBreadcrumb(statusCard.breadcrumb, labels);
|
|
318
|
+
const rows = [
|
|
319
|
+
[labels.run, `${runTitle} · \`${runId}\``],
|
|
320
|
+
...(breadcrumb ? [[labels.breadcrumb, breadcrumb]] : []),
|
|
321
|
+
[labels.status, primary.status[statusCard.status] ?? statusCard.status],
|
|
322
|
+
[labels.gate, `${gateLabel} (\`${currentGate}\`)`],
|
|
323
|
+
[labels.allowed, operationalList(statusCard.allowed_now, none)],
|
|
324
|
+
[labels.forbidden, operationalList(statusCard.forbidden_now, none)],
|
|
325
|
+
[labels.blocked, statusCard.blocking_condition === "none" ? none : statusCard.blocking_condition],
|
|
326
|
+
[labels.missing, statusCard.missing_approval === "none" ? none : statusCard.missing_approval],
|
|
327
|
+
[labels.nextGate, statusCard.next_gate_after_approval === "none" ? none : statusCard.next_gate_after_approval],
|
|
328
|
+
[labels.allowedAfter, statusCard.allowed_after_approval === "none" ? none : statusCard.allowed_after_approval],
|
|
329
|
+
[labels.step, statusCard.next_step || none],
|
|
330
|
+
[labels.quality, statusCard.quality_outlook || none],
|
|
331
|
+
];
|
|
332
|
+
const markdown = [
|
|
333
|
+
`## ${labels.title}`,
|
|
334
|
+
"",
|
|
335
|
+
`| ${labels.field} | ${labels.value} |`,
|
|
336
|
+
"|---|---|",
|
|
337
|
+
...rows.map(([label, value]) => `| ${markdownCell(label)} | ${markdownCell(value)} |`),
|
|
338
|
+
].join("\n");
|
|
339
|
+
|
|
340
|
+
return Object.freeze({
|
|
341
|
+
schema_version: "1",
|
|
342
|
+
semantic_block: "run_status_card",
|
|
343
|
+
run_id: runId,
|
|
344
|
+
revision_id: revision,
|
|
345
|
+
current_gate: currentGate,
|
|
346
|
+
presentation_language: locale,
|
|
347
|
+
markdown,
|
|
348
|
+
authorizes: false,
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
272
352
|
export function gateOptions(registry, requestedLocale, gate, { includeCancel = false } = {}) {
|
|
273
353
|
const pack = localePack(registry, requestedLocale);
|
|
274
354
|
const options = [
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema_version": "1",
|
|
3
3
|
"generated": true,
|
|
4
4
|
"source": "create-agdf/cli",
|
|
5
|
-
"version": "0.10.
|
|
5
|
+
"version": "0.10.3",
|
|
6
6
|
"entrypoint": "create-agdf/bin/agdf-validator.js",
|
|
7
|
-
"digest": "
|
|
7
|
+
"digest": "f95766cb6826e5e2a27c39ac6ddfc720ed8d3388e07f40411cffcccca925a7ed"
|
|
8
8
|
}
|
|
@@ -369,6 +369,16 @@ for (const required of gateCheckOperationalBoundaries) {
|
|
|
369
369
|
for (const duplicatedPolicy of ["Surface behavior:", "Make exactly one native-attempt", "Interaction Locale Contract", "decorated_label_only"]) {
|
|
370
370
|
if (gateCheckSkill.includes(duplicatedPolicy)) failures.push(`gate-check must not duplicate normative interaction policy: ${duplicatedPolicy}`);
|
|
371
371
|
}
|
|
372
|
+
if (!gateCheckSkill.includes("`status_presentation.markdown` verbatim")) {
|
|
373
|
+
failures.push("gate-check must consume the deterministic operational status presentation");
|
|
374
|
+
}
|
|
375
|
+
if (gateCheckSkill.includes("| Run status | Value |")) {
|
|
376
|
+
failures.push("gate-check must not maintain a second operational status-card template");
|
|
377
|
+
}
|
|
378
|
+
if (!runtimeContract.includes("### Deterministic Operational Presentation")
|
|
379
|
+
|| !/must\s+not reconstruct a table/.test(runtimeContract)) {
|
|
380
|
+
failures.push("interaction contract must own deterministic operational status presentation");
|
|
381
|
+
}
|
|
372
382
|
|
|
373
383
|
for (const gate of ["UR", "PRD", "SD", "TP", "QA", "UAT"]) {
|
|
374
384
|
for (const locale of ["en", "de"]) {
|
|
@@ -824,11 +834,8 @@ for (const skill of expectedSkills) {
|
|
|
824
834
|
if (!skillMd.includes("The canonical gate order and transition model live only in `../../meta/contracts/gate-transition.md`")) {
|
|
825
835
|
failures.push("gate-check must point to gate-transition.md as gate transition SoT");
|
|
826
836
|
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
}
|
|
830
|
-
if (!skillMd.includes("Next gate after approval") || !skillMd.includes("Allowed after approval")) {
|
|
831
|
-
failures.push("gate-check must render post-approval Run Status Card fields when an approval is missing");
|
|
837
|
+
if (!skillMd.includes("`status_presentation.markdown` verbatim") || !skillMd.includes("Do not maintain or render a skill-local table template")) {
|
|
838
|
+
failures.push("gate-check must delegate operational status rendering to the canonical projection");
|
|
832
839
|
}
|
|
833
840
|
if (!skillMd.includes("A decision value without scope reason and evidence is still missing")) {
|
|
834
841
|
failures.push("gate-check must require evidenced Mode/Slice Decision before later work");
|
|
@@ -864,6 +871,14 @@ for (const skill of expectedSkills) {
|
|
|
864
871
|
failures.push("gate-check must classify CLI reports as validators, not the primary workflow");
|
|
865
872
|
}
|
|
866
873
|
}
|
|
874
|
+
if (skill === "release-or") {
|
|
875
|
+
const rulesSection = skillMd.match(/## Rules\r?\n([\s\S]*?)(?=\r?\n## )/)?.[1] ?? "";
|
|
876
|
+
const ruleNumbers = [...rulesSection.matchAll(/^(\d+)\. /gm)].map((match) => Number(match[1]));
|
|
877
|
+
const expectedRuleNumbers = ruleNumbers.map((_, index) => index + 1);
|
|
878
|
+
if (ruleNumbers.length === 0 || JSON.stringify(ruleNumbers) !== JSON.stringify(expectedRuleNumbers)) {
|
|
879
|
+
failures.push(`release-or Rules numbering must be sequential from 1, got ${ruleNumbers.join(", ") || "none"}`);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
867
882
|
|
|
868
883
|
for (const [pathLabel, content] of [
|
|
869
884
|
[`${skill}/SKILL.md`, skillMd],
|
|
@@ -85,10 +85,11 @@ This skill owns only these operational responsibilities:
|
|
|
85
85
|
5. Revalidate the same run, gate and revision immediately after the response and before persistence.
|
|
86
86
|
6. Persist only a currently valid exact approval through the existing control-state workflow.
|
|
87
87
|
|
|
88
|
-
For status, blocked, read-only or rationale interactions, consume the
|
|
89
|
-
locale contract without asking for approval.
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
For status, blocked, read-only or rationale interactions, consume the canonical
|
|
89
|
+
`status_presentation.markdown` verbatim and apply the locale contract without asking for approval.
|
|
90
|
+
Do not reconstruct its fields, ordering, labels or Markdown from `status_card` JSON. Internal process
|
|
91
|
+
steps remain distinct from user decisions. If the projection, adapter evidence or revalidation is
|
|
92
|
+
missing, follow the contract's fail-closed outcome and leave authority unchanged.
|
|
92
93
|
|
|
93
94
|
## Rules
|
|
94
95
|
1. Fail closed when a required approval or artefact status is missing.
|
|
@@ -163,23 +164,15 @@ If a status is not explicit, do not assume it is satisfied.
|
|
|
163
164
|
12. If branch, workspace and durable artefacts disagree, do not choose a scope silently; report the ambiguity and ask for the smallest clarifying gate action.
|
|
164
165
|
|
|
165
166
|
## Output
|
|
166
|
-
Keep the result short and operational.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
| Current gate | `<GateName or internal step>` |
|
|
172
|
-
| Allowed now | `<currently allowed outputs>` |
|
|
173
|
-
| Blocked by | `<blocking condition or none>` |
|
|
174
|
-
| Missing approval | `Approval: <GateName> | none` |
|
|
175
|
-
| Next step | `<single permissible next step>` |
|
|
176
|
-
| Quality outlook | `<next meaningful quality focus or none>` |
|
|
177
|
-
|
|
178
|
-
When an approval is missing, also include `Next gate after approval` and `Allowed after approval` exactly as constrained by the Runtime Contract. Keep forbidden outputs, evidence and next-skill detail in the concise surrounding text when relevant; they are not extra status-card rows.
|
|
167
|
+
Keep the result short and operational. For a status-only response, consume the Runtime Contract's
|
|
168
|
+
code-owned `status_presentation.markdown` verbatim. It is the compact localized operational Run
|
|
169
|
+
Status Card and already includes current and post-approval authority, next-step and quality fields.
|
|
170
|
+
Keep full raw evidence in `status_card` JSON or concise surrounding detail when relevant; do not turn
|
|
171
|
+
the primary chat card into an audit dump. Do not maintain or render a skill-local table template.
|
|
179
172
|
|
|
180
173
|
This complete operational status table remains the status-reporting and detail
|
|
181
174
|
surface. When the same response immediately requests a ready gate approval,
|
|
182
|
-
render its
|
|
175
|
+
render its five-field compact approval-time projection first, then the Gate
|
|
183
176
|
Transition Card in the same immediately preceding assistant message, then
|
|
184
177
|
invoke exactly one native question or exact-text fallback. Both cards derive
|
|
185
178
|
from the same snapshot and are shown exactly once.
|
|
@@ -52,7 +52,7 @@ Use the Runtime Contract definition of `Relevant Run` to decide whether OR is ma
|
|
|
52
52
|
13. Keep machine status normalization in the CLI; do not write internal snake_case status values into the human-facing backlog.
|
|
53
53
|
14. Reconcile Context Graph impact before clean closeout: report `context_graph_reconciliation: resolved | not_applicable | open_gap`.
|
|
54
54
|
15. If Context Graph work remains unresolved, report it as an explicit open gap and do not describe the run as cleanly handoff-ready.
|
|
55
|
-
|
|
55
|
+
16. After writing or updating `MASTER_BACKLOG.md`, run `doctor --json` (or the locally available equivalent) and resolve any `AGDF_BACKLOG_STATUS_UNKNOWN` or `AGDF_BACKLOG_ARTEFACT_LABEL_UNKNOWN` finding before closeout. See the AGDF control scaffold's `MASTER_BACKLOG.md` template Rules section for the canonical status/artefact label vocabulary.
|
|
56
56
|
|
|
57
57
|
## When To Use
|
|
58
58
|
- at the end of every relevant run
|
|
@@ -24,7 +24,8 @@ export function createValidationHandlers(io = console) {
|
|
|
24
24
|
const output = printApprovalEnvelope(report, { io, reEvaluate: () => evaluateGateCheck(options.dir, options) });
|
|
25
25
|
return output.status === "blocked" ? 2 : 0;
|
|
26
26
|
}
|
|
27
|
-
printGateCheckReport(report, options.json, options.statusCard, io);
|
|
27
|
+
const presentationRendered = printGateCheckReport(report, options.json, options.statusCard, io);
|
|
28
|
+
if (presentationRendered === false) return 2;
|
|
28
29
|
return report.status === "blocked" ? 2 : 0;
|
|
29
30
|
}],
|
|
30
31
|
["delivery-map", (options) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { attachApprovalOrientationSnapshot, buildArtefactRefs, buildQualityReadiness,
|
|
1
|
+
import { attachApprovalOrientationSnapshot, buildArtefactRefs, buildQualityReadiness, gateTitle, localePack, renderApprovalOrientationSnapshot, renderOperationalStatusCard, resolveHumanRunTitle } from '../interaction-presentation.js';
|
|
2
2
|
import { interactionLocales, resolveConfiguredChatLanguage } from '../cli/runtime-context.js';
|
|
3
3
|
import { evaluateDoctor } from './doctor.js';
|
|
4
4
|
import { analyzeDeliveryMap, deriveQualityOutlook } from './delivery-map.js';
|
|
@@ -292,6 +292,11 @@ export function evaluateGateCheck(targetDir, selection = {}) {
|
|
|
292
292
|
enumerable: false,
|
|
293
293
|
});
|
|
294
294
|
const revisionId = extractField(runState.content ?? "", "revision_id");
|
|
295
|
+
const statusPresentation = renderOperationalStatusCard(statusCard, {
|
|
296
|
+
registry: interactionLocales,
|
|
297
|
+
humanPresentation,
|
|
298
|
+
revisionId,
|
|
299
|
+
});
|
|
295
300
|
const approvalOrientation = attachApprovalOrientationSnapshot(statusCard, {
|
|
296
301
|
ready: isReadyUserGateApproval({ status, currentGate, missingApproval }),
|
|
297
302
|
humanPresentation,
|
|
@@ -323,6 +328,7 @@ export function evaluateGateCheck(targetDir, selection = {}) {
|
|
|
323
328
|
native_attempt_required: statusCard.native_attempt_required,
|
|
324
329
|
candidate_runs: runState.candidate_runs ?? [],
|
|
325
330
|
status_card: statusCard,
|
|
331
|
+
status_presentation: statusPresentation,
|
|
326
332
|
approval_presentation: renderApprovalOrientationSnapshot(approvalOrientation, {
|
|
327
333
|
registry: interactionLocales,
|
|
328
334
|
expectedIdentity: {
|
|
@@ -392,28 +398,12 @@ export function printApprovalEnvelope(report, { io = console, reEvaluate } = {})
|
|
|
392
398
|
|
|
393
399
|
function printGateCheckStatusCard(report, io) {
|
|
394
400
|
const card = report.status_card;
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
runTitle: card.run_id,
|
|
399
|
-
gateTitle: gateTitle(interactionLocales, card.presentation_language, card.current_gate),
|
|
400
|
-
artefactRefs: buildArtefactRefs({}, interactionLocales, card.presentation_language),
|
|
401
|
-
};
|
|
402
|
-
io.log(`${labels.title}: ${primary.status[card.status] ?? card.status}`);
|
|
403
|
-
io.log(`${labels.run}: ${card.run_id}`);
|
|
404
|
-
io.log(`${labels.humanTitle}: ${presentation.runTitle}`);
|
|
405
|
-
io.log(`${labels.gate}: ${card.current_gate} — ${presentation.gateTitle}`);
|
|
406
|
-
io.log(`${labels.lifecycle}: ${primary.lifecycle[card.delivery_state] ?? primary.lifecycle.unknown}`);
|
|
407
|
-
io.log(`${labels.artefacts}: ${formatArtefactRefs(presentation.artefactRefs)}`);
|
|
408
|
-
io.log(`${labels.blocked}: ${card.blocking_condition === "none" ? primary.none : primary.blocked}`);
|
|
409
|
-
io.log(`${labels.missing}: ${card.missing_approval === "none" ? primary.none : card.missing_approval}`);
|
|
410
|
-
if (card.next_gate_after_approval !== "none") {
|
|
411
|
-
io.log(`${labels.nextGate}: ${card.next_gate_after_approval} — ${gateTitle(interactionLocales, card.presentation_language, card.next_gate_after_approval)}`);
|
|
412
|
-
io.log(`${labels.allowedAfter}: ${primary.afterApproval[card.current_gate] ?? primary.actions[card.current_gate]}`);
|
|
401
|
+
if (!report.status_presentation?.markdown) {
|
|
402
|
+
io.log(localePack(interactionLocales, card?.presentation_language || "en").interaction.statusPresentationFailure);
|
|
403
|
+
return false;
|
|
413
404
|
}
|
|
414
|
-
io.log(
|
|
415
|
-
|
|
416
|
-
io.log(`${labels.quality}: ${primary.quality}`);
|
|
405
|
+
io.log(report.status_presentation.markdown);
|
|
406
|
+
const primary = localePack(interactionLocales, card.presentation_language).primary;
|
|
417
407
|
const readiness = qualityReadinessForRunState(card.runState, report.next_allowed_action);
|
|
418
408
|
if (readiness) {
|
|
419
409
|
const quality = localePack(interactionLocales, card.presentation_language).qualityReadiness;
|
|
@@ -429,17 +419,17 @@ function printGateCheckStatusCard(report, io) {
|
|
|
429
419
|
io.log(`${quality.nextAction}: ${readiness.next_action || primary.none}`);
|
|
430
420
|
io.log(`${quality.decisionOwner}: ${quality.decisionOwnerValue}`);
|
|
431
421
|
}
|
|
422
|
+
return true;
|
|
432
423
|
}
|
|
433
424
|
|
|
434
425
|
export function printGateCheckReport(report, json, statusCard = false, io = console) {
|
|
435
426
|
if (json) {
|
|
436
427
|
io.log(JSON.stringify(report, null, 2));
|
|
437
|
-
return;
|
|
428
|
+
return true;
|
|
438
429
|
}
|
|
439
430
|
|
|
440
431
|
if (statusCard) {
|
|
441
|
-
printGateCheckStatusCard(report, io);
|
|
442
|
-
return;
|
|
432
|
+
return printGateCheckStatusCard(report, io);
|
|
443
433
|
}
|
|
444
434
|
|
|
445
435
|
io.log(`AGDF gate-check: ${report.status}`);
|
|
@@ -456,4 +446,5 @@ export function printGateCheckReport(report, json, statusCard = false, io = cons
|
|
|
456
446
|
for (const item of report.forbidden) io.log(`- ${item}`);
|
|
457
447
|
io.log("");
|
|
458
448
|
io.log(`Next allowed action: ${report.next_allowed_action}`);
|
|
449
|
+
return true;
|
|
459
450
|
}
|
|
@@ -14,6 +14,7 @@ const globalOpenCodeSkillOwnershipMarker = "<!-- AGDF-GLOBAL-SKILL: ";
|
|
|
14
14
|
const globalOpenCodeInstructionsOwnershipMarker = "<!-- AGDF-GLOBAL-INSTRUCTIONS -->";
|
|
15
15
|
const globalOpenCodeRuntimeContractOwnershipMarker = "<!-- AGDF-GLOBAL-RUNTIME-CONTRACT -->";
|
|
16
16
|
const globalOpenCodeValidatorOwnershipMarker = "// AGDF-GLOBAL-LOCAL-VALIDATOR";
|
|
17
|
+
const globalOpenCodeValidatorPackageOwner = "create-agdf";
|
|
17
18
|
const testNpmCliPath = process.env.NODE_ENV === "test" ? process.env.AGDF_TEST_NPM_CLI_PATH || "" : "";
|
|
18
19
|
const npmCommand = testNpmCliPath ? process.execPath : process.platform === "win32" ? process.execPath : "npm";
|
|
19
20
|
const npmPrefixArgs = testNpmCliPath ? [testNpmCliPath] : process.platform === "win32" ? [join(dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js")] : [];
|
|
@@ -74,16 +75,23 @@ export function installOpenCodeGlobalPlugin(configDir) {
|
|
|
74
75
|
nextConfig.instructions = [...(nextConfig.instructions ?? [])];
|
|
75
76
|
if (!nextConfig.instructions.includes("AGDF.md")) nextConfig.instructions.push("AGDF.md");
|
|
76
77
|
|
|
78
|
+
const canonicalPermissions = pluginDefinition.opencode.permissions;
|
|
77
79
|
if (nextConfig.permission === undefined) {
|
|
78
|
-
nextConfig.permission =
|
|
80
|
+
nextConfig.permission = JSON.parse(JSON.stringify(canonicalPermissions));
|
|
79
81
|
} else if (nextConfig.permission && typeof nextConfig.permission === "object" && !Array.isArray(nextConfig.permission)) {
|
|
80
82
|
nextConfig.permission = { ...nextConfig.permission };
|
|
81
|
-
|
|
83
|
+
for (const permissionName of ["question", "edit", "bash"]) {
|
|
84
|
+
if (nextConfig.permission[permissionName] === undefined) {
|
|
85
|
+
nextConfig.permission[permissionName] = canonicalPermissions[permissionName];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
82
88
|
if (nextConfig.permission.skill === undefined) {
|
|
83
|
-
nextConfig.permission.skill = {
|
|
89
|
+
nextConfig.permission.skill = { ...canonicalPermissions.skill };
|
|
84
90
|
} else if (nextConfig.permission.skill && typeof nextConfig.permission.skill === "object" && !Array.isArray(nextConfig.permission.skill)) {
|
|
85
91
|
nextConfig.permission.skill = { ...nextConfig.permission.skill };
|
|
86
|
-
|
|
92
|
+
for (const [skillPattern, decision] of Object.entries(canonicalPermissions.skill)) {
|
|
93
|
+
if (nextConfig.permission.skill[skillPattern] === undefined) nextConfig.permission.skill[skillPattern] = decision;
|
|
94
|
+
}
|
|
87
95
|
}
|
|
88
96
|
}
|
|
89
97
|
|
|
@@ -122,9 +130,35 @@ function globalOpenCodeConfigPaths(configDir) {
|
|
|
122
130
|
contracts: join(configDir, "contracts"),
|
|
123
131
|
skills: join(configDir, "skills"),
|
|
124
132
|
localValidator: join(configDir, "agdf", "bin", "agdf-local.js"),
|
|
133
|
+
localValidatorPackage: join(configDir, "agdf", "package.json"),
|
|
125
134
|
};
|
|
126
135
|
}
|
|
127
136
|
|
|
137
|
+
function readGlobalOpenCodeValidatorPackage(path) {
|
|
138
|
+
if (!existsSync(path)) return null;
|
|
139
|
+
let existing;
|
|
140
|
+
try {
|
|
141
|
+
existing = JSON.parse(readFileSync(path, "utf8"));
|
|
142
|
+
} catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
return existing;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function globalOpenCodeValidatorPackageIsValid(path) {
|
|
149
|
+
const manifest = readGlobalOpenCodeValidatorPackage(path);
|
|
150
|
+
return manifest?.type === "module"
|
|
151
|
+
&& manifest?.agdf?.owner === globalOpenCodeValidatorPackageOwner
|
|
152
|
+
&& manifest?.agdf?.surface === "opencode-global-validator";
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function assertGlobalOpenCodeValidatorPackageWritable(path) {
|
|
156
|
+
if (!existsSync(path)) return;
|
|
157
|
+
if (!globalOpenCodeValidatorPackageIsValid(path)) {
|
|
158
|
+
throw new Error(`Refusing to overwrite unowned global OpenCode validator package: ${path}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
128
162
|
function globalOpenCodeOwnershipMarkerIsValid(content, marker, placement) {
|
|
129
163
|
const lines = content.split(/\r?\n/);
|
|
130
164
|
if (placement === "first-line") return lines[0] === marker;
|
|
@@ -145,6 +179,7 @@ function assertGlobalOpenCodeSurfaceWritable(configDir) {
|
|
|
145
179
|
assertGlobalOpenCodeFileWritable(paths.instructions, globalOpenCodeInstructionsOwnershipMarker, "first-line");
|
|
146
180
|
assertGlobalOpenCodeFileWritable(paths.runtimeContract, globalOpenCodeRuntimeContractOwnershipMarker, "first-line");
|
|
147
181
|
assertGlobalOpenCodeFileWritable(paths.localValidator, globalOpenCodeValidatorOwnershipMarker, "first-line");
|
|
182
|
+
assertGlobalOpenCodeValidatorPackageWritable(paths.localValidatorPackage);
|
|
148
183
|
for (const moduleName of contractModules) {
|
|
149
184
|
assertGlobalOpenCodeFileWritable(
|
|
150
185
|
join(paths.contracts, moduleName),
|
|
@@ -202,6 +237,7 @@ function toGlobalOpenCodeContent(content) {
|
|
|
202
237
|
}
|
|
203
238
|
|
|
204
239
|
export function installOpenCodeGlobalSurface(configDir) {
|
|
240
|
+
assertGlobalOpenCodeSurfaceWritable(configDir);
|
|
205
241
|
const paths = globalOpenCodeConfigPaths(configDir);
|
|
206
242
|
const generatedOpenCodeRoot = join(generatedRoot, ".opencode");
|
|
207
243
|
const generatedInstructions = readFileSync(join(generatedOpenCodeRoot, pluginDefinition.opencode.instructionsFileName), "utf8");
|
|
@@ -223,6 +259,16 @@ export function installOpenCodeGlobalSurface(configDir) {
|
|
|
223
259
|
writeOwnedGlobalOpenCodeFile(paths.instructions, globalInstructions, globalOpenCodeInstructionsOwnershipMarker, "first-line");
|
|
224
260
|
writeOwnedGlobalOpenCodeFile(paths.runtimeContract, globalRuntimeContract, globalOpenCodeRuntimeContractOwnershipMarker, "first-line");
|
|
225
261
|
writeOwnedGlobalOpenCodeFile(paths.localValidator, localValidator, globalOpenCodeValidatorOwnershipMarker, "first-line");
|
|
262
|
+
mkdirSync(dirname(paths.localValidatorPackage), { recursive: true });
|
|
263
|
+
writeFileSync(paths.localValidatorPackage, `${JSON.stringify({
|
|
264
|
+
name: "agdf-opencode-validator-runtime",
|
|
265
|
+
private: true,
|
|
266
|
+
type: "module",
|
|
267
|
+
agdf: {
|
|
268
|
+
owner: globalOpenCodeValidatorPackageOwner,
|
|
269
|
+
surface: "opencode-global-validator",
|
|
270
|
+
},
|
|
271
|
+
}, null, 2)}\n`, "utf8");
|
|
226
272
|
for (const moduleName of contractModules) {
|
|
227
273
|
const generatedModule = readFileSync(join(generatedOpenCodeRoot, "contracts", moduleName), "utf8");
|
|
228
274
|
writeOwnedGlobalOpenCodeFile(
|
|
@@ -249,6 +295,7 @@ export function installOpenCodeGlobalSurface(configDir) {
|
|
|
249
295
|
contracts: paths.contracts,
|
|
250
296
|
skills: paths.skills,
|
|
251
297
|
localValidator: paths.localValidator,
|
|
298
|
+
localValidatorPackage: paths.localValidatorPackage,
|
|
252
299
|
};
|
|
253
300
|
}
|
|
254
301
|
|
|
@@ -352,6 +399,7 @@ function evaluateGlobalOpenCodeSurface(configDir) {
|
|
|
352
399
|
&& contractCount === contractModules.length
|
|
353
400
|
&& skillCount === openCodeSkillNames.length
|
|
354
401
|
&& existsSync(paths.localValidator)
|
|
402
|
+
&& globalOpenCodeValidatorPackageIsValid(paths.localValidatorPackage)
|
|
355
403
|
&& validatorResolution.envelope.machine_validation === "owned_version_matched",
|
|
356
404
|
};
|
|
357
405
|
}
|
|
@@ -269,6 +269,86 @@ export function formatArtefactRefs(refs) {
|
|
|
269
269
|
return refs.map((ref) => ref.exists ? `[${ref.label}](${ref.path})` : ref.missingLabel).join(" · ");
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
+
function markdownCell(value) {
|
|
273
|
+
return String(value ?? "")
|
|
274
|
+
.replaceAll("\\", "\\\\")
|
|
275
|
+
.replaceAll("|", "\\|")
|
|
276
|
+
.replaceAll("&", "&")
|
|
277
|
+
.replaceAll("<", "<")
|
|
278
|
+
.replaceAll(">", ">")
|
|
279
|
+
.replace(/\r?\n/g, "<br>");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function operationalList(value, none) {
|
|
283
|
+
if (!Array.isArray(value) || value.length === 0) return none;
|
|
284
|
+
return value.map((item) => String(item ?? "").trim()).filter(Boolean).join("\n") || none;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function operationalBreadcrumb(value, labels) {
|
|
288
|
+
if (!Array.isArray(value) || value.length === 0) return "";
|
|
289
|
+
const marker = {
|
|
290
|
+
fulfilled: labels.breadcrumbFulfilled,
|
|
291
|
+
current: labels.breadcrumbCurrent,
|
|
292
|
+
open: labels.breadcrumbOpen,
|
|
293
|
+
};
|
|
294
|
+
return value.map((item) => `${marker[item?.status] ?? labels.breadcrumbOpen} ${String(item?.gate ?? "").trim()}`)
|
|
295
|
+
.filter((item) => item.trim())
|
|
296
|
+
.join(labels.breadcrumbSeparator);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function renderOperationalStatusCard(statusCard, {
|
|
300
|
+
registry,
|
|
301
|
+
humanPresentation,
|
|
302
|
+
revisionId = "",
|
|
303
|
+
} = {}) {
|
|
304
|
+
if (!plainObject(statusCard) || !plainObject(humanPresentation)) return null;
|
|
305
|
+
const runId = String(statusCard.run_id ?? "").trim();
|
|
306
|
+
const currentGate = String(statusCard.current_gate ?? "").trim();
|
|
307
|
+
const revision = String(revisionId ?? "").trim() || "unversioned";
|
|
308
|
+
const locale = resolvePresentationLocale(registry, statusCard.presentation_language);
|
|
309
|
+
if (!runId || !currentGate || !locale) return null;
|
|
310
|
+
|
|
311
|
+
const pack = localePack(registry, locale);
|
|
312
|
+
const labels = pack.statusCard;
|
|
313
|
+
const primary = pack.primary;
|
|
314
|
+
const none = primary.none;
|
|
315
|
+
const gateLabel = String(humanPresentation.gateTitle ?? "").trim() || gateTitle(registry, locale, currentGate);
|
|
316
|
+
const runTitle = displaySafeTitle(humanPresentation.runTitle, normalizedRunTitle(runId));
|
|
317
|
+
const breadcrumb = operationalBreadcrumb(statusCard.breadcrumb, labels);
|
|
318
|
+
const rows = [
|
|
319
|
+
[labels.run, `${runTitle} · \`${runId}\``],
|
|
320
|
+
...(breadcrumb ? [[labels.breadcrumb, breadcrumb]] : []),
|
|
321
|
+
[labels.status, primary.status[statusCard.status] ?? statusCard.status],
|
|
322
|
+
[labels.gate, `${gateLabel} (\`${currentGate}\`)`],
|
|
323
|
+
[labels.allowed, operationalList(statusCard.allowed_now, none)],
|
|
324
|
+
[labels.forbidden, operationalList(statusCard.forbidden_now, none)],
|
|
325
|
+
[labels.blocked, statusCard.blocking_condition === "none" ? none : statusCard.blocking_condition],
|
|
326
|
+
[labels.missing, statusCard.missing_approval === "none" ? none : statusCard.missing_approval],
|
|
327
|
+
[labels.nextGate, statusCard.next_gate_after_approval === "none" ? none : statusCard.next_gate_after_approval],
|
|
328
|
+
[labels.allowedAfter, statusCard.allowed_after_approval === "none" ? none : statusCard.allowed_after_approval],
|
|
329
|
+
[labels.step, statusCard.next_step || none],
|
|
330
|
+
[labels.quality, statusCard.quality_outlook || none],
|
|
331
|
+
];
|
|
332
|
+
const markdown = [
|
|
333
|
+
`## ${labels.title}`,
|
|
334
|
+
"",
|
|
335
|
+
`| ${labels.field} | ${labels.value} |`,
|
|
336
|
+
"|---|---|",
|
|
337
|
+
...rows.map(([label, value]) => `| ${markdownCell(label)} | ${markdownCell(value)} |`),
|
|
338
|
+
].join("\n");
|
|
339
|
+
|
|
340
|
+
return Object.freeze({
|
|
341
|
+
schema_version: "1",
|
|
342
|
+
semantic_block: "run_status_card",
|
|
343
|
+
run_id: runId,
|
|
344
|
+
revision_id: revision,
|
|
345
|
+
current_gate: currentGate,
|
|
346
|
+
presentation_language: locale,
|
|
347
|
+
markdown,
|
|
348
|
+
authorizes: false,
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
272
352
|
export function gateOptions(registry, requestedLocale, gate, { includeCancel = false } = {}) {
|
|
273
353
|
const pack = localePack(registry, requestedLocale);
|
|
274
354
|
const options = [
|
package/package.json
CHANGED