create-agdf 0.8.4 → 0.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/bin/create-agdf.js +141 -10
  2. package/generated/.agdf/control/templates/artefacts/VERIFIED_CHANGE.md +10 -0
  3. package/generated/.github/skills/agdf-brownfield-analysis/SKILL.md +3 -3
  4. package/generated/.github/skills/agdf-gate-check/SKILL.md +22 -9
  5. package/generated/.github/skills/agdf-interaction-locales.json +74 -4
  6. package/generated/.github/skills/agdf-release-or/SKILL.md +4 -3
  7. package/generated/.github/skills/agdf-runtime-contract.md +79 -8
  8. package/generated/.opencode/agdf-interaction-locales.json +74 -4
  9. package/generated/.opencode/agdf-runtime-contract.md +79 -8
  10. package/generated/.opencode/skills/agdf-brownfield-analysis/SKILL.md +3 -3
  11. package/generated/.opencode/skills/agdf-gate-check/SKILL.md +22 -9
  12. package/generated/.opencode/skills/agdf-release-or/SKILL.md +4 -3
  13. package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
  14. package/generated/plugins/agdf/control/templates/artefacts/VERIFIED_CHANGE.md +10 -0
  15. package/generated/plugins/agdf/meta/agdf-interaction-locales.json +74 -4
  16. package/generated/plugins/agdf/meta/agdf-plugin.definition.json +13 -1
  17. package/generated/plugins/agdf/meta/agdf-runtime-contract.md +79 -8
  18. package/generated/plugins/agdf/skills/brownfield-analysis/SKILL.md +3 -3
  19. package/generated/plugins/agdf/skills/gate-check/SKILL.md +22 -9
  20. package/generated/plugins/agdf/skills/release-or/SKILL.md +4 -3
  21. package/lib/control-state/run-state-parser.js +30 -3
  22. package/lib/interaction-presentation.js +118 -4
  23. package/package.json +1 -1
@@ -2,6 +2,7 @@ const REQUIRED_GATES = ["UR", "PRD", "SD", "TP", "QA", "UAT"];
2
2
  const ARTEFACT_ORDER = ["UR", "PRD", "SD", "TP"];
3
3
  const OUTCOMES = new Set(["approve", "revise", "decline", "cancel", "no_response", "timeout", "empty", "invalid", "stale"]);
4
4
  const ATTEMPT_OUTCOMES = new Set(["presented", "unavailable_before_invocation", "attempted_not_applied", "unsafe_to_wait"]);
5
+ const APPROVAL_VALUE_TRANSPORTS = new Set(["exact_option_value", "separate_label_and_value", "decorated_label_only", "unknown"]);
5
6
  const APPROVAL_SEQUENCE = Object.freeze(["run_status_card", "gate_transition_card", "approval_interaction"]);
6
7
  const QUALITY_STATUS_RANK = { pass: 0, warn: 1, revise: 2, block: 3 };
7
8
  const QUALITY_DIMENSIONS = Object.freeze([
@@ -63,13 +64,14 @@ export function validateLocaleRegistry(registry) {
63
64
  for (const [key, value] of visibleStrings(pack)) {
64
65
  if (!value.trim()) errors.push(`empty_copy:${locale}:${key}`);
65
66
  const budget = key.startsWith("gateTitles.") ? budgets.title
66
- : key.includes("Description") || key.includes("fallbackReasons") || key.startsWith("primary.actions.") || key.startsWith("primary.afterApproval.") || key === "primary.quality"
67
+ : key.includes("Description") || key.includes("fallbackReasons") || key.startsWith("primary.actions.") || key.startsWith("primary.afterApproval.") || key.startsWith("primary.narration.") || key === "primary.quality"
67
68
  ? budgets.description
68
69
  : budgets.label;
69
70
  if (Number.isInteger(budget) && value.length > budget) errors.push(`length_budget:${locale}:${key}`);
70
71
  }
71
72
  for (const key of REQUIRED_GATES) {
72
73
  if (typeof pack.gateTitles?.[key] !== "string" || !pack.gateTitles[key].trim()) errors.push(`missing_gate_title:${locale}:${key}`);
74
+ if (typeof pack.gateActionTitles?.[key] !== "string" || !pack.gateActionTitles[key].trim()) errors.push(`missing_gate_action_title:${locale}:${key}`);
73
75
  }
74
76
  }
75
77
  if (JSON.stringify(registry?.optionOrder) !== JSON.stringify(["approve", "revise", "decline", "cancel"])) errors.push("option_order");
@@ -179,8 +181,31 @@ export function reconcileRunScope({ requestText = "", scopeKey = "", runs = [] }
179
181
  return Object.freeze({ outcome: matches[0].lifecycle === "active" ? "active_match" : "completed_match", matches, reason: "exact_match" });
180
182
  }
181
183
 
182
- export function executeNativeApprovalAttempt({ ready = false, invokeNative, fallback } = {}) {
184
+ export function evaluateNativeApprovalCapability({ staticCapability, runtimeCapability } = {}) {
185
+ const staticValue = staticCapability?.approvalValueTransport;
186
+ const runtimeValue = runtimeCapability?.approvalValueTransport;
187
+ if (runtimeCapability && staticCapability && (runtimeValue !== staticValue || runtimeCapability.waitSafety !== staticCapability.waitSafety)) {
188
+ return Object.freeze({ eligible: false, native_attempt_required: false, preflight_outcome: "unavailable_before_invocation", reason: "capability_conflict", evidence_source: "runtime" });
189
+ }
190
+ const capability = runtimeCapability ?? staticCapability;
191
+ const evidenceSource = runtimeCapability ? "runtime" : staticCapability ? "static" : "none";
192
+ if (!capability || !APPROVAL_VALUE_TRANSPORTS.has(capability.approvalValueTransport)) return Object.freeze({ eligible: false, native_attempt_required: false, preflight_outcome: "unavailable_before_invocation", reason: "capability_missing", evidence_source: evidenceSource });
193
+ if (capability.waitSafety === "unsafe") return Object.freeze({ eligible: false, native_attempt_required: false, preflight_outcome: "unsafe_to_wait", reason: "unsafe_wait", evidence_source: evidenceSource });
194
+ if (capability.waitSafety !== "deliberate_no_auto_resolution") return Object.freeze({ eligible: false, native_attempt_required: false, preflight_outcome: "unavailable_before_invocation", reason: "capability_missing", evidence_source: evidenceSource });
195
+ if (!new Set(["exact_option_value", "separate_label_and_value"]).has(capability.approvalValueTransport)) return Object.freeze({ eligible: false, native_attempt_required: false, preflight_outcome: "unavailable_before_invocation", reason: capability.approvalValueTransport === "decorated_label_only" ? "decorated_only" : "capability_missing", evidence_source: evidenceSource });
196
+ return Object.freeze({ eligible: true, native_attempt_required: true, preflight_outcome: "eligible_for_invocation", reason: capability.approvalValueTransport === "exact_option_value" ? "exact_transport" : "separate_value_transport", evidence_source: evidenceSource });
197
+ }
198
+
199
+ export function executeNativeApprovalAttempt({ ready = false, orientationSnapshot, capabilityPreflight, invokeNative, fallback } = {}) {
183
200
  if (!ready) return Object.freeze({ attempted: false, outcome: "unavailable_before_invocation", reason: "gate_not_ready", authorizes: false });
201
+ const preflight = validateApprovalOrientationSnapshot(orientationSnapshot);
202
+ if (!preflight.valid) return Object.freeze({ attempted: false, outcome: "unavailable_before_invocation", reason: "orientation_preflight_failed", authorizes: false });
203
+ if (!capabilityPreflight?.eligible) {
204
+ const outcome = capabilityPreflight?.preflight_outcome === "unsafe_to_wait" ? "unsafe_to_wait" : "unavailable_before_invocation";
205
+ const reason = capabilityPreflight?.reason || "capability_missing";
206
+ if (typeof fallback === "function") fallback({ outcome, reason });
207
+ return Object.freeze({ attempted: false, outcome, reason, authorizes: false });
208
+ }
184
209
  if (typeof invokeNative !== "function") return Object.freeze({ attempted: false, outcome: "unavailable_before_invocation", reason: "adapter_unavailable", authorizes: false });
185
210
  let adapterResult;
186
211
  try {
@@ -280,6 +305,7 @@ export function buildApprovalOrientationSnapshot({
280
305
  const pack = localePack(registry, locale);
281
306
  const runTitle = String(humanPresentation.runTitle ?? "").trim() || normalizedRunTitle(runId);
282
307
  const currentGateTitle = String(humanPresentation.gateTitle ?? "").trim() || gateTitle(registry, locale, gate);
308
+ const primaryHeading = String(pack.gateActionTitles?.[gate] ?? "").trim();
283
309
  const artefactRefs = Object.freeze([...(humanPresentation.artefactRefs ?? [])].map((ref) => Object.freeze({ ...ref })));
284
310
  const statusFields = Object.freeze([
285
311
  Object.freeze({ id: "selected_run", label: pack.statusCard.run, value: `${runTitle} · ${runId}` }),
@@ -298,8 +324,15 @@ export function buildApprovalOrientationSnapshot({
298
324
  current_gate: gate,
299
325
  presentation_language: locale,
300
326
  sequence: APPROVAL_SEQUENCE,
301
- compact_status_card: Object.freeze({ title: pack.statusCard.title, fields: statusFields }),
327
+ compact_status_card: Object.freeze({
328
+ semantic_block: "run_status_card",
329
+ title: primaryHeading,
330
+ primary_heading: primaryHeading,
331
+ primary_heading_level: 2,
332
+ fields: statusFields,
333
+ }),
302
334
  gate_transition_card: Object.freeze({
335
+ semantic_block: "gate_transition_card",
303
336
  title: `${currentGateTitle} · ${runTitle} · ${runId}`,
304
337
  artefact_refs: artefactRefs,
305
338
  ready: pack.interaction.ready,
@@ -309,11 +342,32 @@ export function buildApprovalOrientationSnapshot({
309
342
  next_heading: pack.interaction.nextHeading,
310
343
  next_gate: String(statusCard.next_gate_after_approval ?? "none"),
311
344
  }),
312
- approval_interaction: Object.freeze({ expected_approval: expectedApproval, options, authorizes: false }),
345
+ approval_interaction: Object.freeze({ semantic_block: "approval_interaction", expected_approval: expectedApproval, options, authorizes: false }),
313
346
  authorizes: false,
314
347
  });
315
348
  }
316
349
 
350
+ export function validateApprovalOrientationSnapshot(snapshot) {
351
+ const errors = [];
352
+ if (!plainObject(snapshot)) return Object.freeze({ valid: false, errors: Object.freeze(["snapshot_missing"]) });
353
+ if (JSON.stringify(snapshot.sequence) !== JSON.stringify(APPROVAL_SEQUENCE)) errors.push("sequence");
354
+ if (snapshot.compact_status_card?.semantic_block !== "run_status_card") errors.push("run_status_card");
355
+ if (snapshot.gate_transition_card?.semantic_block !== "gate_transition_card") errors.push("gate_transition_card");
356
+ if (snapshot.approval_interaction?.semantic_block !== "approval_interaction") errors.push("approval_interaction");
357
+ const heading = String(snapshot.compact_status_card?.primary_heading ?? "").trim();
358
+ if (!heading || snapshot.compact_status_card?.primary_heading_level !== 2) errors.push("primary_heading");
359
+ if (String(snapshot.compact_status_card?.title ?? "").trim() !== heading) errors.push("primary_heading_owner");
360
+ 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");
361
+ const fieldIds = snapshot.compact_status_card?.fields?.map((field) => field?.id);
362
+ if (JSON.stringify(fieldIds) !== JSON.stringify(["selected_run", "readiness_status", "current_gate", "missing_approval", "next_action", "quality_outlook"])) errors.push("status_fields");
363
+ const expectedApproval = `Approval: ${String(snapshot.current_gate ?? "").trim()}`;
364
+ if (!REQUIRED_GATES.includes(snapshot.current_gate)
365
+ || snapshot.gate_transition_card?.exact_approval !== expectedApproval
366
+ || snapshot.approval_interaction?.expected_approval !== expectedApproval
367
+ || snapshot.approval_interaction?.options?.[0]?.value !== expectedApproval) errors.push("canonical_approval");
368
+ return Object.freeze({ valid: errors.length === 0, errors: Object.freeze(errors) });
369
+ }
370
+
317
371
  export function attachApprovalOrientationSnapshot(statusCard, options = {}) {
318
372
  if (!plainObject(statusCard)) throw new Error("approval orientation status card missing");
319
373
  const snapshot = buildApprovalOrientationSnapshot({ ...options, statusCard });
@@ -363,6 +417,66 @@ export function buildQualityReadiness({
363
417
  });
364
418
  }
365
419
 
420
+ export function collapseInternalState(
421
+ { modeSliceDecision = "", verifiedChangeState = "", contextGraphRequiredAction = "", multiScopeState = "" } = {},
422
+ registry,
423
+ requestedLocale,
424
+ ) {
425
+ const pack = localePack(registry, requestedLocale);
426
+ const labels = pack.internalStateLabels || {};
427
+ const result = {};
428
+ if (modeSliceDecision === "verified_change") {
429
+ if (verifiedChangeState === "escalated") {
430
+ result.verified_change = labels.verifiedChangeEscalated || "Escalated to structured delivery";
431
+ } else if (["missing", "draft", "invalid", "eligible", "executed"].includes(verifiedChangeState)) {
432
+ result.verified_change = labels.verifiedChange || "Compact change under review";
433
+ }
434
+ }
435
+ if (contextGraphRequiredAction === "open_gap") {
436
+ result.context_graph = labels.contextGraphOpenGap || "Graph gap open";
437
+ } else if (["link", "update", "create", "resolve_drift"].includes(contextGraphRequiredAction)) {
438
+ result.context_graph = labels.contextGraphMaintained || "Project memory maintained";
439
+ }
440
+ if (multiScopeState === "blocked") {
441
+ result.multi_scope = labels.multiScopeBlocked || "Ambiguous scope, clarification needed";
442
+ }
443
+ return result;
444
+ }
445
+
446
+ export function buildBreadcrumb(breadcrumb, registry, requestedLocale) {
447
+ if (!Array.isArray(breadcrumb)) return "";
448
+ const pack = localePack(registry, requestedLocale);
449
+ const cardLabels = pack.statusCard || {};
450
+ const titles = pack.gateTitles || {};
451
+ const sep = cardLabels.breadcrumbSeparator || " \u00b7 ";
452
+ const symbols = {
453
+ fulfilled: cardLabels.breadcrumbFulfilled || "\u2713",
454
+ current: cardLabels.breadcrumbCurrent || "\u25cf",
455
+ open: cardLabels.breadcrumbOpen || "\u25cb",
456
+ };
457
+ return breadcrumb
458
+ .map((entry) => {
459
+ const title = titles[entry.gate] || entry.gate;
460
+ const symbol = symbols[entry.status] || symbols.open;
461
+ return `${title} ${symbol}`;
462
+ })
463
+ .join(sep);
464
+ }
465
+
466
+ export function buildTransitionNarration(gate, registry, requestedLocale) {
467
+ const pack = localePack(registry, requestedLocale);
468
+ const narration = pack.primary?.narration || {};
469
+ const gateTitles = pack.gateTitles || {};
470
+ const gateTitle = gateTitles[gate] || gate;
471
+ const gateConfig = narration.gates?.[gate] || {};
472
+ const agentNext = gateConfig.agentNext || "";
473
+ const userAction = gateConfig.userAction || narration.noAction || "no user action required now";
474
+ if (!agentNext) return "";
475
+ const gateSatisfiedTemplate = narration.gateSatisfied || "{gate} approved";
476
+ const gateSatisfied = gateSatisfiedTemplate.replace("{gate}", gateTitle);
477
+ return `${gateSatisfied} \u2192 ${agentNext} \u2192 ${userAction}`;
478
+ }
479
+
366
480
  export const interactionPresentationConstants = Object.freeze({
367
481
  artefactOrder: Object.freeze([...ARTEFACT_ORDER]),
368
482
  outcomes: Object.freeze([...OUTCOMES]),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-agdf",
3
- "version": "0.8.4",
3
+ "version": "0.8.6",
4
4
  "description": "Bootstrap AGDF repository-local plugin files, Copilot/OpenCode instructions, skills, control files and machine-readable checks.",
5
5
  "type": "module",
6
6
  "files": [