create-agdf 0.31.0 → 0.32.0

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 (121) hide show
  1. package/generated/.opencode/agdf-interaction-locales.json +148 -4
  2. package/generated/plugins/agdf/.claude-plugin/plugin.json +1 -1
  3. package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
  4. package/generated/plugins/agdf/companion/events.mjs +5 -3
  5. package/generated/plugins/agdf/companion/figure.mjs +241 -110
  6. package/generated/plugins/agdf/companion/localization.mjs +5 -0
  7. package/generated/plugins/agdf/companion/notice.mjs +10 -2
  8. package/generated/plugins/agdf/companion/register.mjs +90 -32
  9. package/generated/plugins/agdf/companion/run-state.mjs +13 -1
  10. package/generated/plugins/agdf/companion/view.mjs +149 -19
  11. package/generated/plugins/agdf/mcp/server/package.json +2 -2
  12. package/generated/plugins/agdf/mcp/server/src/generation-runtime.js +6 -4
  13. package/generated/plugins/agdf/mcp/server/src/generation.js +8 -2
  14. package/generated/plugins/agdf/mcp/server/src/main.js +4 -1
  15. package/generated/plugins/agdf/mcp/server/src/server.js +19 -5
  16. package/generated/plugins/agdf/mcp/server/ui/cockpit.html +130 -33
  17. package/generated/plugins/agdf/mcp/server/ui/decision-manifest.json +3 -3
  18. package/generated/plugins/agdf/mcp/server/ui/decision.html +3 -3
  19. package/generated/plugins/agdf/mcp/server/ui/manifest.json +3 -3
  20. package/generated/plugins/agdf/meta/agdf-interaction-locales.json +148 -4
  21. package/generated/plugins/agdf/meta/agdf-mcp-capability.json +1 -1
  22. package/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  23. package/generated/plugins/agdf/meta/copilot-payload-baseline.json +3 -3
  24. package/generated/plugins/agdf/meta/distribution-profile-history.json +7 -0
  25. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +148 -4
  26. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  27. package/generated/plugins/agdf/runtime/create-agdf/lib/mcp-dispatch-runtime.js +4 -1
  28. package/generated/plugins/agdf/runtime/create-agdf/lib/mcp-worker-runtime.js +77 -77
  29. package/generated/plugins/agdf/runtime/create-agdf/package.json +1 -1
  30. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/companion/events.js +4 -1
  31. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/companion/figure-state.js +6 -4
  32. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/companion/journal.js +6 -0
  33. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/companion/run-view.js +54 -11
  34. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/decision-content-placeholder.js +4 -1
  35. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/gate-check.js +5 -1
  36. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/cockpit-list.js +10 -8
  37. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/cockpit-session.js +11 -3
  38. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/cockpit-work.js +4 -2
  39. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/cockpit.js +43 -13
  40. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/work-situation.js +25 -0
  41. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-read/cockpit-worker.js +1 -0
  42. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-create-command.js +6 -1
  43. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-inventory-reader.js +50 -3
  44. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-inventory.js +12 -27
  45. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-presentation-render.js +29 -0
  46. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-title.js +43 -0
  47. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/interaction-catalog-validation.js +2 -1
  48. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/interaction-presentation.js +5 -9
  49. package/generated/plugins/agdf/runtime/runtime-manifest.json +2 -2
  50. package/generated/plugins/agdf/submission/openai/availability.md +1 -1
  51. package/generated/plugins/agdf/submission/openai/capability-matrix.json +1 -1
  52. package/generated/plugins/agdf/submission/openai/release-notes.md +5 -8
  53. package/generated/plugins/agdf/submission/openai/reviewer-cases.json +1 -1
  54. package/generated/plugins/agdf-planning/.codex-plugin/plugin.json +1 -1
  55. package/generated/plugins/agdf-planning/plugin.json +1 -1
  56. package/generated/plugins/copilot/agdf/.agdf-payload-inventory.json +76 -58
  57. package/generated/plugins/copilot/agdf/mcp/server/package.json +2 -2
  58. package/generated/plugins/copilot/agdf/mcp/server/src/generation-runtime.js +6 -4
  59. package/generated/plugins/copilot/agdf/mcp/server/src/generation.js +8 -2
  60. package/generated/plugins/copilot/agdf/mcp/server/src/main.js +4 -1
  61. package/generated/plugins/copilot/agdf/mcp/server/src/server.js +19 -5
  62. package/generated/plugins/copilot/agdf/meta/agdf-plugin.definition.json +1 -1
  63. package/generated/plugins/copilot/agdf/meta/distribution-profile-history.json +7 -0
  64. package/generated/plugins/copilot/agdf/plugin.json +1 -1
  65. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +148 -4
  66. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  67. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/mcp-dispatch-runtime.js +4 -1
  68. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/mcp-worker-runtime.js +62 -62
  69. package/generated/plugins/copilot/agdf/runtime/create-agdf/package.json +1 -1
  70. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/events.js +4 -1
  71. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/figure-state.js +6 -4
  72. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/journal.js +6 -0
  73. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/run-view.js +54 -11
  74. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/decision-content-placeholder.js +4 -1
  75. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/gate-check.js +5 -1
  76. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/work-situation.js +25 -0
  77. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-create-command.js +6 -1
  78. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-inventory-reader.js +50 -3
  79. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-inventory.js +12 -27
  80. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-presentation-render.js +29 -0
  81. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-title.js +43 -0
  82. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/interaction-catalog-validation.js +2 -1
  83. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/interaction-presentation.js +5 -9
  84. package/generated/plugins/copilot/agdf/runtime/runtime-manifest.json +2 -2
  85. package/generated/plugins/copilot/agdf/skills/agdf-interaction-locales.json +1 -1
  86. package/generated/submissions/openai/agdf/.codex-plugin/plugin.json +1 -1
  87. package/generated/submissions/openai/agdf/meta/agdf-interaction-locales.json +148 -4
  88. package/generated/submissions/openai/agdf/meta/agdf-plugin.definition.json +1 -1
  89. package/generated/submissions/openai/agdf/meta/copilot-payload-baseline.json +3 -3
  90. package/generated/submissions/openai/agdf/meta/distribution-profile-history.json +7 -0
  91. package/generated/submissions/openai/agdf/plugin.json +1 -1
  92. package/generated/submissions/openai/agdf/submission/openai/availability.md +1 -1
  93. package/generated/submissions/openai/agdf/submission/openai/capability-matrix.json +1 -1
  94. package/generated/submissions/openai/agdf/submission/openai/inventory.json +13 -13
  95. package/generated/submissions/openai/agdf/submission/openai/listing.json +1 -1
  96. package/generated/submissions/openai/agdf/submission/openai/readiness.json +2 -2
  97. package/generated/submissions/openai/agdf/submission/openai/readiness.md +2 -2
  98. package/generated/submissions/openai/agdf/submission/openai/release-notes.md +5 -8
  99. package/generated/submissions/openai/agdf/submission/openai/reviewer-cases.json +1 -1
  100. package/lib/mcp-dispatch-runtime.js +4 -1
  101. package/lib/mcp-worker-runtime.js +77 -77
  102. package/package.json +1 -1
  103. package/runtime/core/lib/companion/events.js +4 -1
  104. package/runtime/core/lib/companion/figure-state.js +6 -4
  105. package/runtime/core/lib/companion/journal.js +6 -0
  106. package/runtime/core/lib/companion/run-view.js +54 -11
  107. package/runtime/core/lib/control-evaluation/decision-content-placeholder.js +4 -1
  108. package/runtime/core/lib/control-evaluation/gate-check.js +5 -1
  109. package/runtime/core/lib/control-inspect/cockpit-list.js +10 -8
  110. package/runtime/core/lib/control-inspect/cockpit-session.js +11 -3
  111. package/runtime/core/lib/control-inspect/cockpit-work.js +4 -2
  112. package/runtime/core/lib/control-inspect/cockpit.js +43 -13
  113. package/runtime/core/lib/control-inspect/work-situation.js +25 -0
  114. package/runtime/core/lib/control-read/cockpit-worker.js +1 -0
  115. package/runtime/core/lib/control-state/run-create-command.js +6 -1
  116. package/runtime/core/lib/control-state/run-inventory-reader.js +50 -3
  117. package/runtime/core/lib/control-state/run-inventory.js +12 -27
  118. package/runtime/core/lib/control-state/run-presentation-render.js +29 -0
  119. package/runtime/core/lib/control-state/run-title.js +43 -0
  120. package/runtime/core/lib/interaction-catalog-validation.js +2 -1
  121. package/runtime/core/lib/interaction-presentation.js +5 -9
@@ -3,7 +3,9 @@
3
3
  // Callers pass validity findings from the full parser; this module never validates receipts.
4
4
  import { scalarFields, sectionTableRows, projectRunFacts } from "./run-state-document.js";
5
5
  import { RUN_ID_PATTERN } from "./run-identity.js";
6
- import { isDecisionContentPlaceholder } from "../control-evaluation/decision-content-placeholder.js";
6
+ import { normalizedRunTitle, runIdentity } from "./run-title.js";
7
+
8
+ export { normalizedRunTitle };
7
9
 
8
10
  export const RUN_INVENTORY_SCHEMA_VERSION = "1";
9
11
  export const RUN_INVENTORY_LIFECYCLES = Object.freeze(["active", "completed", "superseded", "abandoned"]);
@@ -42,29 +44,10 @@ const ISO_TIME = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$/u;
42
44
  const plain = value => String(value ?? "").replace(/`/gu, "").trim();
43
45
  const clip = text => (text.length > TITLE_LIMIT ? `${text.slice(0, TITLE_LIMIT - 1)}…` : text);
44
46
 
45
- // The run-ID title every reader shows when no authored title exists.
46
- export function normalizedRunTitle(runId) {
47
- const words = String(runId ?? "").replace(/[._-]+/g, " ").replace(/\s+/g, " ").trim();
48
- return words ? words.replace(/\b\p{L}/gu, (letter) => letter.toUpperCase()) : "AGDF run";
49
- }
50
-
51
- // Lines of one level-two section up to the next heading; an empty section stays empty.
52
- function sectionLines(text, heading) {
53
- const lines = text.split("\n"), start = lines.findIndex(line => line.trimEnd() === `## ${heading}`);
54
- if (start < 0) return [];
55
- const rest = lines.slice(start + 1), end = rest.findIndex(line => /^#{1,2} /u.test(line));
56
- return end < 0 ? rest : rest.slice(0, end);
57
- }
58
-
59
- function objectiveTitle(text, runId) {
60
- // The first paragraph, so hard-wrapped objectives stay whole.
61
- const lines = sectionLines(text, "Objective").map(value => value.trim()), first = lines.findIndex(Boolean);
62
- const paragraph = first < 0 ? [] : lines.slice(first), end = paragraph.indexOf("");
63
- const line = (end < 0 ? paragraph : paragraph.slice(0, end)).join(" ");
64
- const stripped = line.replace(/^(?:[-*>]|#+|\d+\.)\s+/u, "").replace(/\*\*|__|`/gu, "").replace(/\s+/gu, " ").trim();
65
- return isDecisionContentPlaceholder(stripped)
66
- ? { title: normalizedRunTitle(runId), title_source: "run_id" }
67
- : { title: clip(stripped), title_source: "objective" };
47
+ // The inventory reads no UR document, so its title source is the Objective or the Run ID.
48
+ function titleFacts(text, runId) {
49
+ const { title, title_source, objective } = runIdentity({ runId, runContent: text });
50
+ return { title: clip(title), title_source, objective: objective === null ? null : clip(objective) };
68
51
  }
69
52
 
70
53
  // Base64url receipt cells decoded for display facts only; undecodable cells are skipped.
@@ -126,17 +109,19 @@ function entryOf(source, language) {
126
109
  const gateResult = facts?.artefacts.get(gate === 'Quick Task Execution' ? 'CR' : gate)?.status ?? null;
127
110
  const presentationDecision = source.observation?.presentation_decision === true;
128
111
  const missing = source.observation?.missing === 'none' ? '' : source.observation?.missing ?? '';
129
- const attention = runAttention({ lifecycle, valid, decision, gateResult, presentationDecision, missing });
112
+ const situation = source.observation?.work_situation;
113
+ const attention = situation?.code ?? runAttention({ lifecycle, valid, decision, gateResult, presentationDecision, missing });
130
114
  const revision = /^[1-9]\d*$/u.test(meta.get("revision") ?? "") ? Number(meta.get("revision")) : null;
131
115
  const index = languageIndex(language);
132
116
  return {
133
117
  run_id: runId,
134
- ...(text ? objectiveTitle(text, runId) : { title: normalizedRunTitle(runId), title_source: "run_id" }),
118
+ ...titleFacts(text, runId),
119
+ ...(source.presentation ? { presentation: source.presentation } : {}),
135
120
  valid,
136
121
  lifecycle: valid ? lifecycle : null,
137
122
  current_gate: gate,
138
123
  decision,
139
- attention: { code: attention, label: LABELS[attention][index] },
124
+ attention: { code: attention, label: situation ? (index === 1 ? situation.de : situation.en) : LABELS[attention][index] },
140
125
  basis: revision === null ? TEXT.basisUnknown[index]() : TEXT.basis[index](revision),
141
126
  last_activity: text ? lastActivity(decoded) : { at: null, source: "none", precision: null },
142
127
  revision,
@@ -355,6 +355,35 @@ function artifactSummary(gate, markdown, { runId, revisionId, language, sourceLa
355
355
  return { markdown: summary, digest: hash(summary) };
356
356
  }
357
357
 
358
+ // Cockpit presentation reuses the same language and summary owner as authoring checks.
359
+ // The structured rows are derived from that summary; no second content authority is created.
360
+ export function projectCockpitSummary(gate, markdown, languages, context = {}) {
361
+ if (!['UR', 'PRD', 'SD', 'TP'].includes(gate)) return null;
362
+ try {
363
+ const readiness = evaluateApprovalSummaryReadiness(gate, markdown, languages, { path: context.artefactPath });
364
+ if (!readiness.ready) return null;
365
+ const summary = artifactSummary(gate, markdown, { ...context, language: languages.presentation_language, sourceLanguage: languages.artifact_language });
366
+ const priorities = {
367
+ UR: [/^(?:Ziel|Goal)$/iu, /^(?:Umfang|Scope)$/iu, /^(?:Abnahme|Abnahmesignal|Acceptance)$/iu],
368
+ PRD: [/^(?:Ziel|Nutzerziel|Goal|User intent)$/iu, /^(?:Umfang|Produktumfang|Scope|Product scope)$/iu, /^(?:AC-[A-Z0-9-]+)$/iu],
369
+ SD: [/^(?:Lösung|Solution)$/iu, /^(?:Verantwortung|Ownership)$/iu, /^(?:Entscheidungen|Design decisions)$/iu],
370
+ TP: [/^(?:Aufgaben|Tasks)$/iu, /^(?:Tests)$/iu, /^(?:Risiken|Risks)$/iu],
371
+ };
372
+ const rows = summary.markdown.split(/\r?\n/u).flatMap(line => {
373
+ const match = line.match(/^[-*]\s+([^:\n]{1,80}):\s*(\S.*)$/u);
374
+ return match && match[2].length <= 1200 ? [{ label: match[1].trim(), text: match[2].trim() }] : [];
375
+ });
376
+ const items = priorities[gate].flatMap(pattern => {
377
+ const matches = rows.filter(row => pattern.test(row.label));
378
+ return matches.length === 1 ? matches : [];
379
+ });
380
+ return { state: 'available', gate, items, markdown: summary.markdown, authorizes: false };
381
+ } catch (error) {
382
+ if (String(error.message).startsWith('approval_summary_')) return null;
383
+ throw error;
384
+ }
385
+ }
386
+
358
387
  // A compact decision brief is a projection of the already validated, source-
359
388
  // authored summary. Missing authored fields stay absent; no agent/UI paraphrase.
360
389
  export function decisionBriefFromSummary(summary, language) {
@@ -0,0 +1,43 @@
1
+ // One owner for a Run's human title and orienting Objective. Pure: no `node:` import, so the run
2
+ // inventory, the cockpit, the companion run view and the status card share exactly this rule.
3
+ // Precedence: registered UR heading, then the authored Objective, then the normalized Run ID.
4
+ import { isDecisionContentPlaceholder } from "../control-evaluation/decision-content-placeholder.js";
5
+
6
+ // The run-ID title every reader shows when no authored title exists.
7
+ export function normalizedRunTitle(runId) {
8
+ const words = String(runId ?? "").replace(/[._-]+/g, " ").replace(/\s+/g, " ").trim();
9
+ return words ? words.replace(/\b\p{L}/gu, (letter) => letter.toUpperCase()) : "AGDF run";
10
+ }
11
+
12
+ // A heading as display text: no leading hashes and no inline Markdown marks.
13
+ export function plainHeading(value) {
14
+ return String(value ?? "").replace(/^#+\s*/u, "").replace(/[`*_]/gu, "").replace(/\s+/gu, " ").trim();
15
+ }
16
+
17
+ // Lines of one level-two section up to the next heading; an empty section stays empty.
18
+ function sectionLines(text, heading) {
19
+ const lines = text.split("\n"), start = lines.findIndex(line => line.trimEnd() === `## ${heading}`);
20
+ if (start < 0) return [];
21
+ const rest = lines.slice(start + 1), end = rest.findIndex(line => /^#{1,2} /u.test(line));
22
+ return end < 0 ? rest : rest.slice(0, end);
23
+ }
24
+
25
+ // The first paragraph of the Objective, so hard-wrapped objectives stay whole. A template prompt,
26
+ // a status word or a bare generated Run ID is no orientation and yields null.
27
+ export function authoredObjective(runContent) {
28
+ const text = String(runContent ?? "").replace(/^/u, "").replace(/\r\n?/gu, "\n");
29
+ const lines = sectionLines(text, "Objective").map(value => value.trim()), first = lines.findIndex(Boolean);
30
+ if (first < 0) return null;
31
+ const paragraph = lines.slice(first), end = paragraph.indexOf("");
32
+ const value = (end < 0 ? paragraph : paragraph.slice(0, end)).join(" ")
33
+ .replace(/^(?:[-*>]|#+|\d+\.)\s+/u, "").replace(/\*\*|__|`/gu, "").replace(/\s+/gu, " ").trim();
34
+ return isDecisionContentPlaceholder(value) ? null : value;
35
+ }
36
+
37
+ // `urHeading` is the registered UR document heading without its "UR:" prefix, when the caller read it.
38
+ export function runIdentity({ runId, runContent, urHeading } = {}) {
39
+ const objective = authoredObjective(runContent), ur = plainHeading(urHeading);
40
+ if (ur) return { title: ur, title_source: "ur", objective };
41
+ if (objective) return { title: objective, title_source: "objective", objective };
42
+ return { title: normalizedRunTitle(runId), title_source: "run_id", objective: null };
43
+ }
@@ -20,7 +20,8 @@ export function validateInteractionCatalog(registry) {
20
20
  check(`${language}.decisionCard`, pack.decisionCard, DECISION_CARD_LABEL_KEYS);
21
21
  check(`${language}.flowEvidence`, pack.flowEvidence, FLOW_EVIDENCE_LABEL_KEYS);
22
22
  check(`${language}.companion.states`, pack.companion?.states, COMPANION_MOODS);
23
- check(`${language}.companion.statusStates`, pack.companion?.statusStates, [...COMPANION_MOODS, "unreadable"]);
23
+ // Beside the moods the status line names a run view that cannot be read and a project without AGDF.
24
+ check(`${language}.companion.statusStates`, pack.companion?.statusStates, [...COMPANION_MOODS, "unreadable", "notSetUp"]);
24
25
  const unreadable = pack.companion?.unreadable;
25
26
  check(`${language}.companion.unreadable`, unreadable && Object.fromEntries(COMPANION_UNREADABLE_REASONS.map(key => [key, unreadable[key]])), COMPANION_UNREADABLE_REASONS);
26
27
  }
@@ -1,5 +1,6 @@
1
1
  import { RUN_ID_PATTERN } from "./control-state/run-identity.js";
2
- import { normalizedRunTitle, runInventoryHistoryLine } from "./control-state/run-inventory.js";
2
+ import { runInventoryHistoryLine } from "./control-state/run-inventory.js";
3
+ import { authoredObjective, normalizedRunTitle, plainHeading, runIdentity } from "./control-state/run-title.js";
3
4
  import { DISPATCH_RECOVERY_CODES, DECISION_CARD_COPY_BUDGETS } from "./interaction-catalog.js";
4
5
  import { TASK_TARGET_SOURCES, TASK_TARGET_UNRESOLVED_REASONS } from "./task-target-resolution.js";
5
6
 
@@ -229,16 +230,11 @@ function stripMarkdown(value) {
229
230
  .trim();
230
231
  }
231
232
 
232
- function section(content, heading) {
233
- const escaped = heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
234
- return content.match(new RegExp(`(?:^|\\n)## ${escaped}\\s*\\n([\\s\\S]*?)(?=\\n## |$)`))?.[1]?.trim() ?? "";
235
- }
236
-
237
233
  export { normalizedRunTitle };
238
234
 
235
+ // The current artefact heading names the card's document; otherwise the shared Run title rule applies.
239
236
  export function resolveHumanRunTitle({ currentArtefactHeading, urHeading, runContent, runId }) {
240
- const objective = section(String(runContent ?? ""), "Objective").split(/\r?\n/).map(stripMarkdown).find(Boolean) ?? "";
241
- return [currentArtefactHeading, urHeading, objective].map(stripMarkdown).find(Boolean) || normalizedRunTitle(runId);
237
+ return plainHeading(currentArtefactHeading) || runIdentity({ runId, runContent, urHeading }).title;
242
238
  }
243
239
 
244
240
  function displaySafeTitle(value, fallback) {
@@ -271,7 +267,7 @@ export function buildRunCandidates(runs) {
271
267
  return {
272
268
  run_id: run.run_id,
273
269
  display_title: displaySafeTitle(title, normalizedRunTitle(run.run_id)),
274
- objective: displaySafeObjective(section(String(run.content ?? ""), "Objective"), normalizedRunTitle(run.run_id)),
270
+ objective: displaySafeObjective(authoredObjective(run.content), normalizedRunTitle(run.run_id)),
275
271
  current_gate: currentGate,
276
272
  decision: String(run.control_state?.decision || "unknown").replaceAll("`", "").trim() || "unknown",
277
273
  next_allowed_action: String(run.control_state?.next_allowed_action ?? "").trim(),