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/package.json
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
|
}
|
|
@@ -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 {
|
|
@@ -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")
|
|
@@ -8,14 +8,16 @@ export function hasConsistentWorkCounts(inventory) {
|
|
|
8
8
|
&& inventory.entries.every(row => row && COCKPIT_WORK_SECTIONS.includes(row.section));
|
|
9
9
|
}
|
|
10
10
|
export function projectCockpitList(inventory, { section = 'active', query = '' } = {}) {
|
|
11
|
-
if (!COCKPIT_WORK_SECTIONS.includes(section)) throw Error('dto_invalid');
|
|
11
|
+
if (section !== 'runs' && !COCKPIT_WORK_SECTIONS.includes(section)) throw Error('dto_invalid');
|
|
12
12
|
const normalized = query.trim().toLowerCase();
|
|
13
13
|
if (!inventory) return { section, query: normalized, coverage: 'unavailable', diagnostics: [], sectionCount: null, matchCount: 0, rows: [] };
|
|
14
14
|
if (!hasConsistentWorkCounts(inventory)) throw Error('dto_invalid');
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
15
|
+
const sections = section === 'runs' ? ['active', 'completed'] : [section];
|
|
16
|
+
const diagnostics = inventory.diagnostics.filter(row => !row.section || sections.includes(row.section));
|
|
17
|
+
const unavailable = sections.filter(s => diagnostics.some(row => row.code === 'work_section_unavailable' && (!row.section || row.section === s)));
|
|
18
|
+
if (unavailable.length === sections.length) return { section, query: normalized, coverage: 'unavailable', diagnostics, sectionCount: null, matchCount: null, rows: [] };
|
|
19
|
+
const selected = inventory.entries.map((entry, index) => ({ entry, index })).filter(row => sections.includes(row.entry.section) && !unavailable.includes(row.entry.section));
|
|
20
|
+
const rows = selected.filter(({ entry }) => !normalized || [entry.title, entry.key, entry.stored_status, entry.objective ?? '']
|
|
19
21
|
.some(value => value.toLowerCase().includes(normalized))).map(({ entry, index }) => ({ entry, index,
|
|
20
22
|
identity: JSON.stringify([entry.identity, entry.source.digest]), selectable: entry.selectable, displayTitle: entry.title.replace(/^\[(framework[-_]maintenance|external[-_]delivery)\]\s*/i, '') || entry.title,
|
|
21
23
|
provenance: { path: entry.source.path, digest: entry.source.digest, originalTitle: entry.title } }));
|
|
@@ -25,10 +27,10 @@ export function projectCockpitList(inventory, { section = 'active', query = '' }
|
|
|
25
27
|
|
|
26
28
|
// Presentation-only grouping/search over the shared inventory's already ordered entries.
|
|
27
29
|
export function projectCockpitRuns(inventory, { section = 'active', query = '' } = {}) {
|
|
28
|
-
if (!['active', 'completed'].includes(section)) throw Error('dto_invalid');
|
|
30
|
+
if (!['active', 'completed', 'runs'].includes(section)) throw Error('dto_invalid');
|
|
29
31
|
const normalized = query.trim().toLowerCase();
|
|
30
|
-
const entries = inventory.runs.filter(run => section === 'active'
|
|
31
|
-
? !run.valid || run.lifecycle === 'active' : run.valid && run.lifecycle !== 'active');
|
|
32
|
+
const entries = inventory.runs.filter(run => section === 'runs' || (section === 'active'
|
|
33
|
+
? !run.valid || run.lifecycle === 'active' : run.valid && run.lifecycle !== 'active'));
|
|
32
34
|
const rows = entries.filter(run => !normalized || [run.title, run.run_id, run.attention.label]
|
|
33
35
|
.some(value => value.toLowerCase().includes(normalized)));
|
|
34
36
|
return { rows, sectionCount: entries.length, matchCount: rows.length,
|
|
@@ -11,11 +11,11 @@ import { COCKPIT_LIMITS, parseCockpitArguments, parsePluginCockpitArguments } fr
|
|
|
11
11
|
// One connection owns bounded independent read views. Retiring workers still occupy slots.
|
|
12
12
|
export function createCockpitSessionService(root, {
|
|
13
13
|
budget, poolFactory = viewRoot => new ReadWorkerPool(viewRoot, { limits: { total: COCKPIT_LIMITS.capture },
|
|
14
|
-
maxOldGenerationSizeMb: COCKPIT_LIMITS.workerOldGeneration, budget }), now = Date.now, pluginMode = false,
|
|
14
|
+
maxOldGenerationSizeMb: COCKPIT_LIMITS.workerOldGeneration, budget }), now = Date.now, pluginMode = false, integrationSurface,
|
|
15
15
|
} = {}) {
|
|
16
16
|
const target = pluginMode ? null : { target_id: resolveControlCommandTarget(root).target_id, display_path: root };
|
|
17
17
|
const sessions = new Map();
|
|
18
|
-
let closed = false, renderGeneration = 0, publication = null;
|
|
18
|
+
let closed = false, renderGeneration = 0, publication = null, resourceIdentity = null;
|
|
19
19
|
const failure = (code, viewTarget = target) => ({ schema_version: '2', target: viewTarget, snapshot_id: null, observed_as_of: null,
|
|
20
20
|
source_digest: null, state: 'blocked', code, retryable: code !== 'session_expired', data: null, authorizes: false });
|
|
21
21
|
function losePublication(session) {
|
|
@@ -59,6 +59,12 @@ export function createCockpitSessionService(root, {
|
|
|
59
59
|
function touch(session) { session.touched = now(); schedule(session); }
|
|
60
60
|
return Object.freeze({
|
|
61
61
|
failure,
|
|
62
|
+
bindResourceIdentity({ digest, version } = {}) {
|
|
63
|
+
if (resourceIdentity || sessions.size || !/^[a-f0-9]{64}$/u.test(digest ?? '')
|
|
64
|
+
|| typeof version !== 'string' || !/^[0-9A-Za-z.+-]{1,80}$/u.test(version)) throw Error('resource_identity_invalid');
|
|
65
|
+
resourceIdentity = Object.freeze({ schema_version: '1', version, resource_digest: `sha256:${digest}`,
|
|
66
|
+
resource_scope: 'server_validated', host_display: 'unconfirmed', candidate_match: 'unverified' });
|
|
67
|
+
},
|
|
62
68
|
inspectOwnership() {
|
|
63
69
|
return { views: sessions.size, active: [...sessions.values()].filter(row => !row.retired).length,
|
|
64
70
|
uncertain: publication?.phase === 'uncertain' ? 1 : 0 };
|
|
@@ -99,6 +105,8 @@ export function createCockpitSessionService(root, {
|
|
|
99
105
|
sessions.set(session.id, session); schedule(session);
|
|
100
106
|
return { schema_version: '1', authorizes: false, target: viewTarget,
|
|
101
107
|
_meta: { agdf_cockpit: { session_id: session.id, target: viewTarget, render_generation: ++renderGeneration,
|
|
108
|
+
...(['codex', 'claude'].includes(integrationSurface) ? { integration_surface: integrationSurface } : {}),
|
|
109
|
+
...(resourceIdentity ? { provenance: resourceIdentity } : {}),
|
|
102
110
|
...(input.run_id ? { initial_run_id: input.run_id } : {}),
|
|
103
111
|
idle_timeout_ms: COCKPIT_LIMITS.idle, lifetime_ms: COCKPIT_LIMITS.lifetime } } };
|
|
104
112
|
},
|
|
@@ -154,7 +162,7 @@ export function createCockpitSessionService(root, {
|
|
|
154
162
|
touch(session); return result;
|
|
155
163
|
}
|
|
156
164
|
if (['planned', 'archive'].includes(input.operation) && !session.rows.has(input.row_id)) return fail('resource_denied');
|
|
157
|
-
if (['document', 'prepare_context'].includes(input.operation)
|
|
165
|
+
if (['document', 'preview', 'prepare_context'].includes(input.operation)
|
|
158
166
|
&& session.documents.get(input.resource_id) !== `${input.snapshot_id}:${input.run_id}`) return fail('resource_denied');
|
|
159
167
|
if (['context', 'artifact_readiness'].includes(input.operation) && input.run_id !== session.run_id) return fail('resource_denied');
|
|
160
168
|
if (input.operation === 'artifact_readiness' && (!session.draft?.available || session.draft.gate !== input.gate
|
|
@@ -7,13 +7,15 @@ import { COCKPIT_WORK_SECTIONS as sections } from './cockpit-list.js';
|
|
|
7
7
|
// discriminator changes policy: order and per-row source identity come from Core,
|
|
8
8
|
// never a Markdown table, stored rank, or adapter-specific lifecycle database.
|
|
9
9
|
export function projectCockpitWork(root) {
|
|
10
|
-
const inventory = readWorkInventory(root, { language: 'de' });
|
|
10
|
+
const inventory = readWorkInventory(root, { language: 'de', includePresentation: true, cockpitAssessment: true });
|
|
11
11
|
if (inventory.state === 'unavailable') return { state: 'error', code: 'work_inventory_unavailable', data: null };
|
|
12
12
|
const entries = inventory.entries.map(row => {
|
|
13
13
|
const planned = row.kind === 'planned';
|
|
14
14
|
const section = planned ? sections[1] : row.lifecycle === 'active' || row.lifecycle === null ? sections[0] : sections[2];
|
|
15
15
|
const key = planned ? row.plan_id : row.run_id;
|
|
16
|
-
return { kind: row.kind, identity: row.identity, section, key, original_key: key, title: row.title,
|
|
16
|
+
return { kind: row.kind, identity: row.identity, section, key, original_key: key, title: planned ? row.title : row.presentation?.name || key,
|
|
17
|
+
...(!planned ? { title_source: row.presentation?.name ? 'ur' : 'run_id', objective: row.objective ?? null,
|
|
18
|
+
last_activity: row.last_activity, seal: row.presentation?.seal ?? 'unconfirmed', lifecycle: row.lifecycle, attention_code: row.attention.code } : {}),
|
|
17
19
|
stored_status: planned ? ({ ready: 'Bereit zur Übergabe', draft: 'UR in Klärung', recovery_required: 'Wiederherstellung erforderlich', unconfirmed: 'Zuordnung ungeklärt', unavailable: 'Quelle nicht verfügbar' })[row.coverage]
|
|
18
20
|
: row.attention.label, scope: planned ? 'UR-Planung' : 'Run', priority: row.priority ?? null,
|
|
19
21
|
stored_next_step: planned ? row.actions.join(' · ') : row.next_action ?? '', source_links: row.source.path,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readCockpitArchive, readArchiveDocument } from './cockpit-archive.js';
|
|
2
|
-
import { runDocumentPath } from '../control-state/run-identity.js';
|
|
2
|
+
import { runDocumentPath, runEvidencePath } from '../control-state/run-identity.js';
|
|
3
3
|
import { existsSync } from '../control-read/fs.js';
|
|
4
4
|
import { RUN_ID_PATTERN } from '../control-state/run-identity.js';
|
|
5
5
|
import { join, posix, sep } from 'node:path';
|
|
@@ -11,8 +11,10 @@ import { readRunState, readArtefactHeading } from '../control-evaluation/run-sta
|
|
|
11
11
|
import { evaluateGateCheck } from '../control-evaluation/gate-check.js';
|
|
12
12
|
import { isSafeControlRelativePath } from '../control-state/contained-file.js';
|
|
13
13
|
import { resolveControlCommandTarget } from '../control-state/approval-command-contract.js';
|
|
14
|
-
import { localePack
|
|
15
|
-
import {
|
|
14
|
+
import { localePack } from '../interaction-presentation.js';
|
|
15
|
+
import { runIdentity } from '../control-state/run-title.js';
|
|
16
|
+
import { interactionLocales, resolveArtifactPresentationLanguages } from '../resources/context.js';
|
|
17
|
+
import { projectCockpitSummary } from '../control-state/run-presentation-render.js';
|
|
16
18
|
import { projectCockpitContext, composeCockpitPacket } from './cockpit-context.js';
|
|
17
19
|
import { projectCockpitWork } from './cockpit-work.js';
|
|
18
20
|
import { readControlLayout } from '../control-state/control-layout.js';
|
|
@@ -23,6 +25,7 @@ import { ARTIFACT_READINESS_GATES, projectArtifactReadiness, describeArtifactRea
|
|
|
23
25
|
import { artefactFileDigest } from '../control-state/run-seal-compute.js';
|
|
24
26
|
import { inspectApprovedArtefact } from '../control-state/artefact-binding-proof.js';
|
|
25
27
|
import { runClosure } from '../control-state/run-operations.js';
|
|
28
|
+
import { projectRunEntry } from '../companion/run-view.js';
|
|
26
29
|
// Shared with the companion run view, in a module every runtime profile carries.
|
|
27
30
|
import { boundedCode, projectCockpitAssessment } from './run-classification.js';
|
|
28
31
|
export { boundedCode, projectCockpitAssessment };
|
|
@@ -34,7 +37,7 @@ const DOCUMENT_GATES = new Set(['UR', 'PRD', 'SD', 'TP', 'QA', 'UAT']);
|
|
|
34
37
|
const DESCRIPTION_LIMIT = 256 * 1024;
|
|
35
38
|
const rawDigest = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
36
39
|
const assertDescriptionBound = value => { if (Buffer.byteLength(JSON.stringify(value)) > DESCRIPTION_LIMIT) fail('resource_limit'); };
|
|
37
|
-
const
|
|
40
|
+
const storedObjective = content => content?.match(/(?:^|\n)## Objective\s*\n([\s\S]*?)(?=\n## |$)/)?.[1]?.trim() ?? null;
|
|
38
41
|
// History of an ended Run, read only from its sealed closure receipt; null for every other Run.
|
|
39
42
|
export function projectRunClosure(content) {
|
|
40
43
|
const closure = runClosure(content);
|
|
@@ -80,9 +83,16 @@ export function createCockpitReader(root, options = {}) {
|
|
|
80
83
|
function manifest(runId, state) {
|
|
81
84
|
const result = [];
|
|
82
85
|
const paths = new Set();
|
|
83
|
-
//
|
|
84
|
-
// The canonical state path is platform-native; registrations use POSIX separators.
|
|
86
|
+
// Keep Run State and artefact registrations ahead of standalone evidence reports.
|
|
85
87
|
const rows = [{ type: 'Run State', path: state.path?.split(sep).join(posix.sep) }, ...[...state.artefacts].map(([type, row]) => ({ type, ...row }))];
|
|
88
|
+
const evidenceRoot = runEvidencePath(runId, '') + '/';
|
|
89
|
+
for (const row of state.evidence_applicability.current) {
|
|
90
|
+
const path = row.report?.path;
|
|
91
|
+
if (row.available !== true || !path?.startsWith(evidenceRoot) || !isSafeControlRelativePath(path)
|
|
92
|
+
|| !SUPPORTED.has(path.split('.').at(-1)?.toLowerCase())) continue;
|
|
93
|
+
const { bytes } = view.readOptionalFileSync(join(root, path), READ_LIMITS.preview);
|
|
94
|
+
if (bytes && !bytes.includes(0)) rows.push({ type: 'Evidence', path });
|
|
95
|
+
}
|
|
86
96
|
for (const row of rows) {
|
|
87
97
|
const path = String(row.path ?? '').replace(/^`|`$/g, '').trim();
|
|
88
98
|
if (!path || path === 'none' || paths.has(path)) continue;
|
|
@@ -117,17 +127,23 @@ export function createCockpitReader(root, options = {}) {
|
|
|
117
127
|
const resources = manifest(run.run_id, state);
|
|
118
128
|
const outside = resources.filter(r => r.status === 'blocked' && isSafeControlRelativePath(r.registered_reference)).map(r => r.registered_reference);
|
|
119
129
|
const report = outside.length ? null : evaluateGateCheck(root, { runId: run.run_id, ignoreRunIdEnv: true });
|
|
130
|
+
const assessed = report ? evaluation(state, report, run.meta.lifecycle) : outOfScopeEvaluation(state, outside);
|
|
131
|
+
const figure = report?.status_card && assessed.control_assessment.state !== 'unconfirmed'
|
|
132
|
+
? projectRunEntry({ run_id: run.run_id, lifecycle: run.meta.lifecycle }, report).figure
|
|
133
|
+
: undefined;
|
|
120
134
|
const work_summary = outside.length ? null : runWorkSummary(root, run.content, state.path);
|
|
121
135
|
const draft_check = draftDescriptor(run, state, report);
|
|
122
136
|
const document_states = documentStates(run, state, report, resources, draft_check);
|
|
137
|
+
const urTitle = readArtefactHeading(root, state.artefacts.get('UR')).replace(/^UR:\s*/i, '').trim();
|
|
138
|
+
const identity = runIdentity({ runId: run.run_id, runContent: run.content, urHeading: urTitle });
|
|
123
139
|
return { run_id: run.run_id, revision_id: run.meta.revision_id, lifecycle: run.meta.lifecycle,
|
|
124
140
|
closure: projectRunClosure(run.content), work_summary,
|
|
141
|
+
...(figure && { figure }),
|
|
125
142
|
// The cockpit identifies the undertaking; the gate card can still name its current artefact.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
evaluation: report ? evaluation(state, report, run.meta.lifecycle) : outOfScopeEvaluation(state, outside), persisted: { current_gate: state.current_gate, next_allowed_action: state.next_allowed_action,
|
|
143
|
+
// The detail shows the stored Objective as written, placeholders included (owner decision);
|
|
144
|
+
// the title follows the shared Run title rule.
|
|
145
|
+
objective: storedObjective(run.content), title_source: identity.title_source, title: identity.title,
|
|
146
|
+
evaluation: assessed, persisted: { current_gate: state.current_gate, next_allowed_action: state.next_allowed_action,
|
|
131
147
|
decision: report?.status ?? 'unavailable', artefacts: [...state.artefacts].map(([type, value]) => ({ type, ...value })) },
|
|
132
148
|
context_graph: { refs: state.context_graph.refs }, resources,
|
|
133
149
|
draft_check, document_states, evidence_applicability: Object.fromEntries(['current', 'superseded', 'applicability_unknown'].map(group => [group,
|
|
@@ -202,7 +218,8 @@ export function createCockpitReader(root, options = {}) {
|
|
|
202
218
|
view.revalidate();
|
|
203
219
|
} catch (error) { checkedDraft = null; invalidate(); inspected = null; graph = null; throw error; }
|
|
204
220
|
}
|
|
205
|
-
|
|
221
|
+
// Only the gate preview shows the authored summary; full document reads stay content-only.
|
|
222
|
+
function readDocument(resourceId, { withSummary = false } = {}) {
|
|
206
223
|
const resource = documents.get(resourceId);
|
|
207
224
|
if (!resource) fail('resource_denied');
|
|
208
225
|
if (resource.status === 'blocked') return envelope({ resource }, 'blocked', 'resource_denied');
|
|
@@ -227,7 +244,12 @@ export function createCockpitReader(root, options = {}) {
|
|
|
227
244
|
if (registered) links[href] = registered.resource_id;
|
|
228
245
|
}
|
|
229
246
|
const document_state = details.get(resource.run_id)?.data?.document_states?.find(row => row.resource_id === resourceId);
|
|
230
|
-
|
|
247
|
+
const summary = withSummary && format === 'markdown' && document_state && ['UR', 'PRD', 'SD', 'TP'].includes(resource.type)
|
|
248
|
+
? projectCockpitSummary(resource.type, content, resolveArtifactPresentationLanguages(root, 'de'), { runId: resource.run_id,
|
|
249
|
+
revisionId: document_state.revision_id, artefactPath: resource.path }) : null;
|
|
250
|
+
const cockpit_summary = summary && { ...summary, run_id: resource.run_id, resource_id: resource.resource_id,
|
|
251
|
+
revision_id: document_state.revision_id, content_digest: rawDigest(bytes) };
|
|
252
|
+
return envelope({ resource, format, content, content_digest: rawDigest(bytes), links, ...(document_state ? { document_state } : {}), ...(cockpit_summary ? { cockpit_summary } : {}) });
|
|
231
253
|
}
|
|
232
254
|
function discardScope(retainCheck = false) {
|
|
233
255
|
if (!retainCheck) checkedDraft = null;
|
|
@@ -355,6 +377,14 @@ export function createCockpitReader(root, options = {}) {
|
|
|
355
377
|
return selected;
|
|
356
378
|
});
|
|
357
379
|
},
|
|
380
|
+
preview(resourceId, id, runId) {
|
|
381
|
+
const resource = documents.get(resourceId);
|
|
382
|
+
if (!resource || resource.run_id !== runId) fail('resource_denied');
|
|
383
|
+
assertSnapshot(id);
|
|
384
|
+
const result = readDocument(resourceId, { withSummary: true });
|
|
385
|
+
assertSnapshot(id);
|
|
386
|
+
return result;
|
|
387
|
+
},
|
|
358
388
|
document(resourceId, id, runId) {
|
|
359
389
|
// Check selectors before revalidation or replacing the legitimate view.
|
|
360
390
|
const previous = documents.get(resourceId);
|
|
@@ -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
|
+
}
|
|
@@ -18,6 +18,7 @@ parentPort.on('message', ({ id, operation, selector, snapshot, input }) => {
|
|
|
18
18
|
: operation === 'planned' ? reader.planned(selector, snapshot)
|
|
19
19
|
: operation === 'artifact_readiness' ? reader.artifactReadiness(input.run_id, snapshot, input.gate, input.expected_revision_id)
|
|
20
20
|
: operation === 'document' ? reader.document(selector, snapshot, input?.run_id)
|
|
21
|
+
: operation === 'preview' ? reader.preview(selector, snapshot, input?.run_id)
|
|
21
22
|
: operation === 'freshness' ? reader.freshness(snapshot)
|
|
22
23
|
: operation === 'context' ? reader.context(selector, snapshot)
|
|
23
24
|
: operation === 'prepare_context' ? reader.prepareContext(input)
|
|
@@ -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
|
});
|