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.
- package/generated/.opencode/agdf-interaction-locales.json +148 -4
- package/generated/plugins/agdf/.claude-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/companion/events.mjs +5 -3
- package/generated/plugins/agdf/companion/figure.mjs +241 -110
- package/generated/plugins/agdf/companion/localization.mjs +5 -0
- package/generated/plugins/agdf/companion/notice.mjs +10 -2
- package/generated/plugins/agdf/companion/register.mjs +90 -32
- package/generated/plugins/agdf/companion/run-state.mjs +13 -1
- package/generated/plugins/agdf/companion/view.mjs +149 -19
- package/generated/plugins/agdf/mcp/server/package.json +2 -2
- package/generated/plugins/agdf/mcp/server/src/generation-runtime.js +6 -4
- package/generated/plugins/agdf/mcp/server/src/generation.js +8 -2
- package/generated/plugins/agdf/mcp/server/src/main.js +4 -1
- package/generated/plugins/agdf/mcp/server/src/server.js +19 -5
- package/generated/plugins/agdf/mcp/server/ui/cockpit.html +130 -33
- package/generated/plugins/agdf/mcp/server/ui/decision-manifest.json +3 -3
- package/generated/plugins/agdf/mcp/server/ui/decision.html +3 -3
- package/generated/plugins/agdf/mcp/server/ui/manifest.json +3 -3
- package/generated/plugins/agdf/meta/agdf-interaction-locales.json +148 -4
- package/generated/plugins/agdf/meta/agdf-mcp-capability.json +1 -1
- package/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
- package/generated/plugins/agdf/meta/copilot-payload-baseline.json +3 -3
- package/generated/plugins/agdf/meta/distribution-profile-history.json +7 -0
- package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +148 -4
- package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
- package/generated/plugins/agdf/runtime/create-agdf/lib/mcp-dispatch-runtime.js +4 -1
- package/generated/plugins/agdf/runtime/create-agdf/lib/mcp-worker-runtime.js +77 -77
- package/generated/plugins/agdf/runtime/create-agdf/package.json +1 -1
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/companion/events.js +4 -1
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/companion/figure-state.js +6 -4
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/companion/journal.js +6 -0
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/companion/run-view.js +54 -11
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/decision-content-placeholder.js +4 -1
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/gate-check.js +5 -1
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/cockpit-list.js +10 -8
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/cockpit-session.js +11 -3
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/cockpit-work.js +4 -2
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/cockpit.js +43 -13
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/work-situation.js +25 -0
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-read/cockpit-worker.js +1 -0
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-create-command.js +6 -1
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-inventory-reader.js +50 -3
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-inventory.js +12 -27
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-presentation-render.js +29 -0
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-title.js +43 -0
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/interaction-catalog-validation.js +2 -1
- package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/interaction-presentation.js +5 -9
- package/generated/plugins/agdf/runtime/runtime-manifest.json +2 -2
- package/generated/plugins/agdf/submission/openai/availability.md +1 -1
- package/generated/plugins/agdf/submission/openai/capability-matrix.json +1 -1
- package/generated/plugins/agdf/submission/openai/release-notes.md +5 -8
- package/generated/plugins/agdf/submission/openai/reviewer-cases.json +1 -1
- package/generated/plugins/agdf-planning/.codex-plugin/plugin.json +1 -1
- package/generated/plugins/agdf-planning/plugin.json +1 -1
- package/generated/plugins/copilot/agdf/.agdf-payload-inventory.json +76 -58
- package/generated/plugins/copilot/agdf/mcp/server/package.json +2 -2
- package/generated/plugins/copilot/agdf/mcp/server/src/generation-runtime.js +6 -4
- package/generated/plugins/copilot/agdf/mcp/server/src/generation.js +8 -2
- package/generated/plugins/copilot/agdf/mcp/server/src/main.js +4 -1
- package/generated/plugins/copilot/agdf/mcp/server/src/server.js +19 -5
- package/generated/plugins/copilot/agdf/meta/agdf-plugin.definition.json +1 -1
- package/generated/plugins/copilot/agdf/meta/distribution-profile-history.json +7 -0
- package/generated/plugins/copilot/agdf/plugin.json +1 -1
- package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +148 -4
- package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/mcp-dispatch-runtime.js +4 -1
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/mcp-worker-runtime.js +62 -62
- package/generated/plugins/copilot/agdf/runtime/create-agdf/package.json +1 -1
- package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/events.js +4 -1
- package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/figure-state.js +6 -4
- package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/journal.js +6 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/run-view.js +54 -11
- package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/decision-content-placeholder.js +4 -1
- package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/gate-check.js +5 -1
- package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/work-situation.js +25 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-create-command.js +6 -1
- package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-inventory-reader.js +50 -3
- package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-inventory.js +12 -27
- package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-presentation-render.js +29 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-title.js +43 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/interaction-catalog-validation.js +2 -1
- package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/interaction-presentation.js +5 -9
- package/generated/plugins/copilot/agdf/runtime/runtime-manifest.json +2 -2
- package/generated/plugins/copilot/agdf/skills/agdf-interaction-locales.json +1 -1
- package/generated/submissions/openai/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/submissions/openai/agdf/meta/agdf-interaction-locales.json +148 -4
- package/generated/submissions/openai/agdf/meta/agdf-plugin.definition.json +1 -1
- package/generated/submissions/openai/agdf/meta/copilot-payload-baseline.json +3 -3
- package/generated/submissions/openai/agdf/meta/distribution-profile-history.json +7 -0
- package/generated/submissions/openai/agdf/plugin.json +1 -1
- package/generated/submissions/openai/agdf/submission/openai/availability.md +1 -1
- package/generated/submissions/openai/agdf/submission/openai/capability-matrix.json +1 -1
- package/generated/submissions/openai/agdf/submission/openai/inventory.json +13 -13
- package/generated/submissions/openai/agdf/submission/openai/listing.json +1 -1
- package/generated/submissions/openai/agdf/submission/openai/readiness.json +2 -2
- package/generated/submissions/openai/agdf/submission/openai/readiness.md +2 -2
- package/generated/submissions/openai/agdf/submission/openai/release-notes.md +5 -8
- package/generated/submissions/openai/agdf/submission/openai/reviewer-cases.json +1 -1
- package/lib/mcp-dispatch-runtime.js +4 -1
- package/lib/mcp-worker-runtime.js +77 -77
- package/package.json +1 -1
- package/runtime/core/lib/companion/events.js +4 -1
- package/runtime/core/lib/companion/figure-state.js +6 -4
- package/runtime/core/lib/companion/journal.js +6 -0
- package/runtime/core/lib/companion/run-view.js +54 -11
- package/runtime/core/lib/control-evaluation/decision-content-placeholder.js +4 -1
- package/runtime/core/lib/control-evaluation/gate-check.js +5 -1
- package/runtime/core/lib/control-inspect/cockpit-list.js +10 -8
- package/runtime/core/lib/control-inspect/cockpit-session.js +11 -3
- package/runtime/core/lib/control-inspect/cockpit-work.js +4 -2
- package/runtime/core/lib/control-inspect/cockpit.js +43 -13
- package/runtime/core/lib/control-inspect/work-situation.js +25 -0
- package/runtime/core/lib/control-read/cockpit-worker.js +1 -0
- package/runtime/core/lib/control-state/run-create-command.js +6 -1
- package/runtime/core/lib/control-state/run-inventory-reader.js +50 -3
- package/runtime/core/lib/control-state/run-inventory.js +12 -27
- package/runtime/core/lib/control-state/run-presentation-render.js +29 -0
- package/runtime/core/lib/control-state/run-title.js +43 -0
- package/runtime/core/lib/interaction-catalog-validation.js +2 -1
- package/runtime/core/lib/interaction-presentation.js +5 -9
package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/events.js
CHANGED
|
@@ -70,6 +70,8 @@ export function commandGate(command = {}) {
|
|
|
70
70
|
const payload = command.payload ?? {};
|
|
71
71
|
if (command.kind === 'closeout.record') return 'OR';
|
|
72
72
|
if (command.kind === 'step.record') return payload.internal_step ?? RUN_STEP_SLOTS[payload.step] ?? null;
|
|
73
|
+
if (command.kind === 'verified_change.record') return 'Verified Change';
|
|
74
|
+
if (command.kind === 'ur_draft.replace') return 'UR';
|
|
73
75
|
if (command.kind === 'operation.recover') return null;
|
|
74
76
|
return typeof payload.gate === 'string' ? payload.gate : null;
|
|
75
77
|
}
|
|
@@ -83,7 +85,8 @@ function successKind(command, result) {
|
|
|
83
85
|
if (kind === 'approval.record') return 'approval_recorded';
|
|
84
86
|
if (kind === 'closeout.record') return 'run_completed';
|
|
85
87
|
if (kind === 'material.stage' && command.payload?.action === 'seal') return 'material_staged';
|
|
86
|
-
|
|
88
|
+
// Every document recording of run-steps.recordRunStep (run-command-service.js).
|
|
89
|
+
if (['step.record', 'artefact.record', 'verified_change.record', 'ur_draft.replace'].includes(kind)) return 'document_recorded';
|
|
87
90
|
return null;
|
|
88
91
|
}
|
|
89
92
|
|
|
@@ -21,16 +21,18 @@ export function deriveFigureState(entry) {
|
|
|
21
21
|
if (!entry) return { mood: 'bereit', reason: { key: 'idle' } };
|
|
22
22
|
if (entry.valid === false) return { mood: 'blockiert', reason: { key: 'unreadable', reason: entry.unreadable_reason ?? 'run_state_invalid' } };
|
|
23
23
|
if (entry.evaluation_error) return { mood: 'blockiert', reason: { key: 'unreadable', reason: entry.evaluation_error } };
|
|
24
|
-
|
|
24
|
+
// A completed run whose evaluation is blocked is not done: the block is shown like any other.
|
|
25
|
+
const blocked = entry.evaluated === true && (entry.status === 'blocked' || Boolean(entry.blocking_reason && entry.blocking_reason !== 'none'));
|
|
26
|
+
// A completed run without confirmed completion is still shown, but claims nothing: its own reason.
|
|
27
|
+
if (entry.lifecycle === 'completed' && !blocked) return entry.completion_confirmed === true
|
|
28
|
+
? { mood: 'fertig', reason: { key: 'done' } } : { mood: 'bereit', reason: { key: 'completionUnconfirmed' } };
|
|
25
29
|
if (ENDED_LIFECYCLES.includes(entry.lifecycle)) return { mood: 'bereit', reason: { key: 'ended' } };
|
|
26
30
|
if (!entry.evaluated) return { mood: 'bereit', reason: { key: 'idle' } };
|
|
27
31
|
const gate = entry.current_gate;
|
|
28
|
-
if (
|
|
29
|
-
return { mood: 'blockiert', reason: { key: 'blocked', gate, decision: entry.blocking_reason } };
|
|
32
|
+
if (blocked) return { mood: 'blockiert', reason: { key: 'blocked', gate, decision: entry.blocking_reason } };
|
|
30
33
|
const approval = pendingApproval(entry.missing_approval);
|
|
31
34
|
if (approval) return { mood: 'wartet', reason: { key: 'waitingApproval', gate: approval } };
|
|
32
35
|
if (entry.user_action_required === 'yes') return { mood: 'wartet', reason: { key: 'waitingDecision', gate } };
|
|
33
|
-
if (entry.status === 'pass') return { mood: 'fertig', reason: { key: 'done' } };
|
|
34
36
|
if (STRUCTURED_MODES.includes(entry.mode) && BUILD_GATES.includes(gate)) return { mood: 'baut', reason: { key: 'building', gate } };
|
|
35
37
|
return { mood: 'laeuft', reason: { key: 'working', gate } };
|
|
36
38
|
}
|
package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/journal.js
CHANGED
|
@@ -160,6 +160,12 @@ export function createCompanionJournal({ dataRoot = process.env[COMPANION_DATA_R
|
|
|
160
160
|
return Object.freeze({
|
|
161
161
|
enabled,
|
|
162
162
|
path: enabled ? journalPath(dataRoot) : null,
|
|
163
|
+
// Whether `observe` writes an event for this result, without writing: a facade asks this for a
|
|
164
|
+
// result its generation produced, and so already journaled.
|
|
165
|
+
observes(toolName, input, result) {
|
|
166
|
+
if (!enabled) return false;
|
|
167
|
+
try { return Boolean(eventKind(toolName, input, result)); } catch { return false; }
|
|
168
|
+
},
|
|
163
169
|
observe(toolName, input, result) {
|
|
164
170
|
if (!enabled) return false;
|
|
165
171
|
try {
|
package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/run-view.js
CHANGED
|
@@ -4,8 +4,12 @@
|
|
|
4
4
|
// view sees exactly what the status card shows. Nothing here writes or authorizes.
|
|
5
5
|
import { existsSync, realpathSync } from 'node:fs';
|
|
6
6
|
import { join } from 'node:path';
|
|
7
|
-
import { readFileSync } from '../control-read/fs.js';
|
|
7
|
+
import { readFileSync, statSync } from '../control-read/fs.js';
|
|
8
8
|
import { captureControlScope } from '../control-read/snapshot.js';
|
|
9
|
+
import { containedRegularFile, hasSymlinkComponent, isSafeControlRelativePath } from '../control-state/contained-file.js';
|
|
10
|
+
import { parseControlState } from '../control-state/run-state-parser.js';
|
|
11
|
+
import { runIdentity } from '../control-state/run-title.js';
|
|
12
|
+
import { localizedDecisionFields } from '../control-evaluation/decision-content-readiness.js';
|
|
9
13
|
import { evaluateGateCheck } from '../control-evaluation/gate-check.js';
|
|
10
14
|
import { readRunInventory } from '../control-state/run-inventory-reader.js';
|
|
11
15
|
import { runFolderEntries } from '../control-state/run-state-reader.js';
|
|
@@ -15,7 +19,7 @@ import { runRelativePath } from '../control-state/run-identity.js';
|
|
|
15
19
|
import { artefactFileDigest } from '../control-state/run-seal-compute.js';
|
|
16
20
|
import { boundedCode } from '../control-inspect/run-classification.js';
|
|
17
21
|
import { localizeBlockingCondition, localizeOperationalValue } from '../interaction-presentation.js';
|
|
18
|
-
import { interactionLocales } from '../resources/context.js';
|
|
22
|
+
import { interactionLocales, resolveArtifactPresentationLanguages } from '../resources/context.js';
|
|
19
23
|
import { deriveFigureState } from './figure-state.js';
|
|
20
24
|
|
|
21
25
|
export const RUN_VIEW_SCHEMA = 'run-view/1';
|
|
@@ -43,13 +47,50 @@ export function projectDiagnosis(entries) {
|
|
|
43
47
|
return { unreadable, counts: { unreadable: unreadable.length, readable: entries.length - unreadable.length, by_reason } };
|
|
44
48
|
}
|
|
45
49
|
|
|
50
|
+
const NAMING_TEXT_LIMIT = 500;
|
|
51
|
+
const UR_READ_LIMIT = 131072;
|
|
52
|
+
const bounded = value => (typeof value === 'string' && value.length > NAMING_TEXT_LIMIT ? `${value.slice(0, NAMING_TEXT_LIMIT - 1)}…` : value);
|
|
53
|
+
|
|
54
|
+
// How a run names itself to a person, read inside the run's own snapshot: the stable title of the
|
|
55
|
+
// shared Run title rule (registered UR heading, then the authored Objective; a bare Run ID is no
|
|
56
|
+
// title) and the goal in the display language. Where display and artefact language differ, the goal
|
|
57
|
+
// is the one the UR's localized approval summary states. An unreadable UR leaves both to the Run document.
|
|
58
|
+
export function readRunNaming(root, runId, language = 'en') {
|
|
59
|
+
let content;
|
|
60
|
+
try { content = readFileSync(join(root, runRelativePath(runId, '', 'RUN_STATE.md')), 'utf8'); } catch { return null; }
|
|
61
|
+
let urHeading = '', localizedGoal = null;
|
|
62
|
+
try {
|
|
63
|
+
const path = String(parseControlState(content).artefacts.get('UR')?.path ?? '').replace(/^`|`$/gu, '').trim();
|
|
64
|
+
if (isSafeControlRelativePath(path) && path.startsWith('.agdf/control/') && !hasSymlinkComponent(root, path)) {
|
|
65
|
+
const file = containedRegularFile(root, path);
|
|
66
|
+
if (file.status === 'valid' && statSync(file.path).size <= UR_READ_LIMIT) {
|
|
67
|
+
const text = readFileSync(file.path, 'utf8');
|
|
68
|
+
urHeading = (text.match(/^#\s+(.+)$/mu)?.[1] ?? '').replace(/^UR:\s*/iu, '').trim();
|
|
69
|
+
const sourceLanguage = resolveArtifactPresentationLanguages(root, language).artifact_language;
|
|
70
|
+
localizedGoal = localizedDecisionFields('UR', text, { locale: String(language).split('-')[0], sourceLanguage, path }).fields.goal ?? null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} catch (error) { if (error?.code === 'source_changed') throw error; }
|
|
74
|
+
const identity = runIdentity({ runId, runContent: content, urHeading });
|
|
75
|
+
return { run_title: identity.title_source === 'run_id' ? null : bounded(identity.title),
|
|
76
|
+
run_title_source: identity.title_source, goal: bounded(localizedGoal ?? identity.objective) };
|
|
77
|
+
}
|
|
78
|
+
|
|
46
79
|
// One run entry: inventory facts plus, when evaluated, the status card fields of its report.
|
|
47
|
-
|
|
80
|
+
// `naming` is readRunNaming's result for an evaluated run; without it the inventory names the run.
|
|
81
|
+
export function projectRunEntry(inventoryEntry, report, { registry = interactionLocales, language = 'en', naming = null } = {}) {
|
|
82
|
+
const authored = inventoryEntry.title_source === 'objective' ? inventoryEntry.title ?? null : null;
|
|
48
83
|
const base = {
|
|
49
84
|
run_id: inventoryEntry.run_id,
|
|
50
85
|
title: inventoryEntry.title ?? null,
|
|
51
|
-
// The
|
|
52
|
-
|
|
86
|
+
// The stable human title, the same at every gate; null while only the Run ID could name the run.
|
|
87
|
+
run_title: naming ? naming.run_title : authored,
|
|
88
|
+
run_title_source: naming?.run_title_source ?? (authored ? 'objective' : 'run_id'),
|
|
89
|
+
// The goal in the display language where the UR states one, otherwise the authored objective.
|
|
90
|
+
goal: naming ? naming.goal : inventoryEntry.objective ?? null,
|
|
91
|
+
// The authored objective only, from the shared Run title rule; a template objective or a
|
|
92
|
+
// bare Run ID is no goal (CR finding C2).
|
|
93
|
+
objective: inventoryEntry.objective ?? null,
|
|
53
94
|
lifecycle: inventoryEntry.lifecycle ?? null,
|
|
54
95
|
valid: inventoryEntry.valid !== false,
|
|
55
96
|
revision: inventoryEntry.revision ?? null,
|
|
@@ -66,6 +107,7 @@ export function projectRunEntry(inventoryEntry, report, { registry = interaction
|
|
|
66
107
|
...base,
|
|
67
108
|
title: card.humanPresentation?.runTitle ?? base.title,
|
|
68
109
|
evaluated: true,
|
|
110
|
+
completion_confirmed: base.lifecycle === 'completed' && report.completion?.fulfilled === true,
|
|
69
111
|
status: card.status,
|
|
70
112
|
current_gate: card.current_gate,
|
|
71
113
|
path: card.breadcrumb.map(({ gate, status }) => ({ gate, status })),
|
|
@@ -122,9 +164,10 @@ export function readRunDrift(root, runId) {
|
|
|
122
164
|
return drift;
|
|
123
165
|
}
|
|
124
166
|
|
|
125
|
-
// Active
|
|
126
|
-
//
|
|
127
|
-
|
|
167
|
+
// Active runs are evaluated; terminal and unreadable runs are summarized unless requested by id, so
|
|
168
|
+
// neither a large history nor many unreadable runs cost one evaluation per run. An unreadable run's
|
|
169
|
+
// reason and figure come from the inventory findings.
|
|
170
|
+
const needsEvaluation = (entry, requested) => requested.has(entry.run_id) || (entry.valid && entry.lifecycle === 'active');
|
|
128
171
|
|
|
129
172
|
// A write that lands during a capture invalidates it (source_changed); the read is repeated a bounded
|
|
130
173
|
// number of times before the change is reported (CR delta review RV-1).
|
|
@@ -152,12 +195,12 @@ export function readRunView(root, { language = 'en', runIds = [] } = {}) {
|
|
|
152
195
|
try {
|
|
153
196
|
return captured(readRoot, base => {
|
|
154
197
|
const report = evaluateGateCheck(base, { runId, ignoreRunIdEnv: true, presentationLanguage: language });
|
|
155
|
-
return { report, drift: report?.status_card ? readRunDrift(base, runId) : null };
|
|
198
|
+
return { report, drift: report?.status_card ? readRunDrift(base, runId) : null, naming: readRunNaming(base, runId, language) };
|
|
156
199
|
});
|
|
157
|
-
} catch (error) { return { report: { evaluation_error: boundedCode(error) }, drift: null }; }
|
|
200
|
+
} catch (error) { return { report: { evaluation_error: boundedCode(error) }, drift: null, naming: null }; }
|
|
158
201
|
};
|
|
159
202
|
const entryOf = (inventoryEntry, evaluated) => {
|
|
160
|
-
const entry = projectRunEntry(inventoryEntry, evaluated?.report ?? null, { language });
|
|
203
|
+
const entry = projectRunEntry(inventoryEntry, evaluated?.report ?? null, { language, naming: evaluated?.naming ?? null });
|
|
161
204
|
return entry.evaluated && entry.valid !== false && evaluated.drift ? { ...entry, drift: evaluated.drift } : entry;
|
|
162
205
|
};
|
|
163
206
|
let inventory = null, store;
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
// run inventory and other `node:`-free readers share the same rule.
|
|
3
3
|
const prompts = new Set(['describe the trustworthy outcome.', 'what outcome should become possible?',
|
|
4
4
|
'which tp tasks are fully done, partially done or not done?', 'what remains unverified?', 'which risks remain and what is their impact?']);
|
|
5
|
+
// A bare generated Run ID (`run-<operation uuid>`) names the Run but states no outcome.
|
|
6
|
+
const generatedRunId = /^run-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u;
|
|
5
7
|
export function isDecisionContentPlaceholder(value) {
|
|
6
8
|
if (typeof value !== 'string') return true;
|
|
7
9
|
const text = value.trim().replace(/^`([^`]+)`$/u, '$1').replace(/\s+/gu, ' ').toLowerCase();
|
|
8
|
-
return !text || prompts.has(text) ||
|
|
10
|
+
return !text || prompts.has(text) || generatedRunId.test(text)
|
|
11
|
+
|| /^(?:todo|tbd|<[^>]+>|(?:draft|done|ready|blocked|pass|revise|block)(?:\s*\|\s*(?:draft|done|ready|blocked|pass|revise|block))+)$/.test(text);
|
|
9
12
|
}
|
|
@@ -159,7 +159,7 @@ export function isReadyUserGateApproval({ status, currentGate, missingApproval }
|
|
|
159
159
|
|
|
160
160
|
function buildBreadcrumbPath(modeSliceDecision, currentGate, runState) {
|
|
161
161
|
const gates = BREADCRUMB_PATH_TEMPLATES[modeSliceDecision] ?? ["UR"];
|
|
162
|
-
return gates.map((gate) => {
|
|
162
|
+
return gates.map((gate, index) => {
|
|
163
163
|
if (BREADCRUMB_STANDARD_GATES.includes(gate)) {
|
|
164
164
|
if (gateApprovalStatus(runState, gate) === "approved") return { gate, status: "fulfilled" };
|
|
165
165
|
if (gate === currentGate) return { gate, status: "current" };
|
|
@@ -170,6 +170,10 @@ function buildBreadcrumbPath(modeSliceDecision, currentGate, runState) {
|
|
|
170
170
|
if (gate === currentGate) return { gate, status: "current" };
|
|
171
171
|
if ((gate === "Verified Change" || gate === "Compact Delivery") && currentGate === "OR")
|
|
172
172
|
return { gate, status: "fulfilled" };
|
|
173
|
+
// A path has one current entry: while an earlier gate is still the current one (a stored route
|
|
174
|
+
// with an unapproved UR), the route entry after it is open, not current by its route.
|
|
175
|
+
if (gates.slice(0, index).some((earlier) => earlier === currentGate && gateApprovalStatus(runState, earlier) !== "approved"))
|
|
176
|
+
return { gate, status: "open" };
|
|
173
177
|
if (gate === "Verified Change" && modeSliceDecision === "verified_change")
|
|
174
178
|
return { gate, status: "current" };
|
|
175
179
|
if (gate === "Compact Delivery" && modeSliceDecision === "quick_task")
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Presentation of one canonical control assessment. This module never evaluates or authorizes work.
|
|
2
|
+
export function projectWorkSituation({ assessment, gate, blockingReason, missingApproval, closureOutcome, persistedGate, persistedNext, nextAllowedAction } = {}) {
|
|
3
|
+
if (persistedGate !== undefined && (persistedGate !== gate || !(assessment === 'ended' && closureOutcome) && persistedNext !== nextAllowedAction)) assessment = 'unconfirmed';
|
|
4
|
+
let kind = 'unconfirmed';
|
|
5
|
+
if (assessment === 'open') kind = gate === 'CD+Tests' ? 'in_progress' : 'open';
|
|
6
|
+
else if (assessment === 'blocked') {
|
|
7
|
+
kind = blockingReason === 'presentation_version_decided' ? 'revision_required'
|
|
8
|
+
: blockingReason === 'none' && missingApproval === `Approval: ${gate}` ? 'approval_required'
|
|
9
|
+
: gate === 'UR' && blockingReason === 'AGDF_UR_REQUIREMENTS_INCOMPLETE' ? 'clarification_required' : 'blocked';
|
|
10
|
+
} else if (assessment === 'completed') kind = 'completed';
|
|
11
|
+
else if (assessment === 'ended' && closureOutcome) kind = 'ended';
|
|
12
|
+
const values = {
|
|
13
|
+
revision_required: ['clarification_required', 'Revision required', 'Überarbeitung nötig'],
|
|
14
|
+
approval_required: ['decision_required', 'Approval required', 'Freigabe erforderlich'],
|
|
15
|
+
clarification_required: ['clarification_required', 'Clarification required', 'Klärung erforderlich'],
|
|
16
|
+
in_progress: ['in_progress', 'In implementation', 'In Umsetzung'],
|
|
17
|
+
open: ['in_progress', 'Work may continue', 'Weiterarbeit offen'],
|
|
18
|
+
blocked: ['blocked', 'Blocked', 'Blockiert'],
|
|
19
|
+
completed: ['none', 'Completed', 'Vorhaben abgeschlossen'],
|
|
20
|
+
ended: ['none', 'Ended', 'Vorhaben beendet'],
|
|
21
|
+
unconfirmed: ['unclear', 'Current conditions unconfirmed', 'Aktuelle Voraussetzungen nicht bestätigt'],
|
|
22
|
+
};
|
|
23
|
+
const [code, en, de] = values[kind];
|
|
24
|
+
return { kind, code, en, de };
|
|
25
|
+
}
|
|
@@ -12,6 +12,7 @@ import { runSealState } from './run-seal.js';
|
|
|
12
12
|
import { containedRegularFile, hasSymlinkComponent } from './contained-file.js';
|
|
13
13
|
import { COOPERATIVE_ASSURANCE } from './approval-command-contract.js';
|
|
14
14
|
import { withControlMutation } from './control-layout.js';
|
|
15
|
+
import { isDecisionContentPlaceholder } from '../control-evaluation/decision-content-placeholder.js';
|
|
15
16
|
|
|
16
17
|
import { creationRunId } from './run-identity.js';
|
|
17
18
|
export { creationRunId };
|
|
@@ -50,7 +51,11 @@ export function createRunCommand(root, input, { requestDigest, packageVersion, c
|
|
|
50
51
|
created_at: new Date().toISOString(), producer: { contract: 'run-command/1', package_version: packageVersion },
|
|
51
52
|
assurance: COOPERATIVE_ASSURANCE, effect: { previous_revision_id: null,
|
|
52
53
|
resulting_revision_id: parsed.meta.revision_id, revision: 1, record: { title: input.command.payload.title } } });
|
|
53
|
-
|
|
54
|
+
// Only a semantic title orients the Objective. A placeholder title such as the bare Run ID
|
|
55
|
+
// keeps the template prompt, so recording the UR projects its Goal there.
|
|
56
|
+
const title = input.command.payload.title;
|
|
57
|
+
const oriented = isDecisionContentPlaceholder(title) ? content : content.replace('Describe the trustworthy outcome.', () => title);
|
|
58
|
+
return appendRunOperation(oriented, receipt);
|
|
54
59
|
},
|
|
55
60
|
beforePublish: () => checkpoint('creation_before_publish'),
|
|
56
61
|
});
|
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
import { readControlLayout } from './control-layout.js';
|
|
2
|
-
import { prepareRunContentState } from '../control-evaluation/run-state.js';
|
|
2
|
+
import { prepareRunContentState, readArtefactHeading } from '../control-evaluation/run-state.js';
|
|
3
3
|
import { transitionDecisionForRunState } from '../control-evaluation/gate-policy.js';
|
|
4
|
+
import { evaluateGateCheck } from '../control-evaluation/gate-check.js';
|
|
5
|
+
import { projectCockpitAssessment } from '../control-inspect/run-classification.js';
|
|
6
|
+
import { projectWorkSituation } from '../control-inspect/work-situation.js';
|
|
7
|
+
import { runClosure } from './run-operations.js';
|
|
4
8
|
import { existsSync } from "../control-read/fs.js";
|
|
5
9
|
import { join } from "node:path";
|
|
6
10
|
import { discoverRuns } from "./run-state-reader.js";
|
|
7
11
|
import { buildRunInventory } from "./run-inventory.js";
|
|
12
|
+
import { parseControlState } from './run-state-parser.js';
|
|
13
|
+
import { runSealState, listedArtefactPaths } from './run-seal.js';
|
|
14
|
+
import { isSafeControlRelativePath } from './contained-file.js';
|
|
15
|
+
|
|
16
|
+
const READ_BOUNDARY_CODES = ['source_changed', 'resource_limit', 'resource_denied', 'timeout'];
|
|
17
|
+
const inControlScope = path => isSafeControlRelativePath(path) && path.startsWith('.agdf/control/');
|
|
18
|
+
// The detail reader's boundary: a run registering sources outside .agdf/control cannot be proven here.
|
|
19
|
+
const registersOnlyControlSources = content => listedArtefactPaths(content).every(inControlScope);
|
|
8
20
|
|
|
9
21
|
// Feeds the pure inventory from the run discovery of the current read view: each run's parsed text
|
|
10
22
|
// and, when the full parser rejected it, its findings. No run state is read a second time. `observe: false`
|
|
11
23
|
// skips the per-run gate observation, which reads registered documents, for a caller that evaluates the
|
|
12
24
|
// runs itself (the companion run view, which reads each run in its own captured snapshot).
|
|
13
|
-
|
|
25
|
+
// `cockpitAssessment` evaluates active runs only, like the companion run view: terminal runs stay
|
|
26
|
+
// summarized history, so a long history costs no gate evaluation per run and per refresh.
|
|
27
|
+
export function readRunInventory(root, { language = "en", observe = true, includePresentation = false, cockpitAssessment = false } = {}) {
|
|
14
28
|
if (!existsSync(join(root, '.agdf/control'))) return buildRunInventory(null, { language });
|
|
15
29
|
readControlLayout(root);
|
|
16
30
|
if (!existsSync(join(root, '.agdf/control/runs'))) return buildRunInventory([], { language,
|
|
@@ -20,12 +34,45 @@ export function readRunInventory(root, { language = "en", observe = true } = {})
|
|
|
20
34
|
const store = discovered.filter((run) => run.run_id === "<repository>");
|
|
21
35
|
const sources = discovered.filter(run => run.run_id !== '<repository>').map(run => {
|
|
22
36
|
const source = { run_id: run.run_id, ...(typeof run.content === 'string' ? { text: run.content } : {}), findings: run.valid ? [] : run.findings };
|
|
37
|
+
if (includePresentation && typeof run.content === 'string') {
|
|
38
|
+
let name = '', seal = 'unconfirmed';
|
|
39
|
+
try {
|
|
40
|
+
const ur = parseControlState(run.content).artefacts.get('UR');
|
|
41
|
+
const path = String(ur?.path ?? '').replace(/^`|`$/g, '').trim();
|
|
42
|
+
if (inControlScope(path)) name = readArtefactHeading(root, ur).replace(/^UR:\s*/i, '').trim();
|
|
43
|
+
} catch { /* An unreadable title must not hide the inventory. */ }
|
|
44
|
+
try {
|
|
45
|
+
if (registersOnlyControlSources(run.content)) {
|
|
46
|
+
const result = runSealState(root, run.content).status;
|
|
47
|
+
seal = result === 'valid' ? 'valid' : result === 'unsealed' ? 'unsealed'
|
|
48
|
+
: ['invalid', 'content_changed', 'approvals_changed'].includes(result) ? 'invalid' : 'unconfirmed';
|
|
49
|
+
}
|
|
50
|
+
} catch { /* Unavailable evidence cannot prove a seal state. */ }
|
|
51
|
+
source.presentation = { name: name || null, seal };
|
|
52
|
+
}
|
|
23
53
|
if (run.valid && observe) {
|
|
24
54
|
try {
|
|
25
55
|
const state = prepareRunContentState(root, run.content, run.path), decision = transitionDecisionForRunState(state);
|
|
26
56
|
source.observation = { current_gate: decision.current_gate, decision: decision.status, missing: decision.missing_approval,
|
|
27
57
|
next_action: decision.next_allowed_action, presentation_decision: Boolean(decision.presentation_decision?.decided) };
|
|
28
|
-
|
|
58
|
+
if (cockpitAssessment && run.meta?.lifecycle === 'active') {
|
|
59
|
+
let report = null;
|
|
60
|
+
if (registersOnlyControlSources(run.content)) { try { report = evaluateGateCheck(root, { runId: run.run_id, ignoreRunIdEnv: true }); } catch (error) {
|
|
61
|
+
if (READ_BOUNDARY_CODES.includes(error.code)) throw error;
|
|
62
|
+
// Preserve the Run but do not claim a verified assessment.
|
|
63
|
+
} }
|
|
64
|
+
const assessed = report ? projectCockpitAssessment(report, run.meta?.lifecycle) : { state: 'unconfirmed' };
|
|
65
|
+
source.observation.work_situation = projectWorkSituation({ assessment: assessed.state,
|
|
66
|
+
gate: report?.current_gate, blockingReason: report?.blocking_reason, missingApproval: report?.missing_approval,
|
|
67
|
+
persistedGate: report ? state.current_gate : undefined, persistedNext: state.next_allowed_action, nextAllowedAction: report?.next_allowed_action,
|
|
68
|
+
closureOutcome: runClosure(run.content)?.outcome });
|
|
69
|
+
if (report) Object.assign(source.observation, { current_gate: report.current_gate, decision: report.status,
|
|
70
|
+
missing: report.missing_approval, next_action: report.next_allowed_action });
|
|
71
|
+
}
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if (cockpitAssessment && READ_BOUNDARY_CODES.includes(error.code)) throw error;
|
|
74
|
+
source.findings = [{ code: 'AGDF_RUN_OBSERVATION_UNAVAILABLE' }];
|
|
75
|
+
}
|
|
29
76
|
}
|
|
30
77
|
return source;
|
|
31
78
|
});
|
|
@@ -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 {
|
|
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
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
return
|
|
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
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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(
|
|
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(),
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema_version": "1",
|
|
3
3
|
"generated": true,
|
|
4
4
|
"source": "create-agdf/cli",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.32.0",
|
|
6
6
|
"entrypoint": "create-agdf/bin/agdf-validator.js",
|
|
7
|
-
"digest": "
|
|
7
|
+
"digest": "6adaa20191c11cc43e70669bb4be3b24eca02dfeedfa88be30bfb92abd5b6de3"
|
|
8
8
|
}
|