agentera 0.0.0 → 3.0.0-dev.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/README.md +6 -45
- package/bundle/.agentera-npx-bundle.json +4 -0
- package/bundle/references/adapters/cursor.md +213 -0
- package/bundle/references/adapters/opencode.md +530 -0
- package/bundle/references/adapters/package-manifest-interface-model.yaml +337 -0
- package/bundle/references/adapters/package-registry.yaml +247 -0
- package/bundle/references/adapters/package-surface-characterization.md +48 -0
- package/bundle/references/adapters/runtime-adapter-characterization.md +79 -0
- package/bundle/references/adapters/runtime-adapter-interface-model.yaml +200 -0
- package/bundle/references/adapters/runtime-adapter-registry.yaml +548 -0
- package/bundle/references/adapters/runtime-feature-parity.md +189 -0
- package/bundle/references/analysis/benchmark.md +267 -0
- package/bundle/references/analysis/startup-measurement-contract.yaml +424 -0
- package/bundle/references/artifacts/artifact-registry-interface-model.yaml +288 -0
- package/bundle/references/cli/agent-ready-state-contract.yaml +950 -0
- package/bundle/references/cli/app-lifecycle-vocabulary.yaml +233 -0
- package/bundle/references/cli/audience-namespace-cli-migration.yaml +355 -0
- package/bundle/references/cli/bundle-skill-vocabulary.yaml +278 -0
- package/bundle/references/cli/capability-instruction-contract.yaml +123 -0
- package/bundle/references/cli/capability-tool-classification.yaml +53 -0
- package/bundle/references/cli/routing-execution-vocabulary.yaml +281 -0
- package/bundle/references/cli/update-channels.yaml +120 -0
- package/bundle/references/cli/vocabulary-index.yaml +160 -0
- package/bundle/references/cli/vocabulary.md +562 -0
- package/bundle/references/meta/documentation-inventory.md +43 -0
- package/bundle/references/v1-section-mapping.md +47 -0
- package/bundle/registry.json +39 -0
- package/bundle/skills/agentera/.claude-plugin/plugin.json +27 -0
- package/bundle/skills/agentera/SKILL.md +470 -0
- package/bundle/skills/agentera/agents/dokumentera.toml +6 -0
- package/bundle/skills/agentera/agents/hej.toml +6 -0
- package/bundle/skills/agentera/agents/inspektera.toml +6 -0
- package/bundle/skills/agentera/agents/inspirera.toml +6 -0
- package/bundle/skills/agentera/agents/optimera.toml +6 -0
- package/bundle/skills/agentera/agents/orkestrera.toml +6 -0
- package/bundle/skills/agentera/agents/planera.toml +6 -0
- package/bundle/skills/agentera/agents/profilera.toml +6 -0
- package/bundle/skills/agentera/agents/realisera.toml +6 -0
- package/bundle/skills/agentera/agents/resonera.toml +6 -0
- package/bundle/skills/agentera/agents/visionera.toml +6 -0
- package/bundle/skills/agentera/agents/visualisera.toml +6 -0
- package/bundle/skills/agentera/capabilities/dokumentera/instructions.md +428 -0
- package/bundle/skills/agentera/capabilities/dokumentera/schemas/artifacts.yaml +73 -0
- package/bundle/skills/agentera/capabilities/dokumentera/schemas/exit.yaml +35 -0
- package/bundle/skills/agentera/capabilities/dokumentera/schemas/triggers.yaml +35 -0
- package/bundle/skills/agentera/capabilities/dokumentera/schemas/validation.yaml +139 -0
- package/bundle/skills/agentera/capabilities/hej/instructions.md +331 -0
- package/bundle/skills/agentera/capabilities/hej/schemas/artifacts.yaml +69 -0
- package/bundle/skills/agentera/capabilities/hej/schemas/exit.yaml +32 -0
- package/bundle/skills/agentera/capabilities/hej/schemas/triggers.yaml +58 -0
- package/bundle/skills/agentera/capabilities/hej/schemas/validation.yaml +55 -0
- package/bundle/skills/agentera/capabilities/inspektera/instructions.md +514 -0
- package/bundle/skills/agentera/capabilities/inspektera/schemas/artifacts.yaml +76 -0
- package/bundle/skills/agentera/capabilities/inspektera/schemas/exit.yaml +36 -0
- package/bundle/skills/agentera/capabilities/inspektera/schemas/triggers.yaml +38 -0
- package/bundle/skills/agentera/capabilities/inspektera/schemas/validation.yaml +113 -0
- package/bundle/skills/agentera/capabilities/inspirera/instructions.md +280 -0
- package/bundle/skills/agentera/capabilities/inspirera/schemas/artifacts.yaml +24 -0
- package/bundle/skills/agentera/capabilities/inspirera/schemas/exit.yaml +33 -0
- package/bundle/skills/agentera/capabilities/inspirera/schemas/triggers.yaml +34 -0
- package/bundle/skills/agentera/capabilities/inspirera/schemas/validation.yaml +58 -0
- package/bundle/skills/agentera/capabilities/optimera/instructions.md +437 -0
- package/bundle/skills/agentera/capabilities/optimera/schemas/artifacts.yaml +69 -0
- package/bundle/skills/agentera/capabilities/optimera/schemas/exit.yaml +35 -0
- package/bundle/skills/agentera/capabilities/optimera/schemas/triggers.yaml +39 -0
- package/bundle/skills/agentera/capabilities/optimera/schemas/validation.yaml +91 -0
- package/bundle/skills/agentera/capabilities/orkestrera/instructions.md +433 -0
- package/bundle/skills/agentera/capabilities/orkestrera/schemas/artifacts.yaml +64 -0
- package/bundle/skills/agentera/capabilities/orkestrera/schemas/exit.yaml +34 -0
- package/bundle/skills/agentera/capabilities/orkestrera/schemas/triggers.yaml +42 -0
- package/bundle/skills/agentera/capabilities/orkestrera/schemas/validation.yaml +107 -0
- package/bundle/skills/agentera/capabilities/planera/instructions.md +368 -0
- package/bundle/skills/agentera/capabilities/planera/schemas/artifacts.yaml +62 -0
- package/bundle/skills/agentera/capabilities/planera/schemas/exit.yaml +33 -0
- package/bundle/skills/agentera/capabilities/planera/schemas/triggers.yaml +34 -0
- package/bundle/skills/agentera/capabilities/planera/schemas/validation.yaml +61 -0
- package/bundle/skills/agentera/capabilities/profilera/instructions.md +419 -0
- package/bundle/skills/agentera/capabilities/profilera/schemas/artifacts.yaml +18 -0
- package/bundle/skills/agentera/capabilities/profilera/schemas/exit.yaml +34 -0
- package/bundle/skills/agentera/capabilities/profilera/schemas/triggers.yaml +45 -0
- package/bundle/skills/agentera/capabilities/profilera/schemas/validation.yaml +57 -0
- package/bundle/skills/agentera/capabilities/realisera/instructions.md +403 -0
- package/bundle/skills/agentera/capabilities/realisera/schemas/artifacts.yaml +80 -0
- package/bundle/skills/agentera/capabilities/realisera/schemas/exit.yaml +35 -0
- package/bundle/skills/agentera/capabilities/realisera/schemas/triggers.yaml +39 -0
- package/bundle/skills/agentera/capabilities/realisera/schemas/validation.yaml +110 -0
- package/bundle/skills/agentera/capabilities/resonera/instructions.md +329 -0
- package/bundle/skills/agentera/capabilities/resonera/schemas/artifacts.yaml +47 -0
- package/bundle/skills/agentera/capabilities/resonera/schemas/exit.yaml +35 -0
- package/bundle/skills/agentera/capabilities/resonera/schemas/triggers.yaml +46 -0
- package/bundle/skills/agentera/capabilities/resonera/schemas/validation.yaml +77 -0
- package/bundle/skills/agentera/capabilities/visionera/instructions.md +309 -0
- package/bundle/skills/agentera/capabilities/visionera/schemas/artifacts.yaml +57 -0
- package/bundle/skills/agentera/capabilities/visionera/schemas/exit.yaml +35 -0
- package/bundle/skills/agentera/capabilities/visionera/schemas/triggers.yaml +41 -0
- package/bundle/skills/agentera/capabilities/visionera/schemas/validation.yaml +74 -0
- package/bundle/skills/agentera/capabilities/visualisera/instructions.md +400 -0
- package/bundle/skills/agentera/capabilities/visualisera/schemas/artifacts.yaml +44 -0
- package/bundle/skills/agentera/capabilities/visualisera/schemas/exit.yaml +34 -0
- package/bundle/skills/agentera/capabilities/visualisera/schemas/triggers.yaml +33 -0
- package/bundle/skills/agentera/capabilities/visualisera/schemas/validation.yaml +80 -0
- package/bundle/skills/agentera/capability_schema_contract.yaml +385 -0
- package/bundle/skills/agentera/protocol.yaml +463 -0
- package/bundle/skills/agentera/references/contract.md +1039 -0
- package/bundle/skills/agentera/schemas/artifacts/changelog.yaml +60 -0
- package/bundle/skills/agentera/schemas/artifacts/decisions.yaml +461 -0
- package/bundle/skills/agentera/schemas/artifacts/design.yaml +55 -0
- package/bundle/skills/agentera/schemas/artifacts/docs.yaml +402 -0
- package/bundle/skills/agentera/schemas/artifacts/experiments.yaml +373 -0
- package/bundle/skills/agentera/schemas/artifacts/health.yaml +484 -0
- package/bundle/skills/agentera/schemas/artifacts/objective.yaml +399 -0
- package/bundle/skills/agentera/schemas/artifacts/plan.yaml +342 -0
- package/bundle/skills/agentera/schemas/artifacts/progress.yaml +325 -0
- package/bundle/skills/agentera/schemas/artifacts/todo.yaml +110 -0
- package/bundle/skills/agentera/schemas/artifacts/vision.yaml +262 -0
- package/bundle/skills/hej/.claude-plugin/plugin.json +6 -0
- package/bundle/skills/hej/SKILL.md +69 -0
- package/bundle/skills/hej/agents/hej.toml +11 -0
- package/bundle/skills/hej/agents/openai.yaml +8 -0
- package/dist/analytics/extractCorpus.js +1791 -0
- package/dist/analytics/extractCorpus.js.map +1 -0
- package/dist/analytics/usageStats.js +487 -0
- package/dist/analytics/usageStats.js.map +1 -0
- package/dist/bin/agentera.js +4 -0
- package/dist/bin/agentera.js.map +1 -0
- package/dist/cli/appContext.js +226 -0
- package/dist/cli/appContext.js.map +1 -0
- package/dist/cli/argvalidate.js +41 -0
- package/dist/cli/argvalidate.js.map +1 -0
- package/dist/cli/capabilityContext.js +2421 -0
- package/dist/cli/capabilityContext.js.map +1 -0
- package/dist/cli/commands/backfill.js +84 -0
- package/dist/cli/commands/backfill.js.map +1 -0
- package/dist/cli/commands/capability.js +44 -0
- package/dist/cli/commands/capability.js.map +1 -0
- package/dist/cli/commands/compact.js +148 -0
- package/dist/cli/commands/compact.js.map +1 -0
- package/dist/cli/commands/doctor.js +180 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/lint.js +179 -0
- package/dist/cli/commands/lint.js.map +1 -0
- package/dist/cli/commands/prime.js +545 -0
- package/dist/cli/commands/prime.js.map +1 -0
- package/dist/cli/commands/query.js +346 -0
- package/dist/cli/commands/query.js.map +1 -0
- package/dist/cli/commands/report.js +210 -0
- package/dist/cli/commands/report.js.map +1 -0
- package/dist/cli/commands/schema.js +306 -0
- package/dist/cli/commands/schema.js.map +1 -0
- package/dist/cli/commands/state.js +1012 -0
- package/dist/cli/commands/state.js.map +1 -0
- package/dist/cli/commands/upgrade.js +49 -0
- package/dist/cli/commands/upgrade.js.map +1 -0
- package/dist/cli/commands/validate.js +519 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/commands/verify.js +204 -0
- package/dist/cli/commands/verify.js.map +1 -0
- package/dist/cli/dispatch.js +962 -0
- package/dist/cli/dispatch.js.map +1 -0
- package/dist/cli/orientation.js +595 -0
- package/dist/cli/orientation.js.map +1 -0
- package/dist/cli/prime-blob.js +3 -0
- package/dist/cli/prime-blob.js.map +1 -0
- package/dist/cli/stateQuery.js +292 -0
- package/dist/cli/stateQuery.js.map +1 -0
- package/dist/cli/structured.js +18 -0
- package/dist/cli/structured.js.map +1 -0
- package/dist/core/difflib.js +274 -0
- package/dist/core/difflib.js.map +1 -0
- package/dist/core/git.js +43 -0
- package/dist/core/git.js.map +1 -0
- package/dist/core/paths.js +50 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/pyjson.js +101 -0
- package/dist/core/pyjson.js.map +1 -0
- package/dist/core/sourceRoot.js +72 -0
- package/dist/core/sourceRoot.js.map +1 -0
- package/dist/core/toml.js +11 -0
- package/dist/core/toml.js.map +1 -0
- package/dist/core/yaml.js +25 -0
- package/dist/core/yaml.js.map +1 -0
- package/dist/eval/evalSkills.js +258 -0
- package/dist/eval/evalSkills.js.map +1 -0
- package/dist/eval/semanticEval.js +148 -0
- package/dist/eval/semanticEval.js.map +1 -0
- package/dist/eval/semanticFixtures.js +227 -0
- package/dist/eval/semanticFixtures.js.map +1 -0
- package/dist/hooks/common.js +160 -0
- package/dist/hooks/common.js.map +1 -0
- package/dist/hooks/compaction.js +935 -0
- package/dist/hooks/compaction.js.map +1 -0
- package/dist/hooks/cursorPreToolUse.js +19 -0
- package/dist/hooks/cursorPreToolUse.js.map +1 -0
- package/dist/hooks/cursorSessionStart.js +71 -0
- package/dist/hooks/cursorSessionStart.js.map +1 -0
- package/dist/hooks/sessionStart.js +209 -0
- package/dist/hooks/sessionStart.js.map +1 -0
- package/dist/hooks/sessionStop.js +212 -0
- package/dist/hooks/sessionStop.js.map +1 -0
- package/dist/hooks/validateArtifact.js +933 -0
- package/dist/hooks/validateArtifact.js.map +1 -0
- package/dist/registries/artifactRegistry.js +206 -0
- package/dist/registries/artifactRegistry.js.map +1 -0
- package/dist/registries/capabilityContract.js +310 -0
- package/dist/registries/capabilityContract.js.map +1 -0
- package/dist/registries/packageRegistry.js +641 -0
- package/dist/registries/packageRegistry.js.map +1 -0
- package/dist/registries/runtimeAdapterRegistry.js +315 -0
- package/dist/registries/runtimeAdapterRegistry.js.map +1 -0
- package/dist/setup/codex.js +1052 -0
- package/dist/setup/codex.js.map +1 -0
- package/dist/setup/copilot.js +227 -0
- package/dist/setup/copilot.js.map +1 -0
- package/dist/setup/cursor.js +127 -0
- package/dist/setup/cursor.js.map +1 -0
- package/dist/setup/doctor.js +1269 -0
- package/dist/setup/doctor.js.map +1 -0
- package/dist/state/installRoot.js +279 -0
- package/dist/state/installRoot.js.map +1 -0
- package/dist/state/progressCommit.js +289 -0
- package/dist/state/progressCommit.js.map +1 -0
- package/dist/state/startupAnalysis.js +1953 -0
- package/dist/state/startupAnalysis.js.map +1 -0
- package/dist/upgrade/appModel.js +189 -0
- package/dist/upgrade/appModel.js.map +1 -0
- package/dist/upgrade/channels.js +197 -0
- package/dist/upgrade/channels.js.map +1 -0
- package/dist/upgrade/compatibility.js +197 -0
- package/dist/upgrade/compatibility.js.map +1 -0
- package/dist/upgrade/doctor.js +368 -0
- package/dist/upgrade/doctor.js.map +1 -0
- package/dist/upgrade/migrateArtifactsV2ToV3.js +412 -0
- package/dist/upgrade/migrateArtifactsV2ToV3.js.map +1 -0
- package/dist/upgrade/upgradeCommands.js +40 -0
- package/dist/upgrade/upgradeCommands.js.map +1 -0
- package/dist/upgrade/upgradeOrchestrator.js +280 -0
- package/dist/upgrade/upgradeOrchestrator.js.map +1 -0
- package/dist/validate/appHomeContract.js +150 -0
- package/dist/validate/appHomeContract.js.map +1 -0
- package/dist/validate/capability.js +412 -0
- package/dist/validate/capability.js.map +1 -0
- package/dist/validate/crossCapability.js +145 -0
- package/dist/validate/crossCapability.js.map +1 -0
- package/dist/validate/lifecycleAdapters.js +772 -0
- package/dist/validate/lifecycleAdapters.js.map +1 -0
- package/dist/validate/selfAudit.js +107 -0
- package/dist/validate/selfAudit.js.map +1 -0
- package/package.json +28 -8
- package/LICENSE +0 -201
- package/bin/agentera.mjs +0 -50
- package/lib/exec.mjs +0 -116
- package/lib/resolve.mjs +0 -129
|
@@ -0,0 +1,2421 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { loadYamlMapping } from "../core/yaml.js";
|
|
4
|
+
import { publicDoctorStatus } from "../upgrade/doctor.js";
|
|
5
|
+
import { activeAppModel, discoverSchemasDir } from "./appContext.js";
|
|
6
|
+
import { artifactPath } from "./appContext.js";
|
|
7
|
+
import { asList, firstPresent, sourceMetadata } from "./stateQuery.js";
|
|
8
|
+
import { loadNamedArtifact } from "./orientation.js";
|
|
9
|
+
import { decisionContextEntry, decisionSourceContract, extractDecisionEntries } from "./commands/state.js";
|
|
10
|
+
export const CAPABILITY_NAMES = [
|
|
11
|
+
"hej", "visionera", "resonera", "inspirera", "planera", "realisera",
|
|
12
|
+
"optimera", "inspektera", "dokumentera", "profilera", "visualisera", "orkestrera",
|
|
13
|
+
];
|
|
14
|
+
export const BESPOKE_CONTEXT_CAPABILITIES = new Set([]);
|
|
15
|
+
const STATE_FAMILY_FALLBACK_COMMANDS = {
|
|
16
|
+
plan: "agentera plan --format json",
|
|
17
|
+
docs: "agentera docs --format json",
|
|
18
|
+
progress: "agentera progress --format json",
|
|
19
|
+
health: "agentera health --format json",
|
|
20
|
+
todo: "agentera todo --format json",
|
|
21
|
+
decisions: "agentera decisions --format json",
|
|
22
|
+
changelog: "agentera query changelog --format json",
|
|
23
|
+
objective: "agentera objective --format json",
|
|
24
|
+
experiments: "agentera experiments --format json",
|
|
25
|
+
};
|
|
26
|
+
const STARTUP_ENVELOPE_STATE_FAMILIES = new Set([
|
|
27
|
+
"plan", "docs", "progress", "health", "todo", "objective", "benchmark_context",
|
|
28
|
+
]);
|
|
29
|
+
const PLANERA_STARTUP_CONTRACT_VERSION = "agentera.planeraStartup.v1";
|
|
30
|
+
const PLANERA_PLANNING_LEVELS = ["skip", "light", "full"];
|
|
31
|
+
const PLANERA_STEP_VERBS = ["orient", "specify", "review", "audit", "write", "handoff"];
|
|
32
|
+
const PLANERA_INSTRUCTIONS_AUTHORITY_EXCEPTIONS = [
|
|
33
|
+
"editing Planera behavior or instructions",
|
|
34
|
+
"resolving contradiction or ambiguity in compact context",
|
|
35
|
+
"validating detailed behavior not covered by compact context",
|
|
36
|
+
"investigating benchmark or read-trigger evidence",
|
|
37
|
+
];
|
|
38
|
+
const PLANERA_RAW_PLAN_ACCESS_ALLOWED_FOR = [
|
|
39
|
+
"writing a new plan",
|
|
40
|
+
"archiving a completed plan",
|
|
41
|
+
"artifact validation",
|
|
42
|
+
"corruption diagnostics",
|
|
43
|
+
"unavailable or incomplete CLI state after CLI fallbacks",
|
|
44
|
+
];
|
|
45
|
+
const PLANERA_COMPLETED_PLAN_ARCHIVE_CONFIRMATION = {
|
|
46
|
+
direct_planera_invocation: "Archiving an already completed existing plan before writing its replacement is implicit " +
|
|
47
|
+
"in the direct Planera invocation and does not require a separate pre-write confirmation.",
|
|
48
|
+
human_initiated_plan_write: "Plan approval is still required before writing a human-initiated replacement plan.",
|
|
49
|
+
active_or_incomplete_plan: "Replacing, discarding, or archiving an active or incomplete plan is not implicit; " +
|
|
50
|
+
"ask for explicit confirmation before the write.",
|
|
51
|
+
};
|
|
52
|
+
function isFile(p) {
|
|
53
|
+
try {
|
|
54
|
+
return fs.statSync(p).isFile();
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function entryStatus(entry, def = "open") {
|
|
61
|
+
const raw = "status" in entry ? entry.status : def;
|
|
62
|
+
return String(raw || def).toLowerCase();
|
|
63
|
+
}
|
|
64
|
+
function pyRepr(value) {
|
|
65
|
+
return value.includes("'") && !value.includes('"') ? `"${value}"` : `'${value}'`;
|
|
66
|
+
}
|
|
67
|
+
export function validatePrimeCapability(capability) {
|
|
68
|
+
if (!CAPABILITY_NAMES.includes(capability)) {
|
|
69
|
+
const valid = CAPABILITY_NAMES.join(", ");
|
|
70
|
+
throw new Error(`unsupported capability ${pyRepr(capability)}; valid capabilities: ${valid}. ` +
|
|
71
|
+
"Example: agentera prime --context planera --format json");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// ── instruction contract ────────────────────────────────────────────
|
|
75
|
+
function capabilityInstructionContractPath() {
|
|
76
|
+
const model = activeAppModel();
|
|
77
|
+
const active = path.join(String(model.authoritativeRoot ?? model.activeBundleRoot), "references", "cli", "capability-instruction-contract.yaml");
|
|
78
|
+
if (isFile(active))
|
|
79
|
+
return active;
|
|
80
|
+
return path.join(path.resolve(discoverSchemasDir(), "..", "..", "..", ".."), "references", "cli", "capability-instruction-contract.yaml");
|
|
81
|
+
}
|
|
82
|
+
function capabilityInstructionContract() {
|
|
83
|
+
const p = capabilityInstructionContractPath();
|
|
84
|
+
if (!isFile(p))
|
|
85
|
+
return {};
|
|
86
|
+
try {
|
|
87
|
+
return loadYamlMapping(fs.readFileSync(p, "utf8"));
|
|
88
|
+
}
|
|
89
|
+
catch (exc) {
|
|
90
|
+
process.stderr.write(`warning: failed to load capability instruction contract: ${exc.message}\n`);
|
|
91
|
+
return {};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function capabilityInstructionTarget(capability) {
|
|
95
|
+
const skillRoot = String(activeAppModel().skillRoot);
|
|
96
|
+
const rel = `skills/agentera/capabilities/${capability}/instructions.md`;
|
|
97
|
+
const installedTarget = path.join(skillRoot, "capabilities", capability, "instructions.md");
|
|
98
|
+
return { path: rel, exists: isFile(installedTarget), runtime_path: installedTarget };
|
|
99
|
+
}
|
|
100
|
+
function firstInvocationReadMetadata(capability) {
|
|
101
|
+
const authority = capabilityInstructionContract();
|
|
102
|
+
const firstRead = authority.first_invocation_read && typeof authority.first_invocation_read === "object" ? authority.first_invocation_read : {};
|
|
103
|
+
const allowedValues = firstRead.allowed_values && typeof firstRead.allowed_values === "object" ? firstRead.allowed_values : {};
|
|
104
|
+
const value = capability === "planera" ? "compact_startup" : "full";
|
|
105
|
+
const valueContract = (allowedValues[value] ?? {});
|
|
106
|
+
const fullContract = (allowedValues.full ?? {});
|
|
107
|
+
return {
|
|
108
|
+
field: "first_invocation_read",
|
|
109
|
+
value,
|
|
110
|
+
allowed_values: Object.keys(allowedValues),
|
|
111
|
+
default_rule: firstRead.default_rule ?? firstRead.default_future_rule ?? null,
|
|
112
|
+
instruction_target: capabilityInstructionTarget(capability),
|
|
113
|
+
obligation_summary: valueContract.obligation ?? fullContract.obligation ?? null,
|
|
114
|
+
meaning: valueContract.meaning ?? null,
|
|
115
|
+
runtime_enforcement: false,
|
|
116
|
+
provenance: {
|
|
117
|
+
authority_path: "references/cli/capability-instruction-contract.yaml",
|
|
118
|
+
authority_status: authority.status ?? null,
|
|
119
|
+
decision: authority.decision ?? null,
|
|
120
|
+
source_field: "first_invocation_read.allowed_values",
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function planeraStartupContract() {
|
|
125
|
+
return {
|
|
126
|
+
schemaVersion: PLANERA_STARTUP_CONTRACT_VERSION,
|
|
127
|
+
status: "implemented_compact_normal_startup_contract",
|
|
128
|
+
canonical_surface: "agentera prime --context planera --format json",
|
|
129
|
+
bounded: true,
|
|
130
|
+
instructions_runtime_read_required: false,
|
|
131
|
+
instructions_authority: {
|
|
132
|
+
normal_startup: "Use this compact context for normal Planera execution startup before reading " +
|
|
133
|
+
"skills/agentera/capabilities/planera/instructions.md.",
|
|
134
|
+
read_planera_instructions_when: PLANERA_INSTRUCTIONS_AUTHORITY_EXCEPTIONS,
|
|
135
|
+
},
|
|
136
|
+
planning: {
|
|
137
|
+
levels: PLANERA_PLANNING_LEVELS,
|
|
138
|
+
step_marker_format: "── step N/6: verb",
|
|
139
|
+
required_steps: PLANERA_STEP_VERBS,
|
|
140
|
+
full_plan_review_required: true,
|
|
141
|
+
pre_write_self_audit_required: true,
|
|
142
|
+
max_full_plan_tasks: 8,
|
|
143
|
+
},
|
|
144
|
+
cli_first_orientation: {
|
|
145
|
+
use_startup_state_first: true,
|
|
146
|
+
current_plan_command: "agentera plan --format json",
|
|
147
|
+
complete_plan_contract_key: "source_contract.complete_for_plan_artifact",
|
|
148
|
+
fallback_policy: "Use CLI-provided fallback commands for missing or incomplete state families " +
|
|
149
|
+
"before any last-resort raw artifact read.",
|
|
150
|
+
},
|
|
151
|
+
artifact_access_boundaries: {
|
|
152
|
+
skip_raw_plan_artifact_when: "`agentera plan --format json` reports source_contract.complete_for_plan_artifact=true " +
|
|
153
|
+
"during normal read-only startup or evaluation.",
|
|
154
|
+
raw_plan_artifact_allowed_for: PLANERA_RAW_PLAN_ACCESS_ALLOWED_FOR,
|
|
155
|
+
completed_plan_archive_confirmation: PLANERA_COMPLETED_PLAN_ARCHIVE_CONFIRMATION,
|
|
156
|
+
artifact_mapping_source: "agentera docs/query artifact mapping before writes or closeout",
|
|
157
|
+
},
|
|
158
|
+
handoff_expectations: [
|
|
159
|
+
"skip level suggests ⧉ realisera and waits for confirmation unless the user already asked to implement now",
|
|
160
|
+
"single-task plans suggest ⧉ realisera and wait for confirmation",
|
|
161
|
+
"full plans suggest ⎈ orkestrera and wait for confirmation",
|
|
162
|
+
],
|
|
163
|
+
unsupported_command_boundary: {
|
|
164
|
+
capability_cli_commands_added: true,
|
|
165
|
+
forbidden_examples: [],
|
|
166
|
+
route_boundary: "Use `/agentera plan` for routing, `agentera plan` for plan state, " +
|
|
167
|
+
"and `agentera planera` for capability routing guidance only.",
|
|
168
|
+
},
|
|
169
|
+
seam_decision: {
|
|
170
|
+
selected: "prime --context",
|
|
171
|
+
not_changed: [
|
|
172
|
+
{ surface: "agentera schema --format json", reason: "runtime/schema command discovery, not capability workflow startup context" },
|
|
173
|
+
{ surface: "dispatcher guidance", reason: "route and CLI-state separation guidance, not a bounded Planera workflow payload" },
|
|
174
|
+
],
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
// ── artifact inventory + capability context ─────────────────────────
|
|
179
|
+
function appendUnique(items, value) {
|
|
180
|
+
if (value && !items.includes(value))
|
|
181
|
+
items.push(value);
|
|
182
|
+
}
|
|
183
|
+
function capabilityArtifactInventory(capability) {
|
|
184
|
+
const inventory = { read_needs: [], write_targets: [] };
|
|
185
|
+
const capabilityDir = path.join(String(activeAppModel().skillRoot), "capabilities", capability);
|
|
186
|
+
const p = path.join(capabilityDir, "schemas", "artifacts.yaml");
|
|
187
|
+
if (!isFile(p))
|
|
188
|
+
return [inventory, `No capability artifact schema found for ${capability}.`];
|
|
189
|
+
let data;
|
|
190
|
+
try {
|
|
191
|
+
data = loadYamlMapping(fs.readFileSync(p, "utf8"));
|
|
192
|
+
}
|
|
193
|
+
catch (exc) {
|
|
194
|
+
return [inventory, `Capability artifact schema for ${capability} could not be read: ${exc.message}`];
|
|
195
|
+
}
|
|
196
|
+
const artifacts = data.ARTIFACTS;
|
|
197
|
+
if (!artifacts || typeof artifacts !== "object" || Array.isArray(artifacts)) {
|
|
198
|
+
return [inventory, `Capability artifact schema for ${capability} has no ARTIFACTS mapping.`];
|
|
199
|
+
}
|
|
200
|
+
const errors = [];
|
|
201
|
+
for (const [key, entry] of Object.entries(artifacts)) {
|
|
202
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
203
|
+
errors.push(`entry ${key} is not a mapping`);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
const e = entry;
|
|
207
|
+
const artifactId = String(e.artifact_id ?? "").trim();
|
|
208
|
+
const localRole = String(e.local_role ?? "").trim();
|
|
209
|
+
if (!artifactId) {
|
|
210
|
+
errors.push(`entry ${key} is missing artifact_id`);
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (localRole === "consumes")
|
|
214
|
+
appendUnique(inventory.read_needs, artifactId);
|
|
215
|
+
else if (localRole === "produces")
|
|
216
|
+
appendUnique(inventory.write_targets, artifactId);
|
|
217
|
+
else if (localRole === "produces_and_consumes") {
|
|
218
|
+
appendUnique(inventory.read_needs, artifactId);
|
|
219
|
+
appendUnique(inventory.write_targets, artifactId);
|
|
220
|
+
}
|
|
221
|
+
else
|
|
222
|
+
errors.push(`entry ${key} has unsupported local_role ${pyRepr(localRole)}`);
|
|
223
|
+
}
|
|
224
|
+
const error = errors.length > 0 ? `Capability artifact schema for ${capability} has invalid ARTIFACTS entries: ${errors.join("; ")}.` : null;
|
|
225
|
+
return [inventory, error];
|
|
226
|
+
}
|
|
227
|
+
function capabilityContext(capability) {
|
|
228
|
+
if (!capability)
|
|
229
|
+
return null;
|
|
230
|
+
const [inventory, error] = capabilityArtifactInventory(capability);
|
|
231
|
+
const needs = inventory.read_needs;
|
|
232
|
+
const writeTargets = inventory.write_targets;
|
|
233
|
+
const missing = needs.filter((name) => !STARTUP_ENVELOPE_STATE_FAMILIES.has(name));
|
|
234
|
+
const cliFallback = missing.filter((name) => name in STATE_FAMILY_FALLBACK_COMMANDS).map((name) => STATE_FAMILY_FALLBACK_COMMANDS[name]);
|
|
235
|
+
const context = {
|
|
236
|
+
capability,
|
|
237
|
+
first_invocation_read: firstInvocationReadMetadata(capability),
|
|
238
|
+
declared_state_needs: needs,
|
|
239
|
+
declared_write_targets: writeTargets,
|
|
240
|
+
artifact_inventory: inventory,
|
|
241
|
+
included_state_families: needs.filter((name) => STARTUP_ENVELOPE_STATE_FAMILIES.has(name)),
|
|
242
|
+
missing_state_families: missing,
|
|
243
|
+
cli_fallback: cliFallback,
|
|
244
|
+
raw_artifact_read_policy: "Use the included state families from this prime --context response first. " +
|
|
245
|
+
"If needed families are missing or CLI state is incomplete, run the CLI fallback commands before raw file access.",
|
|
246
|
+
schema_error: error,
|
|
247
|
+
};
|
|
248
|
+
if (capability === "planera")
|
|
249
|
+
context.startup_contract = planeraStartupContract();
|
|
250
|
+
return context;
|
|
251
|
+
}
|
|
252
|
+
// ── slim state helpers ──────────────────────────────────────────────
|
|
253
|
+
function taskRef(task) {
|
|
254
|
+
return { number: task.number ?? null, name: firstPresent(task, ["name", "title"], ""), status: entryStatus(task, "pending") };
|
|
255
|
+
}
|
|
256
|
+
function sourceProvenance(sourceFamily, command, field = null) {
|
|
257
|
+
const provenance = { source_family: sourceFamily, command };
|
|
258
|
+
if (field)
|
|
259
|
+
provenance.field = field;
|
|
260
|
+
return provenance;
|
|
261
|
+
}
|
|
262
|
+
function docsConventions(docs) {
|
|
263
|
+
const conventions = docs.conventions;
|
|
264
|
+
return conventions && typeof conventions === "object" && !Array.isArray(conventions) ? conventions : {};
|
|
265
|
+
}
|
|
266
|
+
function hasRecordedValue(value) {
|
|
267
|
+
if (value === null || value === undefined)
|
|
268
|
+
return false;
|
|
269
|
+
if (typeof value === "string")
|
|
270
|
+
return value.trim().length > 0;
|
|
271
|
+
if (Array.isArray(value))
|
|
272
|
+
return value.length > 0;
|
|
273
|
+
if (typeof value === "object")
|
|
274
|
+
return Object.keys(value).length > 0;
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
function fallbackStatePointer(artifactId, command) {
|
|
278
|
+
return { status: "fallback_only", artifact_id: artifactId, fallback_command: command, raw_artifact_reads_required: false };
|
|
279
|
+
}
|
|
280
|
+
function capabilityContextAppSummary(appHome, bundle) {
|
|
281
|
+
const caveats = [];
|
|
282
|
+
if (appHome.status !== "up_to_date") {
|
|
283
|
+
caveats.push("Agentera app files are not up to date; this is a caveat, not approval to repair or update app files.");
|
|
284
|
+
}
|
|
285
|
+
return {
|
|
286
|
+
status: appHome.status,
|
|
287
|
+
home: appHome.home,
|
|
288
|
+
source: appHome.source,
|
|
289
|
+
managed_app_root: appHome.managed_app_root,
|
|
290
|
+
user_data_root: appHome.user_data_root,
|
|
291
|
+
expected_version: bundle.expectedVersion,
|
|
292
|
+
caveats,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
function capabilityContextProfileSummary(profile) {
|
|
296
|
+
const caveats = [];
|
|
297
|
+
if (profile.status !== "loaded")
|
|
298
|
+
caveats.push("profile-derived state is unavailable in prime --context response.");
|
|
299
|
+
else if (profile.stale === true)
|
|
300
|
+
caveats.push("profile-derived state is stale; this is a caveat, not approval to refresh profile state.");
|
|
301
|
+
const summary = {};
|
|
302
|
+
for (const key of ["status", "path", "stale", "days_since_generated", "stale_threshold_days", "suggested_action"]) {
|
|
303
|
+
if (key in profile)
|
|
304
|
+
summary[key] = profile[key];
|
|
305
|
+
}
|
|
306
|
+
summary.caveats = caveats;
|
|
307
|
+
return summary;
|
|
308
|
+
}
|
|
309
|
+
function slimPlanState(plan) {
|
|
310
|
+
const firstPending = plan.first_pending;
|
|
311
|
+
return {
|
|
312
|
+
exists: Boolean(plan.exists),
|
|
313
|
+
status: plan.status ?? null,
|
|
314
|
+
title: plan.title ?? null,
|
|
315
|
+
complete: plan.complete ?? null,
|
|
316
|
+
total: plan.total ?? null,
|
|
317
|
+
first_pending: firstPending && typeof firstPending === "object" && !Array.isArray(firstPending) ? taskRef(firstPending) : null,
|
|
318
|
+
source_provenance: sourceProvenance("plan", "agentera plan --format json"),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
function slimDocsState(docs) {
|
|
322
|
+
const conventions = docsConventions(docs);
|
|
323
|
+
return {
|
|
324
|
+
exists: Boolean(docs.exists),
|
|
325
|
+
status: docs.status ?? null,
|
|
326
|
+
mapping_entries: docs.mapping_entries ?? asList(docs.mapping).length,
|
|
327
|
+
version_policy: {
|
|
328
|
+
version_files: asList(conventions.version_files),
|
|
329
|
+
semver_policy: conventions.semver_policy && typeof conventions.semver_policy === "object" && !Array.isArray(conventions.semver_policy) ? conventions.semver_policy : {},
|
|
330
|
+
},
|
|
331
|
+
source_provenance: sourceProvenance("docs", "agentera docs --format json", "summary"),
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
function slimProgressState(progress) {
|
|
335
|
+
const latest = progress.latest && typeof progress.latest === "object" && !Array.isArray(progress.latest) ? progress.latest : {};
|
|
336
|
+
const latestCycle = {};
|
|
337
|
+
for (const key of ["number", "timestamp", "type", "phase"]) {
|
|
338
|
+
if (latest[key] !== null && latest[key] !== undefined && latest[key] !== "")
|
|
339
|
+
latestCycle[key] = latest[key];
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
exists: Boolean(progress.exists),
|
|
343
|
+
status: progress.status ?? null,
|
|
344
|
+
latest_cycle: latestCycle,
|
|
345
|
+
verified_present: hasRecordedValue(latest.verified),
|
|
346
|
+
source_provenance: sourceProvenance("progress", "agentera progress --format json"),
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function slimHealthState(health) {
|
|
350
|
+
return {
|
|
351
|
+
exists: Boolean(health.exists),
|
|
352
|
+
number: health.number ?? null,
|
|
353
|
+
grade: health.grade ?? null,
|
|
354
|
+
trajectory: health.trajectory ?? null,
|
|
355
|
+
worst: health.worst ?? null,
|
|
356
|
+
degrading: Boolean(health.degrading),
|
|
357
|
+
source_provenance: sourceProvenance("health", "agentera health --format json"),
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
function slimTodoState(todoItems) {
|
|
361
|
+
const severityCounts = {};
|
|
362
|
+
for (const item of todoItems) {
|
|
363
|
+
const severity = String(item.severity ?? "normal");
|
|
364
|
+
severityCounts[severity] = (severityCounts[severity] ?? 0) + 1;
|
|
365
|
+
}
|
|
366
|
+
return {
|
|
367
|
+
open_count: todoItems.length,
|
|
368
|
+
severity_counts: severityCounts,
|
|
369
|
+
source_provenance: sourceProvenance("todo", "agentera todo --format json"),
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function genericSlimStartupContext(capability, context, plan, docs, progress, health, todoItems, profile) {
|
|
373
|
+
const decisionsPointer = fallbackStatePointer("decisions", "agentera decisions --format json");
|
|
374
|
+
const docsState = slimDocsState(docs);
|
|
375
|
+
const profileState = capabilityContextProfileSummary(profile);
|
|
376
|
+
if (capability === "visionera") {
|
|
377
|
+
return {
|
|
378
|
+
vision_startup_context: {
|
|
379
|
+
vision: fallbackStatePointer("vision", "agentera query vision --format json"),
|
|
380
|
+
docs_mapping: docsState,
|
|
381
|
+
progress: slimProgressState(progress),
|
|
382
|
+
health: slimHealthState(health),
|
|
383
|
+
todo: slimTodoState(todoItems),
|
|
384
|
+
decisions: decisionsPointer,
|
|
385
|
+
design: fallbackStatePointer("design", "agentera query design --format json"),
|
|
386
|
+
profile: profileState,
|
|
387
|
+
},
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
if (capability === "resonera") {
|
|
391
|
+
return {
|
|
392
|
+
deliberation_context: {
|
|
393
|
+
decisions: decisionsPointer,
|
|
394
|
+
vision: fallbackStatePointer("vision", "agentera query vision --format json"),
|
|
395
|
+
objective: fallbackStatePointer("objective", "agentera objective --format json"),
|
|
396
|
+
todo: slimTodoState(todoItems),
|
|
397
|
+
docs_mapping: docsState,
|
|
398
|
+
profile: profileState,
|
|
399
|
+
protected_write_boundaries: ["vision", "todo", "objective"],
|
|
400
|
+
},
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
if (capability === "inspirera") {
|
|
404
|
+
return {
|
|
405
|
+
research_context: {
|
|
406
|
+
profile: profileState,
|
|
407
|
+
vision: fallbackStatePointer("vision", "agentera query vision --format json"),
|
|
408
|
+
write_boundaries: ["todo", "vision"],
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
if (capability === "planera") {
|
|
413
|
+
return {
|
|
414
|
+
planning_context: {
|
|
415
|
+
startup_contract: context.startup_contract ?? null,
|
|
416
|
+
plan: slimPlanState(plan),
|
|
417
|
+
docs: docsState,
|
|
418
|
+
health: slimHealthState(health),
|
|
419
|
+
todo: slimTodoState(todoItems),
|
|
420
|
+
progress: slimProgressState(progress),
|
|
421
|
+
decisions: decisionsPointer,
|
|
422
|
+
profile: profileState,
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
if (capability === "profilera") {
|
|
427
|
+
return {
|
|
428
|
+
profile_context: { profile: profileState, decisions: decisionsPointer, raw_profile_body_emitted: false },
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
if (capability === "visualisera") {
|
|
432
|
+
return {
|
|
433
|
+
design_context: {
|
|
434
|
+
design: fallbackStatePointer("design", "agentera query design --format json"),
|
|
435
|
+
vision: fallbackStatePointer("vision", "agentera query vision --format json"),
|
|
436
|
+
progress: slimProgressState(progress),
|
|
437
|
+
todo: slimTodoState(todoItems),
|
|
438
|
+
docs_mapping: docsState,
|
|
439
|
+
profile: profileState,
|
|
440
|
+
},
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
return {};
|
|
444
|
+
}
|
|
445
|
+
function slimCapabilityContext(capability, mode, appHome, bundle, profile, plan, docs, progress, health, todoItems, bespokeContexts) {
|
|
446
|
+
const context = capabilityContext(capability) ?? {
|
|
447
|
+
capability,
|
|
448
|
+
declared_state_needs: [],
|
|
449
|
+
declared_write_targets: [],
|
|
450
|
+
artifact_inventory: { read_needs: [], write_targets: [] },
|
|
451
|
+
included_state_families: [],
|
|
452
|
+
missing_state_families: [],
|
|
453
|
+
cli_fallback: [],
|
|
454
|
+
raw_artifact_read_policy: "Use the included state families from this prime --context response first. " +
|
|
455
|
+
"If needed families are missing or CLI state is incomplete, run the CLI fallback commands before raw file access.",
|
|
456
|
+
schema_error: `No capability context found for ${capability}.`,
|
|
457
|
+
};
|
|
458
|
+
const contextPayload = { capability, schema_error: context.schema_error ?? null };
|
|
459
|
+
Object.assign(contextPayload, genericSlimStartupContext(capability, context, plan, docs, progress, health, todoItems, profile));
|
|
460
|
+
const firstRead = context.first_invocation_read;
|
|
461
|
+
if (firstRead !== null && firstRead !== undefined)
|
|
462
|
+
contextPayload.first_invocation_read = firstRead;
|
|
463
|
+
// bespoke contexts are all null for the six non-bespoke capabilities.
|
|
464
|
+
if (bespokeContexts) {
|
|
465
|
+
for (const [name, value] of Object.entries(bespokeContexts)) {
|
|
466
|
+
if (value !== null && value !== undefined)
|
|
467
|
+
contextPayload[name] = slimBespokeContext(name, value);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return {
|
|
471
|
+
schemaVersion: "agentera.capabilityContext.v1",
|
|
472
|
+
capability,
|
|
473
|
+
mode,
|
|
474
|
+
app: capabilityContextAppSummary(appHome, bundle),
|
|
475
|
+
profile: capabilityContextProfileSummary(profile),
|
|
476
|
+
state: {
|
|
477
|
+
declared_read_needs: context.declared_state_needs ?? [],
|
|
478
|
+
declared_write_targets: context.declared_write_targets ?? [],
|
|
479
|
+
artifact_inventory: context.artifact_inventory ?? { read_needs: [], write_targets: [] },
|
|
480
|
+
included: context.included_state_families ?? [],
|
|
481
|
+
missing: context.missing_state_families ?? [],
|
|
482
|
+
fallback_commands: context.cli_fallback ?? [],
|
|
483
|
+
schema_error: context.schema_error ?? null,
|
|
484
|
+
},
|
|
485
|
+
context: contextPayload,
|
|
486
|
+
raw_artifact_read_policy: context.raw_artifact_read_policy ?? null,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
function orientationAppHome(bundle) {
|
|
490
|
+
return {
|
|
491
|
+
status: bundle.status,
|
|
492
|
+
home: bundle.appHome,
|
|
493
|
+
source: bundle.appHomeSource,
|
|
494
|
+
managed_app_root: bundle.managedAppRoot,
|
|
495
|
+
user_data_root: bundle.userDataRoot,
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
export function buildPrimeCapabilityContextPayload(state, capabilityName, command = "prime") {
|
|
499
|
+
const bundlePublic = publicDoctorStatus(state.bundle);
|
|
500
|
+
const appHome = orientationAppHome(state.bundle);
|
|
501
|
+
const bespoke = bespokeCapabilityContexts(capabilityName, state);
|
|
502
|
+
return {
|
|
503
|
+
command,
|
|
504
|
+
status: "ok",
|
|
505
|
+
capability_context: slimCapabilityContext(capabilityName, state.mode, appHome, bundlePublic, state.profile_dict, state.plan, state.docs, state.progress, state.health, state.todo_items, bespoke),
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
// ── orchestration bespoke context (orkestrera) ──────────────────────
|
|
509
|
+
function orchestrationTaskSummary(task) {
|
|
510
|
+
const evidence = task.evidence;
|
|
511
|
+
const evidenceItems = Array.isArray(evidence) ? evidence : evidence === null || evidence === undefined || evidence === "" ? [] : [evidence];
|
|
512
|
+
return {
|
|
513
|
+
...taskRef(task),
|
|
514
|
+
depends_on: asList(task.depends_on),
|
|
515
|
+
acceptance_summary: { count: asList(task.acceptance).length, items: asList(task.acceptance) },
|
|
516
|
+
evidence_summary: { count: evidenceItems.length, items: evidenceItems },
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
function progressVerificationSummary(progress) {
|
|
520
|
+
const source = { source_family: "progress", command: "agentera progress --format json" };
|
|
521
|
+
if (!progress.exists) {
|
|
522
|
+
return {
|
|
523
|
+
status: "unavailable",
|
|
524
|
+
source_provenance: source,
|
|
525
|
+
cycle: null,
|
|
526
|
+
verified_present: false,
|
|
527
|
+
non_empty_evidence_present: false,
|
|
528
|
+
non_empty_evidence_fields: [],
|
|
529
|
+
verified: null,
|
|
530
|
+
verification_summary: null,
|
|
531
|
+
latest_progress_verification_pointer: null,
|
|
532
|
+
caveats: ["No progress cycles are recorded in CLI progress state."],
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
const latest = progress.latest && typeof progress.latest === "object" && !Array.isArray(progress.latest) ? progress.latest : {};
|
|
536
|
+
const verified = latest.verified;
|
|
537
|
+
const verifiedPresent = hasRecordedValue(verified);
|
|
538
|
+
const cycle = {};
|
|
539
|
+
for (const key of ["number", "timestamp", "type", "phase"]) {
|
|
540
|
+
if (latest[key] !== null && latest[key] !== undefined && latest[key] !== "")
|
|
541
|
+
cycle[key] = latest[key];
|
|
542
|
+
}
|
|
543
|
+
const pointer = { ...source, cycle_number: latest.number ?? null, field: "verified" };
|
|
544
|
+
const caveats = verifiedPresent ? [] : ["Latest progress cycle has no non-empty verified evidence."];
|
|
545
|
+
const evidenceFields = verifiedPresent ? ["verified"] : [];
|
|
546
|
+
return {
|
|
547
|
+
status: "available",
|
|
548
|
+
source_provenance: source,
|
|
549
|
+
cycle,
|
|
550
|
+
verified_present: verifiedPresent,
|
|
551
|
+
non_empty_evidence_present: verifiedPresent,
|
|
552
|
+
non_empty_evidence_fields: evidenceFields,
|
|
553
|
+
verified: verifiedPresent ? verified : null,
|
|
554
|
+
verification_summary: verifiedPresent ? verified : null,
|
|
555
|
+
latest_progress_verification_pointer: pointer,
|
|
556
|
+
caveats,
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
function retryState() {
|
|
560
|
+
return {
|
|
561
|
+
status: "not_recorded",
|
|
562
|
+
source_provenance: {
|
|
563
|
+
source_family: "progress",
|
|
564
|
+
command: "agentera progress --format json",
|
|
565
|
+
reason: "Current CLI/artifact state records progress cycles but no retry attempt state for orchestration tasks.",
|
|
566
|
+
},
|
|
567
|
+
caveats: ["Retry attempt state is not recorded; no attempt count is exposed."],
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
function evaluatorHandoff(selected, progressVerification, retry, stateCaveats) {
|
|
571
|
+
const caveats = [...stateCaveats, ...(progressVerification.caveats ?? []), ...(retry.caveats ?? [])];
|
|
572
|
+
if (selected === null) {
|
|
573
|
+
caveats.push("No dependency-ready task is selected for evaluator handoff.");
|
|
574
|
+
return {
|
|
575
|
+
status: "unavailable",
|
|
576
|
+
task: null,
|
|
577
|
+
acceptance_criteria: [],
|
|
578
|
+
evidence_requirements: [],
|
|
579
|
+
latest_progress_verification_pointer: progressVerification.latest_progress_verification_pointer ?? null,
|
|
580
|
+
evaluation_caveats: caveats,
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
const evidenceRequirements = (selected.evidence_summary?.items ?? []);
|
|
584
|
+
if (evidenceRequirements.length === 0) {
|
|
585
|
+
caveats.push("Selected task has no explicit evidence requirements recorded in plan state.");
|
|
586
|
+
}
|
|
587
|
+
return {
|
|
588
|
+
status: "ready",
|
|
589
|
+
task: taskRef(selected),
|
|
590
|
+
acceptance_criteria: selected.acceptance_summary?.items ?? [],
|
|
591
|
+
evidence_requirements: evidenceRequirements,
|
|
592
|
+
latest_progress_verification_pointer: progressVerification.latest_progress_verification_pointer ?? null,
|
|
593
|
+
evaluation_caveats: caveats,
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
function uniqueList(items) {
|
|
597
|
+
const out = [];
|
|
598
|
+
for (const item of items)
|
|
599
|
+
if (!out.includes(item))
|
|
600
|
+
out.push(item);
|
|
601
|
+
return out;
|
|
602
|
+
}
|
|
603
|
+
function orchestrationContext(capability, plan, progress, health, todoItems, docs, profile, nextAction) {
|
|
604
|
+
if (capability !== "orkestrera")
|
|
605
|
+
return null;
|
|
606
|
+
const tasks = asList(plan.tasks).filter((t) => t && typeof t === "object" && !Array.isArray(t));
|
|
607
|
+
const taskByNumber = {};
|
|
608
|
+
for (const task of tasks)
|
|
609
|
+
if (task.number !== null && task.number !== undefined)
|
|
610
|
+
taskByNumber[String(task.number)] = task;
|
|
611
|
+
const dependencyReady = [];
|
|
612
|
+
const blocked = [];
|
|
613
|
+
for (const task of tasks) {
|
|
614
|
+
const status = entryStatus(task, "pending");
|
|
615
|
+
if (DONE_STATUSES_ORCH.has(status))
|
|
616
|
+
continue;
|
|
617
|
+
const reasons = [];
|
|
618
|
+
if (BLOCKED_STATUSES_ORCH.has(status))
|
|
619
|
+
reasons.push(`task status is ${status}`);
|
|
620
|
+
for (const dep of asList(task.depends_on)) {
|
|
621
|
+
const dependency = taskByNumber[String(dep)];
|
|
622
|
+
if (dependency === undefined)
|
|
623
|
+
reasons.push(`dependency ${dep} is not present in plan tasks`);
|
|
624
|
+
else if (!DONE_STATUSES_ORCH.has(entryStatus(dependency, "pending"))) {
|
|
625
|
+
reasons.push(`dependency ${dep} is ${entryStatus(dependency, "pending")}`);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
if (reasons.length > 0)
|
|
629
|
+
blocked.push({ ...orchestrationTaskSummary(task), blocked_reasons: reasons });
|
|
630
|
+
else
|
|
631
|
+
dependencyReady.push(orchestrationTaskSummary(task));
|
|
632
|
+
}
|
|
633
|
+
const selected = dependencyReady.length > 0 ? dependencyReady[0] : null;
|
|
634
|
+
const stateCaveats = [];
|
|
635
|
+
let fallbackCommands = [];
|
|
636
|
+
const capabilityContract = capabilityContext(capability) ?? {};
|
|
637
|
+
for (const family of (capabilityContract.missing_state_families ?? [])) {
|
|
638
|
+
stateCaveats.push(`${family} state is not included in prime --context startup context.`);
|
|
639
|
+
}
|
|
640
|
+
fallbackCommands.push(...(capabilityContract.cli_fallback ?? []));
|
|
641
|
+
if (!plan.exists) {
|
|
642
|
+
stateCaveats.push("plan state is unavailable; task queue cannot be complete.");
|
|
643
|
+
fallbackCommands.push("agentera plan --format json");
|
|
644
|
+
}
|
|
645
|
+
if (!progress.exists) {
|
|
646
|
+
stateCaveats.push("progress state is unavailable; latest verification is not summarized here.");
|
|
647
|
+
fallbackCommands.push("agentera progress --format json");
|
|
648
|
+
}
|
|
649
|
+
if (!health.exists) {
|
|
650
|
+
stateCaveats.push("health state is unavailable or incomplete.");
|
|
651
|
+
fallbackCommands.push("agentera health --format json");
|
|
652
|
+
}
|
|
653
|
+
if (!docs.exists) {
|
|
654
|
+
stateCaveats.push("docs mapping state is unavailable or incomplete.");
|
|
655
|
+
fallbackCommands.push("agentera docs --format json");
|
|
656
|
+
}
|
|
657
|
+
if (todoItems.length === 0) {
|
|
658
|
+
stateCaveats.push("todo state has no open entries in prime --context response; absence may mean none open or unavailable.");
|
|
659
|
+
fallbackCommands.push("agentera todo --format json");
|
|
660
|
+
}
|
|
661
|
+
if (profile.status !== "loaded") {
|
|
662
|
+
stateCaveats.push("profile-derived state is unavailable in prime --context response.");
|
|
663
|
+
}
|
|
664
|
+
else if (profile.stale === true) {
|
|
665
|
+
stateCaveats.push("profile-derived state is stale; this is a caveat, not approval to refresh profile state.");
|
|
666
|
+
}
|
|
667
|
+
fallbackCommands = uniqueList(fallbackCommands);
|
|
668
|
+
const progressVerification = progressVerificationSummary(progress);
|
|
669
|
+
const retry = retryState();
|
|
670
|
+
const handoff = evaluatorHandoff(selected, progressVerification, retry, stateCaveats);
|
|
671
|
+
const complete = Boolean(plan.exists) && tasks.length > 0 && stateCaveats.length === 0;
|
|
672
|
+
return {
|
|
673
|
+
capability: "orkestrera",
|
|
674
|
+
task_queue: { total: tasks.length, dependency_ready_tasks: dependencyReady, blocked_tasks: blocked },
|
|
675
|
+
selected_next_task: selected,
|
|
676
|
+
selected_next_action: nextAction,
|
|
677
|
+
progress_verification: progressVerification,
|
|
678
|
+
retry_state: retry,
|
|
679
|
+
evaluator_handoff: handoff,
|
|
680
|
+
task_summaries: tasks.map((task) => orchestrationTaskSummary(task)),
|
|
681
|
+
state_family_caveats: stateCaveats,
|
|
682
|
+
fallback_commands: fallbackCommands,
|
|
683
|
+
source_contract: {
|
|
684
|
+
complete_for_orchestration_context: complete,
|
|
685
|
+
raw_artifact_reads_required: false,
|
|
686
|
+
raw_artifact_read_policy: "Use this orchestration_context and included hej state first. Run listed routine CLI fallback commands " +
|
|
687
|
+
"for missing or incomplete state families; raw artifact reads are last-resort diagnostics, not normal startup behavior.",
|
|
688
|
+
included_state_families: capabilityContract.included_state_families ?? [],
|
|
689
|
+
missing_state_families: capabilityContract.missing_state_families ?? [],
|
|
690
|
+
fallback_commands: fallbackCommands,
|
|
691
|
+
caveats: stateCaveats,
|
|
692
|
+
owns: [
|
|
693
|
+
"dependency-ready task queue",
|
|
694
|
+
"blocked task reasons",
|
|
695
|
+
"selected next task",
|
|
696
|
+
"task acceptance summaries",
|
|
697
|
+
"task evidence summaries",
|
|
698
|
+
"latest progress verification summary",
|
|
699
|
+
"retry_state provenance",
|
|
700
|
+
"evaluator handoff inputs",
|
|
701
|
+
"state-family caveats",
|
|
702
|
+
],
|
|
703
|
+
deferred: [],
|
|
704
|
+
},
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
const DONE_STATUSES_ORCH = new Set(["complete", "completed", "closed", "done", "resolved", "retired"]);
|
|
708
|
+
const BLOCKED_STATUSES_ORCH = new Set(["blocked", "stuck"]);
|
|
709
|
+
function compactTaskSummaryForSlim(task) {
|
|
710
|
+
if (!task || typeof task !== "object" || Array.isArray(task))
|
|
711
|
+
return task;
|
|
712
|
+
return {
|
|
713
|
+
number: task.number ?? null,
|
|
714
|
+
name: task.name ?? null,
|
|
715
|
+
status: task.status ?? null,
|
|
716
|
+
depends_on: task.depends_on ?? null,
|
|
717
|
+
acceptance_count: task.acceptance_summary && typeof task.acceptance_summary === "object" ? task.acceptance_summary.count ?? null : null,
|
|
718
|
+
evidence_count: task.evidence_summary && typeof task.evidence_summary === "object" ? task.evidence_summary.count ?? null : null,
|
|
719
|
+
blocked_reasons: task.blocked_reasons ?? null,
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
function compactProgressVerification(value) {
|
|
723
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
724
|
+
return value;
|
|
725
|
+
const out = {};
|
|
726
|
+
for (const key of [
|
|
727
|
+
"status", "source_provenance", "cycle", "verified_present",
|
|
728
|
+
"non_empty_evidence_present", "non_empty_evidence_fields", "latest_progress_verification_pointer", "caveats",
|
|
729
|
+
]) {
|
|
730
|
+
if (key in value)
|
|
731
|
+
out[key] = value[key];
|
|
732
|
+
}
|
|
733
|
+
return out;
|
|
734
|
+
}
|
|
735
|
+
function slimOrchestrationContext(value) {
|
|
736
|
+
const compact = { ...value };
|
|
737
|
+
const taskQueue = value.task_queue && typeof value.task_queue === "object" && !Array.isArray(value.task_queue) ? value.task_queue : {};
|
|
738
|
+
compact.task_queue = {
|
|
739
|
+
total: taskQueue.total ?? null,
|
|
740
|
+
dependency_ready_tasks: asList(taskQueue.dependency_ready_tasks).map((t) => compactTaskSummaryForSlim(t)),
|
|
741
|
+
blocked_tasks: asList(taskQueue.blocked_tasks).map((t) => compactTaskSummaryForSlim(t)),
|
|
742
|
+
};
|
|
743
|
+
compact.progress_verification = compactProgressVerification(value.progress_verification);
|
|
744
|
+
compact.task_summaries = asList(value.task_summaries).map((t) => compactTaskSummaryForSlim(t));
|
|
745
|
+
return compact;
|
|
746
|
+
}
|
|
747
|
+
function slimBespokeContext(name, value) {
|
|
748
|
+
if (name === "orchestration_context")
|
|
749
|
+
return slimOrchestrationContext(value);
|
|
750
|
+
if (name === "evidence_context")
|
|
751
|
+
return slimEvidenceContext(value);
|
|
752
|
+
if (name === "closeout_context")
|
|
753
|
+
return slimCloseoutContext(value);
|
|
754
|
+
return value;
|
|
755
|
+
}
|
|
756
|
+
function bespokeCapabilityContexts(capabilityName, state) {
|
|
757
|
+
return {
|
|
758
|
+
orchestration_context: orchestrationContext(capabilityName, state.plan, state.progress, state.health, state.todo_items, state.docs, state.profile_dict, state.next_action),
|
|
759
|
+
closeout_context: dokumenteraCloseoutContext(capabilityName, state.schemas, state.plan, state.progress, state.todo_items, state.docs, state.profile_dict, state.bundle),
|
|
760
|
+
evidence_context: inspekteraEvidenceContext(capabilityName, state.schemas, state.plan, state.progress, state.health, state.todo_items, state.docs, state.profile_dict, state.bundle),
|
|
761
|
+
benchmark_context: optimeraBenchmarkContext(capabilityName),
|
|
762
|
+
execution_context: realiseraExecutionContext(capabilityName, state.schemas, state.plan, state.progress, state.health, state.todo_items, state.docs, state.profile_dict, state.bundle),
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
// ── realisera execution bespoke context ─────────────────────────────
|
|
766
|
+
const TARGET_VERSION_RE = /\b\d+\.\d+\.\d+\b/;
|
|
767
|
+
function dependencyReadyTasks(tasks) {
|
|
768
|
+
const taskByNumber = {};
|
|
769
|
+
for (const task of tasks)
|
|
770
|
+
if (task.number !== null && task.number !== undefined)
|
|
771
|
+
taskByNumber[String(task.number)] = task;
|
|
772
|
+
const ready = [];
|
|
773
|
+
for (const task of tasks) {
|
|
774
|
+
const status = entryStatus(task, "pending");
|
|
775
|
+
if (DONE_STATUSES_ORCH.has(status) || BLOCKED_STATUSES_ORCH.has(status))
|
|
776
|
+
continue;
|
|
777
|
+
let blocked = false;
|
|
778
|
+
for (const dep of asList(task.depends_on)) {
|
|
779
|
+
const dependency = taskByNumber[String(dep)];
|
|
780
|
+
if (dependency === undefined || !DONE_STATUSES_ORCH.has(entryStatus(dependency, "pending"))) {
|
|
781
|
+
blocked = true;
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
if (!blocked)
|
|
786
|
+
ready.push(task);
|
|
787
|
+
}
|
|
788
|
+
return ready;
|
|
789
|
+
}
|
|
790
|
+
function selectEvidenceTarget(plan) {
|
|
791
|
+
const tasks = asList(plan.tasks).filter((t) => t && typeof t === "object" && !Array.isArray(t));
|
|
792
|
+
const noTarget = {
|
|
793
|
+
status: "no_target",
|
|
794
|
+
target_type: "repository",
|
|
795
|
+
task: null,
|
|
796
|
+
selection_reason: "no_plan_task_target",
|
|
797
|
+
source_provenance: sourceProvenance("plan", "agentera plan --format json", "entries"),
|
|
798
|
+
caveats: ["No plan task target was selected; evaluate repository-level evidence only."],
|
|
799
|
+
};
|
|
800
|
+
if (!plan.exists || tasks.length === 0)
|
|
801
|
+
return noTarget;
|
|
802
|
+
const inProgress = tasks.find((task) => entryStatus(task, "pending") === "in_progress");
|
|
803
|
+
if (inProgress) {
|
|
804
|
+
return {
|
|
805
|
+
status: "selected",
|
|
806
|
+
target_type: "plan_task",
|
|
807
|
+
task: taskRef(inProgress),
|
|
808
|
+
selection_reason: "in_progress_task",
|
|
809
|
+
source_provenance: sourceProvenance("plan", "agentera plan --format json", "entries.status"),
|
|
810
|
+
caveats: [],
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
const ready = dependencyReadyTasks(tasks);
|
|
814
|
+
if (ready.length > 0) {
|
|
815
|
+
return {
|
|
816
|
+
status: "selected",
|
|
817
|
+
target_type: "plan_task",
|
|
818
|
+
task: taskRef(ready[0]),
|
|
819
|
+
selection_reason: "first_dependency_ready_pending_task",
|
|
820
|
+
source_provenance: sourceProvenance("plan", "agentera plan --format json", "entries.depends_on"),
|
|
821
|
+
caveats: [],
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
const completedWithEvidence = [...tasks].reverse().find((task) => DONE_STATUSES_ORCH.has(entryStatus(task, "pending")) && hasRecordedValue(task.evidence));
|
|
825
|
+
if (completedWithEvidence) {
|
|
826
|
+
return {
|
|
827
|
+
status: "selected",
|
|
828
|
+
target_type: "plan_task",
|
|
829
|
+
task: taskRef(completedWithEvidence),
|
|
830
|
+
selection_reason: "latest_completed_task_with_evidence",
|
|
831
|
+
source_provenance: sourceProvenance("plan", "agentera plan --format json", "entries.evidence"),
|
|
832
|
+
caveats: [],
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
return noTarget;
|
|
836
|
+
}
|
|
837
|
+
function taskByRef(plan, ref) {
|
|
838
|
+
if (!ref)
|
|
839
|
+
return null;
|
|
840
|
+
for (const task of asList(plan.tasks)) {
|
|
841
|
+
if (task && typeof task === "object" && !Array.isArray(task) && task.number === ref.number)
|
|
842
|
+
return task;
|
|
843
|
+
}
|
|
844
|
+
return null;
|
|
845
|
+
}
|
|
846
|
+
function planContextField(plan, field) {
|
|
847
|
+
const summary = plan.summary && typeof plan.summary === "object" && !Array.isArray(plan.summary) ? plan.summary : {};
|
|
848
|
+
return field in summary ? summary[field] : plan[field];
|
|
849
|
+
}
|
|
850
|
+
function realiseraScopeBoundary(plan, selected) {
|
|
851
|
+
const explicitPaths = [];
|
|
852
|
+
const scopeField = planContextField(plan, "scope");
|
|
853
|
+
const sources = [selected ?? {}, scopeField && typeof scopeField === "object" ? scopeField : {}];
|
|
854
|
+
for (const source of sources) {
|
|
855
|
+
if (!source || typeof source !== "object" || Array.isArray(source))
|
|
856
|
+
continue;
|
|
857
|
+
for (const key of ["source_files", "files", "paths"]) {
|
|
858
|
+
for (const value of asList(source[key])) {
|
|
859
|
+
const text = String(value).trim();
|
|
860
|
+
if (text && !explicitPaths.includes(text))
|
|
861
|
+
explicitPaths.push(text);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
return {
|
|
866
|
+
artifact_families: ["plan", "progress", "todo", "docs", "health", "changelog", "decisions", "vision", "profile", "design"],
|
|
867
|
+
source_scope: {
|
|
868
|
+
status: explicitPaths.length > 0 ? "explicit" : "unspecified",
|
|
869
|
+
explicit_paths: explicitPaths,
|
|
870
|
+
policy: "Do not infer source-file allowlists or exclusions from task text; use only explicit plan/source-contract paths.",
|
|
871
|
+
},
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
function realiseraArtifactUpdateRequirements(plan, docs) {
|
|
875
|
+
const mapping = asList(docs.mapping);
|
|
876
|
+
const mapped = mapping.filter((e) => e && typeof e === "object" && e.artifact).map((e) => e.artifact);
|
|
877
|
+
return {
|
|
878
|
+
required_families: ["plan", "progress", "todo", "changelog"],
|
|
879
|
+
protected_families: ["vision", "objective", "profile", "installed_app"],
|
|
880
|
+
docs_mapping_available: Boolean(docs.exists && mapping.length > 0),
|
|
881
|
+
mapped_artifacts: mapped,
|
|
882
|
+
plan_status_update_required: Boolean(plan.exists),
|
|
883
|
+
policy: "Update execution artifacts during the cycle; do not mutate protected state without explicit approval.",
|
|
884
|
+
source_provenance: sourceProvenance("docs", "agentera docs --format json", "summary.mapping"),
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
function realiseraPlanCompletionSweep(plan) {
|
|
888
|
+
const complete = Boolean(plan.complete_plan);
|
|
889
|
+
return {
|
|
890
|
+
status: complete ? "eligible" : "not_eligible",
|
|
891
|
+
mutation_allowed: false,
|
|
892
|
+
required_updates: ["progress aggregate cycle", "changelog plan-level entries", "TODO milestone advance", "health cross-reference"],
|
|
893
|
+
archive_candidate: complete ? "active plan archive path is generated only during Realisera sweep execution" : null,
|
|
894
|
+
caveats: complete ? [] : ["Plan completion sweep is not eligible until every plan task is complete."],
|
|
895
|
+
source_provenance: sourceProvenance("plan", "agentera plan --format json", "summary.status"),
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
function selectedTargetVersion(plan) {
|
|
899
|
+
const textParts = [String(plan.title ?? "")];
|
|
900
|
+
const firstPending = plan.first_pending;
|
|
901
|
+
if (firstPending && typeof firstPending === "object" && !Array.isArray(firstPending)) {
|
|
902
|
+
for (const key of ["name", "title"])
|
|
903
|
+
textParts.push(String(firstPending[key] ?? ""));
|
|
904
|
+
}
|
|
905
|
+
for (const task of asList(plan.tasks)) {
|
|
906
|
+
if (task && typeof task === "object" && !Array.isArray(task)) {
|
|
907
|
+
for (const key of ["name", "title"])
|
|
908
|
+
textParts.push(String(task[key] ?? ""));
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
const match = TARGET_VERSION_RE.exec(textParts.join("\n"));
|
|
912
|
+
return match ? match[0] : null;
|
|
913
|
+
}
|
|
914
|
+
function changelogRecordsTarget(text, targetVersion) {
|
|
915
|
+
if (!targetVersion)
|
|
916
|
+
return false;
|
|
917
|
+
const escaped = targetVersion.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
918
|
+
const re = new RegExp(`(?<![\\d.])${escaped}(?![\\d.+-])`);
|
|
919
|
+
return text.split(/\r\n|\r|\n/).some((line) => re.test(line));
|
|
920
|
+
}
|
|
921
|
+
function closeoutChangelogBoundary(schemas, plan) {
|
|
922
|
+
const info = schemas.changelog ?? { path: "CHANGELOG.md", record: undefined, schema: {}, fields: {} };
|
|
923
|
+
const p = artifactPath(info, "changelog");
|
|
924
|
+
const source = sourceMetadata("changelog", p);
|
|
925
|
+
const targetVersion = selectedTargetVersion(plan);
|
|
926
|
+
const unavailable = (caveat) => ({
|
|
927
|
+
status: "unavailable",
|
|
928
|
+
source,
|
|
929
|
+
source_provenance: sourceProvenance("changelog", "agentera query changelog --format json"),
|
|
930
|
+
selected_target_version: targetVersion,
|
|
931
|
+
selected_target_recorded: false,
|
|
932
|
+
unreleased_present: false,
|
|
933
|
+
latest_release_heading: null,
|
|
934
|
+
boundary_present: false,
|
|
935
|
+
boundary: null,
|
|
936
|
+
caveats: [caveat],
|
|
937
|
+
});
|
|
938
|
+
if (!fs.existsSync(p))
|
|
939
|
+
return unavailable("CHANGELOG state is unavailable in CLI state.");
|
|
940
|
+
let text;
|
|
941
|
+
try {
|
|
942
|
+
text = fs.readFileSync(p, "utf8");
|
|
943
|
+
}
|
|
944
|
+
catch (exc) {
|
|
945
|
+
return unavailable(`CHANGELOG state could not be read by the CLI: ${exc.message}`);
|
|
946
|
+
}
|
|
947
|
+
const headings = text.split(/\r\n|\r|\n/).filter((line) => line.startsWith("## ")).map((line) => line.trim());
|
|
948
|
+
const unreleased = headings.find((h) => h.toLowerCase().includes("unreleased")) ?? null;
|
|
949
|
+
const latestRelease = headings.find((h) => !h.toLowerCase().includes("unreleased")) ?? null;
|
|
950
|
+
const selectedRecorded = changelogRecordsTarget(text, targetVersion);
|
|
951
|
+
const caveats = [];
|
|
952
|
+
if (headings.length === 0)
|
|
953
|
+
caveats.push("CHANGELOG state has no release headings.");
|
|
954
|
+
if (targetVersion && !selectedRecorded)
|
|
955
|
+
caveats.push(`CHANGELOG state has no ${targetVersion} closeout entry yet.`);
|
|
956
|
+
const boundary = unreleased || latestRelease;
|
|
957
|
+
return {
|
|
958
|
+
status: headings.length > 0 ? "available" : "incomplete",
|
|
959
|
+
source,
|
|
960
|
+
source_provenance: {
|
|
961
|
+
...sourceProvenance("changelog", "agentera query changelog --format json", "release_headings"),
|
|
962
|
+
internal_source: "CLI-resolved CHANGELOG.md heading scan",
|
|
963
|
+
},
|
|
964
|
+
selected_target_version: targetVersion,
|
|
965
|
+
selected_target_recorded: selectedRecorded,
|
|
966
|
+
unreleased_present: unreleased !== null,
|
|
967
|
+
latest_release_heading: latestRelease,
|
|
968
|
+
boundary_present: boundary !== null,
|
|
969
|
+
boundary,
|
|
970
|
+
release_state: selectedRecorded ? "selected_target_recorded" : "no_selected_target_closeout_entry",
|
|
971
|
+
caveats,
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
function realiseraExecutionContext(capability, schemas, plan, progress, health, todoItems, docs, profile, bundle) {
|
|
975
|
+
if (capability !== "realisera")
|
|
976
|
+
return null;
|
|
977
|
+
const capabilityContract = capabilityContext(capability) ?? {};
|
|
978
|
+
const tasks = asList(plan.tasks).filter((t) => t && typeof t === "object" && !Array.isArray(t));
|
|
979
|
+
const target = selectEvidenceTarget(plan);
|
|
980
|
+
const selected = taskByRef(plan, target && typeof target === "object" ? target.task : null);
|
|
981
|
+
const acceptance = selected && typeof selected === "object" ? asList(selected.acceptance) : [];
|
|
982
|
+
const progressVerification = progressVerificationSummary(progress);
|
|
983
|
+
const changelogBoundary = closeoutChangelogBoundary(schemas, plan);
|
|
984
|
+
const sweep = realiseraPlanCompletionSweep(plan);
|
|
985
|
+
let mode;
|
|
986
|
+
if (plan.complete_plan)
|
|
987
|
+
mode = "completed_plan_sweep";
|
|
988
|
+
else if (!plan.exists || tasks.length === 0)
|
|
989
|
+
mode = "no_plan";
|
|
990
|
+
else if (target.status === "selected" && selected !== null)
|
|
991
|
+
mode = "plan_driven";
|
|
992
|
+
else
|
|
993
|
+
mode = "blocked_or_dependency_unready";
|
|
994
|
+
let stateCaveats = [];
|
|
995
|
+
let fallbackCommands = [];
|
|
996
|
+
for (const family of (capabilityContract.missing_state_families ?? [])) {
|
|
997
|
+
stateCaveats.push(`${family} state is not included in prime --context startup context.`);
|
|
998
|
+
}
|
|
999
|
+
fallbackCommands.push(...(capabilityContract.cli_fallback ?? []));
|
|
1000
|
+
if (!plan.exists) {
|
|
1001
|
+
stateCaveats.push("plan state is unavailable; execution context cannot select plan-driven work.");
|
|
1002
|
+
fallbackCommands.push("agentera plan --format json");
|
|
1003
|
+
}
|
|
1004
|
+
if (mode === "blocked_or_dependency_unready") {
|
|
1005
|
+
stateCaveats.push("No dependency-ready pending plan task is available in CLI plan state.");
|
|
1006
|
+
fallbackCommands.push("agentera plan --format json");
|
|
1007
|
+
}
|
|
1008
|
+
if (mode === "plan_driven" && acceptance.length === 0) {
|
|
1009
|
+
stateCaveats.push("Selected Realisera task has no acceptance criteria in CLI plan state.");
|
|
1010
|
+
fallbackCommands.push("agentera plan --format json");
|
|
1011
|
+
}
|
|
1012
|
+
if (!progress.exists) {
|
|
1013
|
+
stateCaveats.push("progress state is unavailable; progress logging context is incomplete.");
|
|
1014
|
+
fallbackCommands.push("agentera progress --format json");
|
|
1015
|
+
}
|
|
1016
|
+
if (!health.exists) {
|
|
1017
|
+
stateCaveats.push("health state is unavailable or incomplete.");
|
|
1018
|
+
fallbackCommands.push("agentera health --format json");
|
|
1019
|
+
}
|
|
1020
|
+
if (!docs.exists) {
|
|
1021
|
+
stateCaveats.push("docs mapping state is unavailable or incomplete.");
|
|
1022
|
+
fallbackCommands.push("agentera docs --format json");
|
|
1023
|
+
}
|
|
1024
|
+
if (todoItems.length === 0) {
|
|
1025
|
+
stateCaveats.push("todo state has no open entries in prime --context response; absence may mean none open or unavailable.");
|
|
1026
|
+
fallbackCommands.push("agentera todo --format json");
|
|
1027
|
+
}
|
|
1028
|
+
if (changelogBoundary.status !== "available") {
|
|
1029
|
+
stateCaveats.push(...(changelogBoundary.caveats ?? []));
|
|
1030
|
+
fallbackCommands.push("agentera query changelog --format json");
|
|
1031
|
+
}
|
|
1032
|
+
if (profile.status !== "loaded") {
|
|
1033
|
+
stateCaveats.push("profile-derived state is unavailable in prime --context response.");
|
|
1034
|
+
}
|
|
1035
|
+
else if (profile.stale === true) {
|
|
1036
|
+
stateCaveats.push("profile-derived state is stale; this is a caveat, not approval to refresh profile state.");
|
|
1037
|
+
}
|
|
1038
|
+
if (bundle.status !== "up_to_date") {
|
|
1039
|
+
stateCaveats.push("Agentera app files are not up to date; this is a caveat, not approval to repair or update app files.");
|
|
1040
|
+
}
|
|
1041
|
+
const scopeBoundary = realiseraScopeBoundary(plan, selected);
|
|
1042
|
+
if (scopeBoundary.source_scope.status === "unspecified") {
|
|
1043
|
+
stateCaveats.push("source-file scope is unspecified; no allowed or prohibited source paths were inferred.");
|
|
1044
|
+
}
|
|
1045
|
+
fallbackCommands = uniqueList(fallbackCommands);
|
|
1046
|
+
stateCaveats = uniqueList(stateCaveats);
|
|
1047
|
+
const requiredState = {
|
|
1048
|
+
work_selection: mode === "plan_driven" || mode === "completed_plan_sweep",
|
|
1049
|
+
acceptance_criteria: mode === "completed_plan_sweep" || acceptance.length > 0,
|
|
1050
|
+
artifact_update_requirements: Boolean(docs.exists),
|
|
1051
|
+
progress_logging_requirements: progressVerification.status === "available" || (progressVerification.caveats ?? []).length > 0,
|
|
1052
|
+
changelog_boundary: changelogBoundary.status === "available",
|
|
1053
|
+
scope_boundary: true,
|
|
1054
|
+
safety_boundaries: true,
|
|
1055
|
+
};
|
|
1056
|
+
const missingRequired = Object.entries(requiredState).filter(([, present]) => !present).map(([name]) => name);
|
|
1057
|
+
const caveated = stateCaveats.length > 0;
|
|
1058
|
+
const complete = (mode === "plan_driven" || mode === "completed_plan_sweep") && missingRequired.length === 0;
|
|
1059
|
+
return {
|
|
1060
|
+
capability: "realisera",
|
|
1061
|
+
mode,
|
|
1062
|
+
work_selection: {
|
|
1063
|
+
status: target.status,
|
|
1064
|
+
selection_reason: target.selection_reason,
|
|
1065
|
+
task: selected && typeof selected === "object" ? taskRef(selected) : null,
|
|
1066
|
+
source_provenance: target.source_provenance,
|
|
1067
|
+
caveats: target.caveats ?? [],
|
|
1068
|
+
},
|
|
1069
|
+
plan_task: selected && typeof selected === "object" ? orchestrationTaskSummary(selected) : null,
|
|
1070
|
+
acceptance_criteria: {
|
|
1071
|
+
status: acceptance.length > 0 ? "available" : "incomplete",
|
|
1072
|
+
items: acceptance,
|
|
1073
|
+
count: acceptance.length,
|
|
1074
|
+
source_provenance: sourceProvenance("plan", "agentera plan --format json", "entries.acceptance"),
|
|
1075
|
+
},
|
|
1076
|
+
constraints: {
|
|
1077
|
+
plan_constraints_present: hasRecordedValue(planContextField(plan, "constraints")),
|
|
1078
|
+
plan_constraints_summary: "Plan constraints are represented here as structured safety and fallback policy; " +
|
|
1079
|
+
"run the plan CLI fallback only if full wording is needed.",
|
|
1080
|
+
protected_actions: [
|
|
1081
|
+
"no profile refresh",
|
|
1082
|
+
"no installed app refresh",
|
|
1083
|
+
"no vision edit",
|
|
1084
|
+
"no objective-state edit",
|
|
1085
|
+
"no dispatch without explicit cycle execution",
|
|
1086
|
+
"no commit/push/tag/publication without explicit approval",
|
|
1087
|
+
],
|
|
1088
|
+
unsupported_cli_command_policy: "Do not introduce capability-name or slash-alias CLI commands for Realisera.",
|
|
1089
|
+
source_provenance: sourceProvenance("plan", "agentera plan --format json", "summary.constraints"),
|
|
1090
|
+
},
|
|
1091
|
+
scope_boundary: scopeBoundary,
|
|
1092
|
+
verification_expectations: {
|
|
1093
|
+
latest_progress_verification: progressVerification,
|
|
1094
|
+
expected_commands: ["focused pytest targets", "Realisera capability validation", "self-validation", "agentera gate", "compaction check", "git diff --check"],
|
|
1095
|
+
source_provenance: sourceProvenance("plan", "agentera plan --format json", "entries.acceptance"),
|
|
1096
|
+
},
|
|
1097
|
+
artifact_update_requirements: realiseraArtifactUpdateRequirements(plan, docs),
|
|
1098
|
+
progress_logging_requirements: {
|
|
1099
|
+
append_cycle: true,
|
|
1100
|
+
verified_field_mandatory: true,
|
|
1101
|
+
latest_progress_verification_pointer: progressVerification.latest_progress_verification_pointer ?? null,
|
|
1102
|
+
source_provenance: sourceProvenance("progress", "agentera progress --format json"),
|
|
1103
|
+
},
|
|
1104
|
+
changelog_boundary: changelogBoundary,
|
|
1105
|
+
git_boundary: {
|
|
1106
|
+
remote_push_allowed: false,
|
|
1107
|
+
commit_allowed_only_with_explicit_user_request: true,
|
|
1108
|
+
tag_or_publication_allowed: false,
|
|
1109
|
+
source_provenance: sourceProvenance("execution_context", "agentera prime --context realisera --format json", "git_boundary"),
|
|
1110
|
+
},
|
|
1111
|
+
plan_completion_sweep: sweep,
|
|
1112
|
+
state_family_caveats: stateCaveats,
|
|
1113
|
+
fallback_commands: fallbackCommands,
|
|
1114
|
+
source_contract: {
|
|
1115
|
+
complete_for_execution_context: complete,
|
|
1116
|
+
caveated,
|
|
1117
|
+
raw_artifact_reads_required: false,
|
|
1118
|
+
raw_artifact_read_policy: "Use this execution_context and included hej state first. Run listed routine/query CLI fallback commands " +
|
|
1119
|
+
"for missing or incomplete execution state; raw artifact reads are last-resort diagnostics, not normal Realisera startup behavior.",
|
|
1120
|
+
included_state_families: capabilityContract.included_state_families ?? [],
|
|
1121
|
+
missing_state_families: capabilityContract.missing_state_families ?? [],
|
|
1122
|
+
required_execution_state: requiredState,
|
|
1123
|
+
missing_required_execution_state: missingRequired,
|
|
1124
|
+
fallback_commands: fallbackCommands,
|
|
1125
|
+
caveats: stateCaveats,
|
|
1126
|
+
owns: [
|
|
1127
|
+
"selected work item",
|
|
1128
|
+
"task details and acceptance criteria",
|
|
1129
|
+
"constraints and safety boundaries",
|
|
1130
|
+
"verification expectations",
|
|
1131
|
+
"artifact update requirements",
|
|
1132
|
+
"progress logging requirements",
|
|
1133
|
+
"changelog boundary",
|
|
1134
|
+
"scope boundary",
|
|
1135
|
+
"read-only plan completion sweep metadata",
|
|
1136
|
+
"truthful completeness metadata",
|
|
1137
|
+
],
|
|
1138
|
+
deferred: [],
|
|
1139
|
+
},
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
// ── inspektera evidence bespoke context ─────────────────────────────
|
|
1143
|
+
function dateFromIsoUtc(s) {
|
|
1144
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(s.trim());
|
|
1145
|
+
if (!m)
|
|
1146
|
+
return null;
|
|
1147
|
+
const utc = Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
|
|
1148
|
+
const back = new Date(utc);
|
|
1149
|
+
if (back.getUTCFullYear() !== Number(m[1]) || back.getUTCMonth() !== Number(m[2]) - 1 || back.getUTCDate() !== Number(m[3]))
|
|
1150
|
+
return null;
|
|
1151
|
+
return utc;
|
|
1152
|
+
}
|
|
1153
|
+
function todayUtcMs() {
|
|
1154
|
+
const now = new Date();
|
|
1155
|
+
return Date.UTC(now.getFullYear(), now.getMonth(), now.getDate());
|
|
1156
|
+
}
|
|
1157
|
+
function currentStateStatus(value, label, staleAfterDays = 30) {
|
|
1158
|
+
if (typeof value !== "string" || !value.trim())
|
|
1159
|
+
return ["unknown", null];
|
|
1160
|
+
const observed = dateFromIsoUtc(value.trim().slice(0, 10));
|
|
1161
|
+
if (observed === null)
|
|
1162
|
+
return ["unknown", `${label} current-state date is not ISO-parseable in CLI state.`];
|
|
1163
|
+
const ageDays = Math.round((todayUtcMs() - observed) / 86400000);
|
|
1164
|
+
if (ageDays > staleAfterDays)
|
|
1165
|
+
return ["stale", `${label} evidence is stale (${ageDays} days old; threshold=${staleAfterDays}).`];
|
|
1166
|
+
return ["current", null];
|
|
1167
|
+
}
|
|
1168
|
+
function evidenceDocsState(docs) {
|
|
1169
|
+
const available = Boolean(docs.exists);
|
|
1170
|
+
const nonEmptyFields = ["mapping_entries", "indexed_documents", "last_audit"].filter((f) => hasRecordedValue(docs[f]));
|
|
1171
|
+
const [currentState, currentStateCaveat] = currentStateStatus(docs.last_audit, "Docs");
|
|
1172
|
+
return {
|
|
1173
|
+
status: available ? "available" : "unavailable",
|
|
1174
|
+
source_provenance: sourceProvenance("docs", "agentera docs --format json"),
|
|
1175
|
+
mapping_entries: docs.mapping_entries ?? 0,
|
|
1176
|
+
indexed_documents: docs.indexed_documents ?? 0,
|
|
1177
|
+
last_audit: docs.last_audit ?? null,
|
|
1178
|
+
current_state: currentState,
|
|
1179
|
+
non_empty_evidence_present: nonEmptyFields.length > 0,
|
|
1180
|
+
non_empty_evidence_fields: nonEmptyFields,
|
|
1181
|
+
caveats: [
|
|
1182
|
+
...(available ? [] : ["Docs state is unavailable in CLI docs state."]),
|
|
1183
|
+
...(available && currentStateCaveat ? [currentStateCaveat] : []),
|
|
1184
|
+
],
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
function evidenceHealthState(health) {
|
|
1188
|
+
const available = Boolean(health.exists);
|
|
1189
|
+
const merged = { audit_number: health.number, ...health };
|
|
1190
|
+
const nonEmptyFields = ["audit_number", "trajectory", "grade"].filter((f) => hasRecordedValue(merged[f]));
|
|
1191
|
+
const auditDate = health.date ?? health.timestamp ?? null;
|
|
1192
|
+
const [currentState, currentStateCaveat] = currentStateStatus(auditDate, "Health");
|
|
1193
|
+
return {
|
|
1194
|
+
status: available ? "available" : "unavailable",
|
|
1195
|
+
source_provenance: sourceProvenance("health", "agentera health --format json"),
|
|
1196
|
+
audit_number: health.number ?? null,
|
|
1197
|
+
date: auditDate,
|
|
1198
|
+
timestamp: auditDate,
|
|
1199
|
+
trajectory: health.trajectory ?? null,
|
|
1200
|
+
grade: health.grade ?? null,
|
|
1201
|
+
degrading: Boolean(health.degrading),
|
|
1202
|
+
current_state: currentState,
|
|
1203
|
+
non_empty_evidence_present: nonEmptyFields.length > 0,
|
|
1204
|
+
non_empty_evidence_fields: nonEmptyFields,
|
|
1205
|
+
caveats: [
|
|
1206
|
+
...(available ? [] : ["Health state is unavailable in CLI health state."]),
|
|
1207
|
+
...(available && currentStateCaveat ? [currentStateCaveat] : []),
|
|
1208
|
+
],
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
function evidenceTodoState(schemas, todoItems) {
|
|
1212
|
+
const info = schemas.todo ?? { path: "TODO.md", record: undefined, schema: {}, fields: {} };
|
|
1213
|
+
const exists = fs.existsSync(artifactPath(info, "todo"));
|
|
1214
|
+
return {
|
|
1215
|
+
status: exists ? "available" : "unavailable",
|
|
1216
|
+
source_provenance: sourceProvenance("todo", "agentera todo --format json"),
|
|
1217
|
+
open_count: todoItems.length,
|
|
1218
|
+
items: todoItems,
|
|
1219
|
+
non_empty_evidence_present: todoItems.length > 0,
|
|
1220
|
+
non_empty_evidence_fields: todoItems.length > 0 ? ["items"] : [],
|
|
1221
|
+
caveats: exists ? [] : ["TODO state is unavailable in CLI TODO state."],
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
1224
|
+
function evidenceProtectedStateChecks() {
|
|
1225
|
+
const source = sourceProvenance("evidence_context", "agentera prime --context inspektera --format json", "protected_state_checks");
|
|
1226
|
+
return {
|
|
1227
|
+
status: "not_checked_by_design",
|
|
1228
|
+
allowed_status_values: ["verified_local", "not_checked_by_design", "requires_manual_check", "unavailable"],
|
|
1229
|
+
source_provenance: source,
|
|
1230
|
+
checks: [
|
|
1231
|
+
{
|
|
1232
|
+
name: "vision_state",
|
|
1233
|
+
status: "not_checked_by_design",
|
|
1234
|
+
protected: true,
|
|
1235
|
+
checked: false,
|
|
1236
|
+
source_provenance: source,
|
|
1237
|
+
caveats: ["Vision state is protected during execution cycles and was not read or modified."],
|
|
1238
|
+
},
|
|
1239
|
+
{
|
|
1240
|
+
name: "objective_state",
|
|
1241
|
+
status: "not_checked_by_design",
|
|
1242
|
+
protected: true,
|
|
1243
|
+
checked: false,
|
|
1244
|
+
source_provenance: source,
|
|
1245
|
+
caveats: ["Objective state is protected during execution cycles and was not read or modified."],
|
|
1246
|
+
},
|
|
1247
|
+
],
|
|
1248
|
+
caveats: ["Protected-state boundaries are reported without reading or modifying vision or objective state."],
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
function evidenceVersionChecks(docs) {
|
|
1252
|
+
const conventions = docsConventions(docs);
|
|
1253
|
+
const versionFiles = asList(conventions.version_files);
|
|
1254
|
+
const semverPolicy = conventions.semver_policy && typeof conventions.semver_policy === "object" && !Array.isArray(conventions.semver_policy) ? conventions.semver_policy : {};
|
|
1255
|
+
const source = sourceProvenance("docs", "agentera docs --format json", "summary.conventions");
|
|
1256
|
+
const ec = (field) => sourceProvenance("evidence_context", "agentera prime --context inspektera --format json", field);
|
|
1257
|
+
const checks = [
|
|
1258
|
+
{
|
|
1259
|
+
name: "docs_version_policy",
|
|
1260
|
+
status: Object.keys(semverPolicy).length > 0 ? "verified_local" : "unavailable",
|
|
1261
|
+
source_provenance: source,
|
|
1262
|
+
evidence: { semver_policy: semverPolicy },
|
|
1263
|
+
caveats: Object.keys(semverPolicy).length > 0 ? [] : ["Docs semver policy is unavailable in CLI docs state."],
|
|
1264
|
+
},
|
|
1265
|
+
{
|
|
1266
|
+
name: "version_files",
|
|
1267
|
+
status: versionFiles.length > 0 ? "verified_local" : "unavailable",
|
|
1268
|
+
source_provenance: source,
|
|
1269
|
+
evidence: { version_files: versionFiles },
|
|
1270
|
+
caveats: versionFiles.length > 0 ? [] : ["Docs version files are unavailable in CLI docs state."],
|
|
1271
|
+
},
|
|
1272
|
+
{
|
|
1273
|
+
name: "publication_evidence",
|
|
1274
|
+
status: "requires_manual_check",
|
|
1275
|
+
source_provenance: ec("version_checks.publication_evidence"),
|
|
1276
|
+
remote_checks_performed: false,
|
|
1277
|
+
registry_checks_performed: false,
|
|
1278
|
+
caveats: ["Publication and registry evidence is not recorded in CLI evidence context and requires manual verification if needed."],
|
|
1279
|
+
},
|
|
1280
|
+
{
|
|
1281
|
+
name: "remote_push_evidence",
|
|
1282
|
+
status: "requires_manual_check",
|
|
1283
|
+
source_provenance: ec("version_checks.remote_push_evidence"),
|
|
1284
|
+
remote_checks_performed: false,
|
|
1285
|
+
caveats: ["Remote push evidence is not recorded in CLI evidence context and requires manual verification if needed."],
|
|
1286
|
+
},
|
|
1287
|
+
{
|
|
1288
|
+
name: "installed_app_refresh",
|
|
1289
|
+
status: "not_checked_by_design",
|
|
1290
|
+
source_provenance: ec("version_checks.installed_app_refresh"),
|
|
1291
|
+
refresh_performed: false,
|
|
1292
|
+
caveats: ["Installed app refresh state is deliberately not checked or changed by evidence context."],
|
|
1293
|
+
},
|
|
1294
|
+
];
|
|
1295
|
+
let status;
|
|
1296
|
+
if (checks.some((c) => c.status === "requires_manual_check"))
|
|
1297
|
+
status = "requires_manual_check";
|
|
1298
|
+
else if (checks.some((c) => c.status === "unavailable"))
|
|
1299
|
+
status = "unavailable";
|
|
1300
|
+
else
|
|
1301
|
+
status = "verified_local";
|
|
1302
|
+
return {
|
|
1303
|
+
status,
|
|
1304
|
+
allowed_status_values: ["verified_local", "not_checked_by_design", "requires_manual_check", "unavailable"],
|
|
1305
|
+
source_provenance: source,
|
|
1306
|
+
checks,
|
|
1307
|
+
caveats: checks.flatMap((c) => (c.caveats ?? [])),
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
function evidencePlanCriteria(plan, target) {
|
|
1311
|
+
const taskRefObj = target.task && typeof target.task === "object" && !Array.isArray(target.task) ? target.task : null;
|
|
1312
|
+
let selected = null;
|
|
1313
|
+
if (taskRefObj) {
|
|
1314
|
+
const tasks = asList(plan.tasks).filter((t) => t && typeof t === "object" && !Array.isArray(t));
|
|
1315
|
+
selected = tasks.find((t) => t.number === taskRefObj.number) ?? null;
|
|
1316
|
+
}
|
|
1317
|
+
const criteria = selected && typeof selected === "object" ? asList(selected.acceptance) : [];
|
|
1318
|
+
return {
|
|
1319
|
+
status: criteria.length > 0 ? "available" : "incomplete",
|
|
1320
|
+
source_provenance: sourceProvenance("plan", "agentera plan --format json", "entries.acceptance"),
|
|
1321
|
+
target: taskRefObj,
|
|
1322
|
+
criteria,
|
|
1323
|
+
criteria_count: criteria.length,
|
|
1324
|
+
non_empty_evidence_present: criteria.length > 0,
|
|
1325
|
+
non_empty_evidence_fields: criteria.length > 0 ? ["criteria"] : [],
|
|
1326
|
+
caveats: criteria.length > 0 ? [] : ["Selected evaluation target has no acceptance criteria in CLI plan state."],
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
function residualRiskEntry(category, status, message, sp) {
|
|
1330
|
+
return { category, status, message, source_provenance: sp };
|
|
1331
|
+
}
|
|
1332
|
+
function decisionContextRisk(schemas) {
|
|
1333
|
+
const info = schemas.decisions ?? { path: ".agentera/decisions.yaml", record: undefined, schema: {}, fields: {} };
|
|
1334
|
+
const p = artifactPath(info, "decisions");
|
|
1335
|
+
const source = sourceMetadata("decisions", p);
|
|
1336
|
+
const data = loadNamedArtifact(schemas, "decisions");
|
|
1337
|
+
const entries = extractDecisionEntries(data).map((e) => decisionContextEntry(e));
|
|
1338
|
+
if (!source.exists) {
|
|
1339
|
+
return {
|
|
1340
|
+
status: "unavailable",
|
|
1341
|
+
source_provenance: sourceProvenance("decisions", "agentera decisions --format json"),
|
|
1342
|
+
summary: null,
|
|
1343
|
+
caveats: ["Decision state is unavailable in CLI evidence context."],
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
const contract = decisionSourceContract(source, entries, {});
|
|
1347
|
+
const compacted = contract.completeness.compacted_entries;
|
|
1348
|
+
const missing = contract.completeness.entries_with_missing_fields;
|
|
1349
|
+
const caveats = compacted || missing ? (contract.caveats ?? []) : [];
|
|
1350
|
+
return {
|
|
1351
|
+
status: caveats.length > 0 ? "caveated" : "available",
|
|
1352
|
+
source_provenance: sourceProvenance("decisions", "agentera decisions --format json"),
|
|
1353
|
+
summary: contract.completeness,
|
|
1354
|
+
caveats,
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
function parseDecisionReviewDate(value) {
|
|
1358
|
+
if (typeof value !== "string" || !value.trim())
|
|
1359
|
+
return null;
|
|
1360
|
+
return dateFromIsoUtc(value.trim().slice(0, 10));
|
|
1361
|
+
}
|
|
1362
|
+
function decisionReviewDue(entry) {
|
|
1363
|
+
const satisfaction = entry.satisfaction && typeof entry.satisfaction === "object" && !Array.isArray(entry.satisfaction) ? entry.satisfaction : {};
|
|
1364
|
+
const candidates = [
|
|
1365
|
+
["review_date", entry.review_date],
|
|
1366
|
+
["review_by", entry.review_by],
|
|
1367
|
+
["satisfaction.review_date", satisfaction.review_date],
|
|
1368
|
+
["satisfaction.review_by", satisfaction.review_by],
|
|
1369
|
+
["satisfaction.review_due", satisfaction.review_due],
|
|
1370
|
+
];
|
|
1371
|
+
for (const [field, value] of candidates) {
|
|
1372
|
+
const parsed = parseDecisionReviewDate(value);
|
|
1373
|
+
if (parsed !== null)
|
|
1374
|
+
return [field, parsed];
|
|
1375
|
+
}
|
|
1376
|
+
return [null, null];
|
|
1377
|
+
}
|
|
1378
|
+
function decisionLabel(entry) {
|
|
1379
|
+
const number = entry.number;
|
|
1380
|
+
if (number !== null && number !== undefined && number !== "")
|
|
1381
|
+
return `Decision ${number}`;
|
|
1382
|
+
const summary = entry.summary;
|
|
1383
|
+
if (typeof summary === "string" && summary.trim())
|
|
1384
|
+
return summary.trim().split(":", 1)[0];
|
|
1385
|
+
return "Decision entry";
|
|
1386
|
+
}
|
|
1387
|
+
function isoFromUtcMs(utc) {
|
|
1388
|
+
const d = new Date(utc);
|
|
1389
|
+
return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}-${String(d.getUTCDate()).padStart(2, "0")}`;
|
|
1390
|
+
}
|
|
1391
|
+
function decisionReviewPressure(schemas) {
|
|
1392
|
+
const info = schemas.decisions ?? { path: ".agentera/decisions.yaml", record: undefined, schema: {}, fields: {} };
|
|
1393
|
+
const p = artifactPath(info, "decisions");
|
|
1394
|
+
const source = sourceMetadata("decisions", p);
|
|
1395
|
+
const sp = sourceProvenance("decisions", "agentera decisions --format json");
|
|
1396
|
+
if (!source.exists) {
|
|
1397
|
+
return { status: "unavailable", source_provenance: sp, summary: null, stale_protected_decisions: [], caveats: [] };
|
|
1398
|
+
}
|
|
1399
|
+
const data = loadNamedArtifact(schemas, "decisions");
|
|
1400
|
+
const dd = data && typeof data === "object" && !Array.isArray(data) ? data : {};
|
|
1401
|
+
const active = Array.isArray(dd.decisions) ? dd.decisions : [];
|
|
1402
|
+
const archive = Array.isArray(dd.archive) ? dd.archive : [];
|
|
1403
|
+
const activeEntries = active.filter((e) => e && typeof e === "object" && !Array.isArray(e)).map((e) => decisionContextEntry(e));
|
|
1404
|
+
const archiveEntries = archive.filter((e) => e && typeof e === "object" && !Array.isArray(e)).map((e) => decisionContextEntry(e));
|
|
1405
|
+
const protectedActive = activeEntries.filter((e) => e.satisfaction && typeof e.satisfaction === "object" && e.satisfaction.review_needed);
|
|
1406
|
+
const protectedArchive = archiveEntries.filter((e) => e.satisfaction && typeof e.satisfaction === "object" && e.satisfaction.review_needed);
|
|
1407
|
+
const today = todayUtcMs();
|
|
1408
|
+
const stale = [];
|
|
1409
|
+
let caveats = [];
|
|
1410
|
+
for (const [collection, entries] of [["decisions", protectedActive], ["archive", protectedArchive]]) {
|
|
1411
|
+
for (const entry of entries) {
|
|
1412
|
+
const [field, reviewDate] = decisionReviewDue(entry);
|
|
1413
|
+
if (reviewDate === null || reviewDate > today)
|
|
1414
|
+
continue;
|
|
1415
|
+
const label = decisionLabel(entry);
|
|
1416
|
+
const message = `${label} requires protected decision review because ${field} elapsed on ${isoFromUtcMs(reviewDate)}.`;
|
|
1417
|
+
stale.push({ label, collection, reason: "review_date_elapsed", review_date: isoFromUtcMs(reviewDate), source_field: field, message });
|
|
1418
|
+
caveats.push(message);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
const protectedOverflowCount = Math.max(protectedActive.length - 10, protectedArchive.length - 40, protectedActive.length + protectedArchive.length - 50, 0);
|
|
1422
|
+
if (protectedOverflowCount) {
|
|
1423
|
+
const message = "Protected decisions exceed the 10/40/50 compaction budget; " +
|
|
1424
|
+
`${protectedOverflowCount} protected decision(s) require review before compaction can complete.`;
|
|
1425
|
+
stale.push({ label: "DECISIONS.md", collection: "decisions/archive", reason: "protected_compaction_budget_pressure", protected_overflow_count: protectedOverflowCount, message });
|
|
1426
|
+
caveats.push(message);
|
|
1427
|
+
}
|
|
1428
|
+
caveats = uniqueList(caveats);
|
|
1429
|
+
return {
|
|
1430
|
+
status: caveats.length > 0 ? "review_required" : "available",
|
|
1431
|
+
source_provenance: sp,
|
|
1432
|
+
summary: {
|
|
1433
|
+
protected_active_decisions: protectedActive.length,
|
|
1434
|
+
protected_archive_decisions: protectedArchive.length,
|
|
1435
|
+
protected_overflow_count: protectedOverflowCount,
|
|
1436
|
+
stale_protected_decisions: stale.length,
|
|
1437
|
+
},
|
|
1438
|
+
stale_protected_decisions: stale,
|
|
1439
|
+
caveats,
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
function inspekteraEvidenceContext(capability, schemas, plan, progress, health, todoItems, docs, profile, bundle) {
|
|
1443
|
+
if (capability !== "inspektera")
|
|
1444
|
+
return null;
|
|
1445
|
+
const capabilityContract = capabilityContext(capability) ?? {};
|
|
1446
|
+
const evaluationTarget = selectEvidenceTarget(plan);
|
|
1447
|
+
const planCriteria = evidencePlanCriteria(plan, evaluationTarget);
|
|
1448
|
+
const progressVerification = progressVerificationSummary(progress);
|
|
1449
|
+
const docsState = evidenceDocsState(docs);
|
|
1450
|
+
const healthState = evidenceHealthState(health);
|
|
1451
|
+
const todoState = evidenceTodoState(schemas, todoItems);
|
|
1452
|
+
const protectedStateChecks = evidenceProtectedStateChecks();
|
|
1453
|
+
const versionChecks = evidenceVersionChecks(docs);
|
|
1454
|
+
const decisionRisk = decisionContextRisk(schemas);
|
|
1455
|
+
const reviewPressure = decisionReviewPressure(schemas);
|
|
1456
|
+
let stateCaveats = [];
|
|
1457
|
+
const attributedRisks = [];
|
|
1458
|
+
for (const family of (capabilityContract.missing_state_families ?? [])) {
|
|
1459
|
+
const message = `${family} state is not included in prime --context startup context.`;
|
|
1460
|
+
stateCaveats.push(message);
|
|
1461
|
+
attributedRisks.push(residualRiskEntry("missing_state_family", "caveated", message, sourceProvenance("prime", "agentera prime --context inspektera --format json", "source_contract.capability_context.missing_state_families")));
|
|
1462
|
+
}
|
|
1463
|
+
if (bundle.status !== "up_to_date") {
|
|
1464
|
+
const message = "Agentera app files are not up to date; this is a caveat, not approval to repair or update app files.";
|
|
1465
|
+
stateCaveats.push(message);
|
|
1466
|
+
attributedRisks.push(residualRiskEntry("installed_app_state", "caveated", message, sourceProvenance("hej", "agentera hej --format json", "bundle.status")));
|
|
1467
|
+
}
|
|
1468
|
+
if (profile.status !== "loaded") {
|
|
1469
|
+
const message = "profile-derived state is unavailable in prime --context response.";
|
|
1470
|
+
stateCaveats.push(message);
|
|
1471
|
+
attributedRisks.push(residualRiskEntry("profile_state", "unavailable", message, sourceProvenance("hej", "agentera hej --format json", "profile.status")));
|
|
1472
|
+
}
|
|
1473
|
+
else if (profile.stale === true) {
|
|
1474
|
+
const message = "profile-derived state is stale; this is a caveat, not approval to refresh profile state.";
|
|
1475
|
+
stateCaveats.push(message);
|
|
1476
|
+
attributedRisks.push(residualRiskEntry("profile_state", "caveated", message, sourceProvenance("hej", "agentera hej --format json", "profile.stale")));
|
|
1477
|
+
}
|
|
1478
|
+
for (const component of [evaluationTarget, planCriteria, progressVerification, docsState, healthState, todoState, protectedStateChecks, versionChecks]) {
|
|
1479
|
+
for (const caveat of (component.caveats ?? [])) {
|
|
1480
|
+
stateCaveats.push(caveat);
|
|
1481
|
+
attributedRisks.push(residualRiskEntry("evidence_family", "caveated", caveat, component.source_provenance ?? sourceProvenance("evidence_context", "agentera prime --context inspektera --format json")));
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
for (const caveat of (decisionRisk.caveats ?? [])) {
|
|
1485
|
+
stateCaveats.push(caveat);
|
|
1486
|
+
attributedRisks.push(residualRiskEntry("decisions_context", decisionRisk.status, caveat, decisionRisk.source_provenance));
|
|
1487
|
+
}
|
|
1488
|
+
for (const caveat of (reviewPressure.caveats ?? [])) {
|
|
1489
|
+
stateCaveats.push(caveat);
|
|
1490
|
+
attributedRisks.push(residualRiskEntry("decision_review_pressure", reviewPressure.status, caveat, reviewPressure.source_provenance));
|
|
1491
|
+
}
|
|
1492
|
+
const retry = retryState();
|
|
1493
|
+
for (const caveat of (retry.caveats ?? [])) {
|
|
1494
|
+
stateCaveats.push(caveat);
|
|
1495
|
+
attributedRisks.push(residualRiskEntry("retry_state", retry.status, caveat, retry.source_provenance));
|
|
1496
|
+
}
|
|
1497
|
+
stateCaveats = uniqueList(stateCaveats);
|
|
1498
|
+
const dedupedRisks = [];
|
|
1499
|
+
const seen = new Set();
|
|
1500
|
+
for (const risk of attributedRisks) {
|
|
1501
|
+
const key = `${risk.category}\u0000${risk.message}`;
|
|
1502
|
+
if (!seen.has(key)) {
|
|
1503
|
+
seen.add(key);
|
|
1504
|
+
dedupedRisks.push(risk);
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
const requiredState = {
|
|
1508
|
+
evaluation_target: evaluationTarget.status === "selected",
|
|
1509
|
+
plan_criteria: planCriteria.status === "available",
|
|
1510
|
+
progress_verification: progressVerification.status === "available",
|
|
1511
|
+
docs_state: docsState.status === "available",
|
|
1512
|
+
health_state: healthState.status === "available",
|
|
1513
|
+
todo_state: todoState.status === "available",
|
|
1514
|
+
source_contract: true,
|
|
1515
|
+
};
|
|
1516
|
+
const missingRequired = Object.entries(requiredState).filter(([, present]) => !present).map(([name]) => name);
|
|
1517
|
+
const fallbackCommands = uniqueList([
|
|
1518
|
+
"agentera plan --format json",
|
|
1519
|
+
"agentera progress --format json",
|
|
1520
|
+
"agentera docs --format json",
|
|
1521
|
+
"agentera health --format json",
|
|
1522
|
+
"agentera todo --format json",
|
|
1523
|
+
"agentera query --list-artifacts --format json",
|
|
1524
|
+
...(capabilityContract.cli_fallback ?? []),
|
|
1525
|
+
]);
|
|
1526
|
+
return {
|
|
1527
|
+
capability: "inspektera",
|
|
1528
|
+
evaluation_target: evaluationTarget,
|
|
1529
|
+
plan_criteria: planCriteria,
|
|
1530
|
+
progress_verification: progressVerification,
|
|
1531
|
+
docs_state: docsState,
|
|
1532
|
+
health_state: healthState,
|
|
1533
|
+
todo_state: todoState,
|
|
1534
|
+
protected_state_checks: protectedStateChecks,
|
|
1535
|
+
version_checks: versionChecks,
|
|
1536
|
+
decision_context: decisionRisk,
|
|
1537
|
+
decision_review_pressure: reviewPressure,
|
|
1538
|
+
residual_risks: {
|
|
1539
|
+
status: dedupedRisks.length > 0 ? "caveated" : "none_recorded",
|
|
1540
|
+
items: stateCaveats,
|
|
1541
|
+
attributed_items: dedupedRisks,
|
|
1542
|
+
caveats: [],
|
|
1543
|
+
},
|
|
1544
|
+
state_family_caveats: stateCaveats,
|
|
1545
|
+
fallback_commands: fallbackCommands,
|
|
1546
|
+
source_contract: {
|
|
1547
|
+
complete_for_evidence_context: missingRequired.length === 0,
|
|
1548
|
+
caveated: stateCaveats.length > 0,
|
|
1549
|
+
raw_artifact_reads_required: false,
|
|
1550
|
+
raw_artifact_read_policy: "Use this evidence_context and included hej state first. Run listed routine/query CLI fallback commands " +
|
|
1551
|
+
"for missing or incomplete evidence state; raw artifact reads are last-resort diagnostics, not normal evaluation startup behavior.",
|
|
1552
|
+
included_state_families: capabilityContract.included_state_families ?? [],
|
|
1553
|
+
missing_state_families: capabilityContract.missing_state_families ?? [],
|
|
1554
|
+
required_evidence_state: requiredState,
|
|
1555
|
+
missing_required_evidence_state: missingRequired,
|
|
1556
|
+
fallback_commands: fallbackCommands,
|
|
1557
|
+
caveats: stateCaveats,
|
|
1558
|
+
owns: [
|
|
1559
|
+
"evaluation target",
|
|
1560
|
+
"plan criteria",
|
|
1561
|
+
"progress verification",
|
|
1562
|
+
"docs state",
|
|
1563
|
+
"health state",
|
|
1564
|
+
"TODO state",
|
|
1565
|
+
"protected-state placeholder checks",
|
|
1566
|
+
"version boundary checks",
|
|
1567
|
+
"compacted decision caveats",
|
|
1568
|
+
"protected decision review pressure",
|
|
1569
|
+
"attributed residual risks",
|
|
1570
|
+
"fallback commands",
|
|
1571
|
+
"raw-read policy",
|
|
1572
|
+
"truthful completeness metadata",
|
|
1573
|
+
],
|
|
1574
|
+
deferred: [],
|
|
1575
|
+
},
|
|
1576
|
+
};
|
|
1577
|
+
}
|
|
1578
|
+
// ── slim transforms for evidence ────────────────────────────────────
|
|
1579
|
+
function truncateContextText(value, maxChars = 240) {
|
|
1580
|
+
if (typeof value !== "string" || value.length <= maxChars)
|
|
1581
|
+
return value;
|
|
1582
|
+
return value.slice(0, maxChars - 1).replace(/\s+$/, "") + "\u2026";
|
|
1583
|
+
}
|
|
1584
|
+
function compactItemsState(value, maxItems = 3, maxChars = 180) {
|
|
1585
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
1586
|
+
return value;
|
|
1587
|
+
const compact = {};
|
|
1588
|
+
for (const [key, item] of Object.entries(value)) {
|
|
1589
|
+
if (!["items", "attributed_items", "summary"].includes(key))
|
|
1590
|
+
compact[key] = item;
|
|
1591
|
+
}
|
|
1592
|
+
const items = value.items;
|
|
1593
|
+
if (Array.isArray(items)) {
|
|
1594
|
+
compact.item_count = items.length;
|
|
1595
|
+
compact.items = items.slice(0, maxItems).map((item) => item && typeof item === "object" && !Array.isArray(item)
|
|
1596
|
+
? Object.fromEntries(Object.entries(item).map(([k, v]) => [k, truncateContextText(v, maxChars)]))
|
|
1597
|
+
: truncateContextText(item, maxChars));
|
|
1598
|
+
compact.truncated_item_count = Math.max(items.length - maxItems, 0);
|
|
1599
|
+
}
|
|
1600
|
+
const attributed = value.attributed_items;
|
|
1601
|
+
if (Array.isArray(attributed))
|
|
1602
|
+
compact.attributed_item_count = attributed.length;
|
|
1603
|
+
if (typeof value.summary === "string") {
|
|
1604
|
+
compact.summary_present = true;
|
|
1605
|
+
compact.summary_excerpt = truncateContextText(value.summary, maxChars);
|
|
1606
|
+
}
|
|
1607
|
+
return compact;
|
|
1608
|
+
}
|
|
1609
|
+
function compactVersionChecks(value) {
|
|
1610
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
1611
|
+
return value;
|
|
1612
|
+
const compact = {};
|
|
1613
|
+
for (const key of ["status", "allowed_status_values", "source_provenance", "caveats"]) {
|
|
1614
|
+
if (key in value)
|
|
1615
|
+
compact[key] = value[key];
|
|
1616
|
+
}
|
|
1617
|
+
const checks = value.checks;
|
|
1618
|
+
if (Array.isArray(checks)) {
|
|
1619
|
+
compact.checks = checks.map((check) => {
|
|
1620
|
+
const out = {};
|
|
1621
|
+
for (const key of ["name", "status", "refresh_performed", "remote_checks_performed", "registry_checks_performed"]) {
|
|
1622
|
+
if (check && typeof check === "object" && key in check)
|
|
1623
|
+
out[key] = check[key];
|
|
1624
|
+
}
|
|
1625
|
+
return out;
|
|
1626
|
+
});
|
|
1627
|
+
}
|
|
1628
|
+
return compact;
|
|
1629
|
+
}
|
|
1630
|
+
function slimEvidenceContext(value) {
|
|
1631
|
+
const compact = { ...value };
|
|
1632
|
+
compact.residual_risks = compactItemsState(value.residual_risks, 15, 180);
|
|
1633
|
+
compact.todo_state = compactItemsState(value.todo_state, 3, 180);
|
|
1634
|
+
compact.progress_verification = compactProgressVerification(value.progress_verification);
|
|
1635
|
+
compact.version_checks = compactVersionChecks(value.version_checks);
|
|
1636
|
+
return compact;
|
|
1637
|
+
}
|
|
1638
|
+
// ── optimera benchmark bespoke context ──────────────────────────────
|
|
1639
|
+
import os from "node:os";
|
|
1640
|
+
const BENCHMARK_CONTEXT_CMD = "agentera prime --context optimera --format json";
|
|
1641
|
+
const BENCHMARK_LATEST_REPORT_LABEL = "startup_benchmark_latest_report";
|
|
1642
|
+
const BENCHMARK_HISTORY_LABEL = "startup_benchmark_history";
|
|
1643
|
+
const BENCHMARK_CONTEXT_SOURCE_LABELS = [BENCHMARK_LATEST_REPORT_LABEL, BENCHMARK_HISTORY_LABEL];
|
|
1644
|
+
const BENCHMARK_TOKEN_NULL_REASONS = [
|
|
1645
|
+
"previous_row_missing", "previous_missing_token_estimates", "estimator_version_mismatch",
|
|
1646
|
+
"runtime_scope_mismatch", "benchmark_mode_mismatch", "contract_version_mismatch",
|
|
1647
|
+
];
|
|
1648
|
+
const BENCHMARK_RECOMMENDATION_ACTIONS = new Set([
|
|
1649
|
+
"plan_cli_startup_envelope", "targeted_capability_guidance_fixes", "close_without_implementation",
|
|
1650
|
+
]);
|
|
1651
|
+
const BENCHMARK_CAVEATED_RUNTIME_STATUSES = new Set(["degraded", "missing", "skipped", "locked", "unreadable"]);
|
|
1652
|
+
const BENCHMARK_FORBIDDEN_OUTPUTS = [
|
|
1653
|
+
"raw_transcripts", "raw_corpus_files", "raw_intermediates", "raw_runtime_store_paths", "raw_session_ids",
|
|
1654
|
+
"private_salts", "generated_salted_hashes", "raw_benchmark_report_bodies", "full_local_benchmark_paths",
|
|
1655
|
+
];
|
|
1656
|
+
const BENCHMARK_SAFE_LABEL_RE = /^[A-Za-z0-9][A-Za-z0-9 .:_-]{0,79}$/;
|
|
1657
|
+
const BENCHMARK_SAFE_SCALAR_RE = /^[A-Za-z0-9][A-Za-z0-9 .:_+@-]{0,119}$/;
|
|
1658
|
+
const HEX16_RE = /^[0-9a-fA-F]{16,}$/;
|
|
1659
|
+
function agenteraDataHome(env = process.env) {
|
|
1660
|
+
const override = env.AGENTERA_HOME;
|
|
1661
|
+
if (override)
|
|
1662
|
+
return override.startsWith("~") ? path.join(os.homedir(), override.slice(1)) : override;
|
|
1663
|
+
if (process.platform === "darwin")
|
|
1664
|
+
return path.join(os.homedir(), "Library", "Application Support", "agentera");
|
|
1665
|
+
if (process.platform === "win32")
|
|
1666
|
+
return path.join(env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming"), "agentera");
|
|
1667
|
+
return path.join(env.XDG_DATA_HOME ?? path.join(os.homedir(), ".local", "share"), "agentera");
|
|
1668
|
+
}
|
|
1669
|
+
function startupBenchmarkDir() {
|
|
1670
|
+
return path.join(agenteraDataHome(), "benchmarks", "startup-state");
|
|
1671
|
+
}
|
|
1672
|
+
function safeBenchmarkNumber(value) {
|
|
1673
|
+
if (typeof value === "boolean")
|
|
1674
|
+
return null;
|
|
1675
|
+
if (typeof value === "number")
|
|
1676
|
+
return value;
|
|
1677
|
+
return null;
|
|
1678
|
+
}
|
|
1679
|
+
function safeBenchmarkLabel(value) {
|
|
1680
|
+
if (typeof value !== "string")
|
|
1681
|
+
return null;
|
|
1682
|
+
const label = value.trim();
|
|
1683
|
+
if (!label || label.includes("/") || label.includes("\\"))
|
|
1684
|
+
return null;
|
|
1685
|
+
if (HEX16_RE.test(label))
|
|
1686
|
+
return null;
|
|
1687
|
+
if (!BENCHMARK_SAFE_LABEL_RE.test(label))
|
|
1688
|
+
return null;
|
|
1689
|
+
return label;
|
|
1690
|
+
}
|
|
1691
|
+
function safeBenchmarkScalar(value, field, allowed = null) {
|
|
1692
|
+
if (value === null || value === undefined)
|
|
1693
|
+
return [null, []];
|
|
1694
|
+
if (typeof value !== "string")
|
|
1695
|
+
return [null, [`${field} was omitted because it is not a bounded string value.`]];
|
|
1696
|
+
const text = value.trim();
|
|
1697
|
+
if (allowed !== null && !allowed.has(text))
|
|
1698
|
+
return [null, [`${field} was omitted because it is not a supported bounded value.`]];
|
|
1699
|
+
if (!text || text.includes("/") || text.includes("\\") || HEX16_RE.test(text))
|
|
1700
|
+
return [null, [`${field} was omitted at the benchmark privacy boundary.`]];
|
|
1701
|
+
if (!BENCHMARK_SAFE_SCALAR_RE.test(text))
|
|
1702
|
+
return [null, [`${field} was omitted because it is outside the bounded scalar contract.`]];
|
|
1703
|
+
return [text, []];
|
|
1704
|
+
}
|
|
1705
|
+
function safeBenchmarkLabelCounts(value, family) {
|
|
1706
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
1707
|
+
return [{}, []];
|
|
1708
|
+
const counts = {};
|
|
1709
|
+
let dropped = 0;
|
|
1710
|
+
for (const [key, rawCount] of Object.entries(value)) {
|
|
1711
|
+
const label = safeBenchmarkLabel(key);
|
|
1712
|
+
const count = safeBenchmarkNumber(rawCount);
|
|
1713
|
+
if (label === null || count === null) {
|
|
1714
|
+
dropped += 1;
|
|
1715
|
+
continue;
|
|
1716
|
+
}
|
|
1717
|
+
counts[label] = count;
|
|
1718
|
+
}
|
|
1719
|
+
const caveats = [];
|
|
1720
|
+
if (dropped)
|
|
1721
|
+
caveats.push(`${family} omitted ${dropped} unsafe label(s) at the benchmark privacy boundary.`);
|
|
1722
|
+
const sorted = {};
|
|
1723
|
+
for (const k of Object.keys(counts).sort())
|
|
1724
|
+
sorted[k] = counts[k];
|
|
1725
|
+
return [sorted, caveats];
|
|
1726
|
+
}
|
|
1727
|
+
function readBenchmarkJson(p, label) {
|
|
1728
|
+
let text;
|
|
1729
|
+
try {
|
|
1730
|
+
text = fs.readFileSync(p, "utf8");
|
|
1731
|
+
}
|
|
1732
|
+
catch (exc) {
|
|
1733
|
+
if (exc.code === "ENOENT")
|
|
1734
|
+
return ["missing", null, [`${label} is missing from retained startup benchmark evidence.`]];
|
|
1735
|
+
return ["unreadable", null, [`${label} could not be read by the CLI.`]];
|
|
1736
|
+
}
|
|
1737
|
+
if (!text.trim())
|
|
1738
|
+
return ["empty", null, [`${label} is empty.`]];
|
|
1739
|
+
let data;
|
|
1740
|
+
try {
|
|
1741
|
+
data = JSON.parse(text);
|
|
1742
|
+
}
|
|
1743
|
+
catch {
|
|
1744
|
+
return ["malformed", null, [`${label} is malformed JSON.`]];
|
|
1745
|
+
}
|
|
1746
|
+
if (!data || typeof data !== "object" || Array.isArray(data) || Object.keys(data).length === 0) {
|
|
1747
|
+
return ["empty", null, [`${label} did not contain a non-empty JSON object.`]];
|
|
1748
|
+
}
|
|
1749
|
+
return ["available", data, []];
|
|
1750
|
+
}
|
|
1751
|
+
function readBenchmarkHistory(p) {
|
|
1752
|
+
let text;
|
|
1753
|
+
try {
|
|
1754
|
+
text = fs.readFileSync(p, "utf8");
|
|
1755
|
+
}
|
|
1756
|
+
catch (exc) {
|
|
1757
|
+
if (exc.code === "ENOENT")
|
|
1758
|
+
return ["missing", [], [`${BENCHMARK_HISTORY_LABEL} is missing from retained startup benchmark evidence.`]];
|
|
1759
|
+
return ["unreadable", [], [`${BENCHMARK_HISTORY_LABEL} could not be read by the CLI.`]];
|
|
1760
|
+
}
|
|
1761
|
+
const lines = text.split(/\r\n|\r|\n/).filter((line) => line.trim());
|
|
1762
|
+
if (lines.length === 0)
|
|
1763
|
+
return ["empty", [], ["Startup benchmark aggregate history exists but has no rows."]];
|
|
1764
|
+
const rows = [];
|
|
1765
|
+
let malformed = 0;
|
|
1766
|
+
for (const line of lines) {
|
|
1767
|
+
let row;
|
|
1768
|
+
try {
|
|
1769
|
+
row = JSON.parse(line);
|
|
1770
|
+
}
|
|
1771
|
+
catch {
|
|
1772
|
+
malformed += 1;
|
|
1773
|
+
continue;
|
|
1774
|
+
}
|
|
1775
|
+
if (row && typeof row === "object" && !Array.isArray(row))
|
|
1776
|
+
rows.push(row);
|
|
1777
|
+
else
|
|
1778
|
+
malformed += 1;
|
|
1779
|
+
}
|
|
1780
|
+
if (malformed)
|
|
1781
|
+
return ["malformed", rows, [`Startup benchmark aggregate history has ${malformed} malformed row(s).`]];
|
|
1782
|
+
if (rows.length === 0)
|
|
1783
|
+
return ["empty", [], ["Startup benchmark aggregate history has no usable rows."]];
|
|
1784
|
+
return ["available", rows, []];
|
|
1785
|
+
}
|
|
1786
|
+
function latestBenchmarkSummary(status, report, caveats) {
|
|
1787
|
+
const source = sourceProvenance("benchmark_context", BENCHMARK_CONTEXT_CMD, "latest_report");
|
|
1788
|
+
if (status !== "available" || report === null) {
|
|
1789
|
+
return {
|
|
1790
|
+
status, source_label: BENCHMARK_LATEST_REPORT_LABEL, source_provenance: source,
|
|
1791
|
+
non_empty_evidence_present: false, contract_version: null, generated_at: null, benchmark_mode: null,
|
|
1792
|
+
benchmark_window: {}, total_records: null, total_state_sequences: null, caveats,
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
const sc = [...caveats];
|
|
1796
|
+
const scalar = (key) => {
|
|
1797
|
+
const [v, c] = safeBenchmarkScalar(report[key], `latest_report.${key}`);
|
|
1798
|
+
sc.push(...c);
|
|
1799
|
+
return v;
|
|
1800
|
+
};
|
|
1801
|
+
const contractVersion = scalar("contract_version");
|
|
1802
|
+
const generatedAt = scalar("generated_at");
|
|
1803
|
+
const benchmarkMode = scalar("benchmark_mode");
|
|
1804
|
+
const previousWatermark = scalar("benchmark_previous_watermark_at");
|
|
1805
|
+
const windowStarted = scalar("benchmark_window_started_after");
|
|
1806
|
+
const watermarkAt = scalar("benchmark_watermark_at");
|
|
1807
|
+
const totalStateSequences = safeBenchmarkNumber(report.total_state_sequences);
|
|
1808
|
+
if (totalStateSequences === null)
|
|
1809
|
+
sc.push("Latest startup benchmark report is missing total_state_sequences.");
|
|
1810
|
+
return {
|
|
1811
|
+
status: "available", source_label: BENCHMARK_LATEST_REPORT_LABEL, source_provenance: source,
|
|
1812
|
+
non_empty_evidence_present: true, contract_version: contractVersion, generated_at: generatedAt,
|
|
1813
|
+
benchmark_mode: benchmarkMode,
|
|
1814
|
+
benchmark_window: { previous_watermark_at: previousWatermark, window_started_after: windowStarted, watermark_at: watermarkAt },
|
|
1815
|
+
total_records: safeBenchmarkNumber(report.total_records), total_state_sequences: totalStateSequences, caveats: sc,
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
function historyBenchmarkSummary(status, rows, caveats) {
|
|
1819
|
+
const latest = rows.length > 0 ? rows[rows.length - 1] : null;
|
|
1820
|
+
let latestSummary = null;
|
|
1821
|
+
const sc = [...caveats];
|
|
1822
|
+
if (latest) {
|
|
1823
|
+
const runtimeScope = Array.isArray(latest.runtime_scope) ? latest.runtime_scope : [];
|
|
1824
|
+
const safeScope = runtimeScope.map((v) => safeBenchmarkLabel(v)).filter((l) => l !== null);
|
|
1825
|
+
if (safeScope.length !== runtimeScope.length)
|
|
1826
|
+
sc.push("Startup benchmark history omitted unsafe runtime-scope label(s).");
|
|
1827
|
+
const scalar = (key, allowed = null) => {
|
|
1828
|
+
const [v, c] = safeBenchmarkScalar(latest[key], `history_summary.latest_row.${key}`, allowed);
|
|
1829
|
+
sc.push(...c);
|
|
1830
|
+
return v;
|
|
1831
|
+
};
|
|
1832
|
+
const generatedAt = scalar("generated_at");
|
|
1833
|
+
const agenteraVersion = scalar("agentera_version");
|
|
1834
|
+
const benchmarkMode = scalar("benchmark_mode");
|
|
1835
|
+
const recommendationAction = scalar("startup_recommendation_action", BENCHMARK_RECOMMENDATION_ACTIONS);
|
|
1836
|
+
latestSummary = {
|
|
1837
|
+
generated_at: generatedAt, agentera_version: agenteraVersion, runtime_scope: safeScope,
|
|
1838
|
+
benchmark_mode: benchmarkMode, total_state_sequences: safeBenchmarkNumber(latest.total_state_sequences),
|
|
1839
|
+
raw_after_cli_rate: safeBenchmarkNumber(latest.raw_after_cli_rate),
|
|
1840
|
+
redundant_raw_access_rate: safeBenchmarkNumber(latest.redundant_raw_access_rate),
|
|
1841
|
+
startup_recommendation_action: recommendationAction,
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
return {
|
|
1845
|
+
status, source_label: BENCHMARK_HISTORY_LABEL,
|
|
1846
|
+
source_provenance: sourceProvenance("benchmark_context", BENCHMARK_CONTEXT_CMD, "history_summary"),
|
|
1847
|
+
non_empty_evidence_present: rows.length > 0, row_count: rows.length, latest_row: latestSummary, caveats: sc,
|
|
1848
|
+
};
|
|
1849
|
+
}
|
|
1850
|
+
function runtimeBenchmarkCoverage(reportStatus, report) {
|
|
1851
|
+
const source = sourceProvenance("benchmark_context", BENCHMARK_CONTEXT_CMD, "runtime_coverage");
|
|
1852
|
+
const miss = (caveat) => ({
|
|
1853
|
+
status: "missing", source_label: BENCHMARK_LATEST_REPORT_LABEL, source_provenance: source,
|
|
1854
|
+
non_empty_evidence_present: false, items: [], status_counts: {}, caveats: [caveat],
|
|
1855
|
+
});
|
|
1856
|
+
if (reportStatus !== "available" || report === null)
|
|
1857
|
+
return miss("Runtime coverage is unavailable without a valid latest startup benchmark report.");
|
|
1858
|
+
const rawItems = report.runtime_coverage;
|
|
1859
|
+
if (!Array.isArray(rawItems))
|
|
1860
|
+
return miss("Latest startup benchmark report has no runtime_coverage list.");
|
|
1861
|
+
const items = [];
|
|
1862
|
+
let caveats = [];
|
|
1863
|
+
const statusCounts = {};
|
|
1864
|
+
for (const raw of rawItems.slice(0, 12)) {
|
|
1865
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
1866
|
+
caveats.push("Runtime coverage omitted a non-object item.");
|
|
1867
|
+
continue;
|
|
1868
|
+
}
|
|
1869
|
+
const runtime = safeBenchmarkLabel(raw.runtime);
|
|
1870
|
+
let status = safeBenchmarkLabel(raw.status);
|
|
1871
|
+
const reason = safeBenchmarkLabel(raw.reason);
|
|
1872
|
+
if (runtime === null) {
|
|
1873
|
+
caveats.push("Runtime coverage omitted an unsafe runtime label.");
|
|
1874
|
+
continue;
|
|
1875
|
+
}
|
|
1876
|
+
status = status || "unknown";
|
|
1877
|
+
statusCounts[status] = (statusCounts[status] ?? 0) + 1;
|
|
1878
|
+
const item = { runtime, status };
|
|
1879
|
+
if (reason)
|
|
1880
|
+
item.reason = reason;
|
|
1881
|
+
for (const key of ["record_count", "candidate_count", "error_count"]) {
|
|
1882
|
+
const number = safeBenchmarkNumber(raw[key]);
|
|
1883
|
+
if (number !== null)
|
|
1884
|
+
item[key] = number;
|
|
1885
|
+
}
|
|
1886
|
+
items.push(item);
|
|
1887
|
+
}
|
|
1888
|
+
if (rawItems.length > items.length)
|
|
1889
|
+
caveats.push("Runtime coverage summary is bounded and may omit invalid or excess rows.");
|
|
1890
|
+
const caveatedStatuses = [...new Set(items.map((i) => String(i.status)).filter((s) => BENCHMARK_CAVEATED_RUNTIME_STATUSES.has(s)))].sort();
|
|
1891
|
+
let statusValue;
|
|
1892
|
+
if (caveatedStatuses.length > 0) {
|
|
1893
|
+
statusValue = "degraded";
|
|
1894
|
+
caveats.push("One or more runtime stores are missing, skipped, locked, unreadable, or degraded; " +
|
|
1895
|
+
"treat this as benchmark evidence caveat, not successful product behavior.");
|
|
1896
|
+
}
|
|
1897
|
+
else if (items.some((i) => i.status === "sparse")) {
|
|
1898
|
+
statusValue = "sparse";
|
|
1899
|
+
caveats.push("One or more runtime stores are sparse; benchmark coverage is caveated.");
|
|
1900
|
+
}
|
|
1901
|
+
else if (items.length > 0) {
|
|
1902
|
+
statusValue = "available";
|
|
1903
|
+
}
|
|
1904
|
+
else {
|
|
1905
|
+
statusValue = "missing";
|
|
1906
|
+
caveats.push("Runtime coverage has no usable bounded rows.");
|
|
1907
|
+
}
|
|
1908
|
+
const sortedCounts = {};
|
|
1909
|
+
for (const k of Object.keys(statusCounts).sort())
|
|
1910
|
+
sortedCounts[k] = statusCounts[k];
|
|
1911
|
+
return {
|
|
1912
|
+
status: statusValue, source_label: BENCHMARK_LATEST_REPORT_LABEL, source_provenance: source,
|
|
1913
|
+
non_empty_evidence_present: items.length > 0, items, status_counts: sortedCounts, caveats: uniqueList(caveats),
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1916
|
+
function stateAccessBenchmarkMetrics(reportStatus, report) {
|
|
1917
|
+
const source = sourceProvenance("benchmark_context", BENCHMARK_CONTEXT_CMD, "state_access_metrics");
|
|
1918
|
+
if (reportStatus !== "available" || report === null) {
|
|
1919
|
+
return {
|
|
1920
|
+
status: "missing", source_label: BENCHMARK_LATEST_REPORT_LABEL, source_provenance: source,
|
|
1921
|
+
non_empty_evidence_present: false,
|
|
1922
|
+
caveats: ["State-access metrics are unavailable without a valid latest startup benchmark report."],
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1925
|
+
const caveats = [];
|
|
1926
|
+
const [cliCounts, c1] = safeBenchmarkLabelCounts(report.cli_state_command_counts, "cli_state_command_counts");
|
|
1927
|
+
const [rawCounts, c2] = safeBenchmarkLabelCounts(report.raw_artifact_access_after_cli_counts, "raw_artifact_access_after_cli_counts");
|
|
1928
|
+
const [redundantCounts, c3] = safeBenchmarkLabelCounts(report.redundant_raw_artifact_access_counts, "redundant_raw_artifact_access_counts");
|
|
1929
|
+
const [capabilityCounts, c4] = safeBenchmarkLabelCounts(report.per_capability_state_counts, "per_capability_state_counts");
|
|
1930
|
+
caveats.push(...c1, ...c2, ...c3, ...c4);
|
|
1931
|
+
const required = {
|
|
1932
|
+
total_state_sequences: safeBenchmarkNumber(report.total_state_sequences),
|
|
1933
|
+
state_sequences_with_raw_after_cli: safeBenchmarkNumber(report.state_sequences_with_raw_after_cli),
|
|
1934
|
+
state_sequences_with_redundant_raw_access: safeBenchmarkNumber(report.state_sequences_with_redundant_raw_access),
|
|
1935
|
+
raw_after_cli_sequence_rate: safeBenchmarkNumber(report.raw_after_cli_sequence_rate),
|
|
1936
|
+
redundant_raw_sequence_rate: safeBenchmarkNumber(report.redundant_raw_sequence_rate),
|
|
1937
|
+
};
|
|
1938
|
+
const missing = Object.entries(required).filter(([, v]) => v === null).map(([k]) => k);
|
|
1939
|
+
if (missing.length > 0)
|
|
1940
|
+
caveats.push(`Latest startup benchmark report is missing state-access metric fields: ${missing.join(", ")}.`);
|
|
1941
|
+
if (required.total_state_sequences === 0)
|
|
1942
|
+
caveats.push("Startup benchmark observed zero state-gathering sequences; optimization conclusions are weak.");
|
|
1943
|
+
return {
|
|
1944
|
+
status: missing.length > 0 ? "incomplete" : "available",
|
|
1945
|
+
source_label: BENCHMARK_LATEST_REPORT_LABEL, source_provenance: source,
|
|
1946
|
+
non_empty_evidence_present: missing.length === 0,
|
|
1947
|
+
...required,
|
|
1948
|
+
total_cli_state_calls: safeBenchmarkNumber(report.total_cli_state_calls),
|
|
1949
|
+
total_raw_artifact_access_after_cli: safeBenchmarkNumber(report.total_raw_artifact_access_after_cli),
|
|
1950
|
+
total_redundant_raw_artifact_accesses: safeBenchmarkNumber(report.total_redundant_raw_artifact_accesses),
|
|
1951
|
+
cli_state_command_counts: cliCounts,
|
|
1952
|
+
raw_artifact_access_after_cli_counts: rawCounts,
|
|
1953
|
+
redundant_raw_artifact_access_counts: redundantCounts,
|
|
1954
|
+
per_capability_state_counts: capabilityCounts,
|
|
1955
|
+
caveats: uniqueList(caveats),
|
|
1956
|
+
};
|
|
1957
|
+
}
|
|
1958
|
+
function tokenBenchmarkImpact(reportStatus, report) {
|
|
1959
|
+
const source = sourceProvenance("benchmark_context", BENCHMARK_CONTEXT_CMD, "token_impact");
|
|
1960
|
+
if (reportStatus !== "available" || report === null) {
|
|
1961
|
+
return {
|
|
1962
|
+
status: "missing", source_label: BENCHMARK_LATEST_REPORT_LABEL, source_provenance: source,
|
|
1963
|
+
non_empty_evidence_present: false,
|
|
1964
|
+
caveats: ["Token-impact estimates are unavailable without a valid latest startup benchmark report."],
|
|
1965
|
+
};
|
|
1966
|
+
}
|
|
1967
|
+
const [rawByArtifact, rawCaveats] = safeBenchmarkLabelCounts(report.estimated_raw_after_cli_tokens_by_artifact, "estimated_raw_after_cli_tokens_by_artifact");
|
|
1968
|
+
const [redundantByArtifact, redundantCaveats] = safeBenchmarkLabelCounts(report.estimated_redundant_raw_tokens_by_artifact, "estimated_redundant_raw_tokens_by_artifact");
|
|
1969
|
+
const [estimatorVersion, estimatorCaveats] = safeBenchmarkScalar(report.token_estimator_version, "token_impact.token_estimator_version");
|
|
1970
|
+
const required = {
|
|
1971
|
+
token_estimator_version: estimatorVersion,
|
|
1972
|
+
estimated_raw_after_cli_tokens: safeBenchmarkNumber(report.estimated_raw_after_cli_tokens),
|
|
1973
|
+
estimated_redundant_raw_tokens: safeBenchmarkNumber(report.estimated_redundant_raw_tokens),
|
|
1974
|
+
};
|
|
1975
|
+
const missing = Object.entries(required).filter(([, v]) => v === null || v === undefined || v === "").map(([k]) => k);
|
|
1976
|
+
const caveats = [...rawCaveats, ...redundantCaveats, ...estimatorCaveats];
|
|
1977
|
+
if (missing.length > 0)
|
|
1978
|
+
caveats.push(`Latest startup benchmark report is missing token-impact fields: ${missing.join(", ")}.`);
|
|
1979
|
+
return {
|
|
1980
|
+
status: missing.length > 0 ? "missing" : "available",
|
|
1981
|
+
source_label: BENCHMARK_LATEST_REPORT_LABEL, source_provenance: source,
|
|
1982
|
+
non_empty_evidence_present: missing.length === 0,
|
|
1983
|
+
...required,
|
|
1984
|
+
estimated_raw_after_cli_tokens_by_artifact: rawByArtifact,
|
|
1985
|
+
estimated_redundant_raw_tokens_by_artifact: redundantByArtifact,
|
|
1986
|
+
caveats: uniqueList(caveats),
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
function benchmarkComparison(reportStatus, report) {
|
|
1990
|
+
const source = sourceProvenance("benchmark_context", BENCHMARK_CONTEXT_CMD, "comparison");
|
|
1991
|
+
if (reportStatus !== "available" || report === null) {
|
|
1992
|
+
return {
|
|
1993
|
+
status: "missing", source_label: BENCHMARK_LATEST_REPORT_LABEL, source_provenance: source,
|
|
1994
|
+
estimated_tokens_saved_vs_previous: null, null_reason: null, allowed_null_reasons: BENCHMARK_TOKEN_NULL_REASONS,
|
|
1995
|
+
caveats: ["Benchmark comparison is unavailable without a valid latest startup benchmark report."],
|
|
1996
|
+
};
|
|
1997
|
+
}
|
|
1998
|
+
const saved = report.estimated_tokens_saved_vs_previous;
|
|
1999
|
+
const reason = report.estimated_tokens_saved_vs_previous_null_reason;
|
|
2000
|
+
let status;
|
|
2001
|
+
let caveats;
|
|
2002
|
+
if (safeBenchmarkNumber(saved) !== null) {
|
|
2003
|
+
status = "comparable";
|
|
2004
|
+
caveats = [];
|
|
2005
|
+
}
|
|
2006
|
+
else if (BENCHMARK_TOKEN_NULL_REASONS.includes(reason)) {
|
|
2007
|
+
status = "not_comparable";
|
|
2008
|
+
caveats = [`Benchmark comparison is not comparable: ${reason}.`];
|
|
2009
|
+
}
|
|
2010
|
+
else {
|
|
2011
|
+
status = "missing";
|
|
2012
|
+
caveats = ["Benchmark comparison status is missing from latest startup benchmark report."];
|
|
2013
|
+
}
|
|
2014
|
+
return {
|
|
2015
|
+
status, source_label: BENCHMARK_LATEST_REPORT_LABEL, source_provenance: source,
|
|
2016
|
+
estimated_tokens_saved_vs_previous: safeBenchmarkNumber(saved),
|
|
2017
|
+
null_reason: BENCHMARK_TOKEN_NULL_REASONS.includes(reason) ? reason : null,
|
|
2018
|
+
allowed_null_reasons: BENCHMARK_TOKEN_NULL_REASONS, caveats,
|
|
2019
|
+
};
|
|
2020
|
+
}
|
|
2021
|
+
function benchmarkRecommendation(reportStatus, report) {
|
|
2022
|
+
const source = sourceProvenance("benchmark_context", BENCHMARK_CONTEXT_CMD, "recommendation");
|
|
2023
|
+
const miss = (caveat) => ({
|
|
2024
|
+
status: "missing", source_label: BENCHMARK_LATEST_REPORT_LABEL, source_provenance: source,
|
|
2025
|
+
action: null, measured_trigger: null, rationale: null, rationale_present: false,
|
|
2026
|
+
rationale_boundary: "not_emitted_from_retained_report", implementation_recommended: false, caveats: [caveat],
|
|
2027
|
+
});
|
|
2028
|
+
if (reportStatus !== "available" || report === null)
|
|
2029
|
+
return miss("Startup benchmark recommendation is unavailable without a valid latest report.");
|
|
2030
|
+
const recommendation = report.startup_recommendation;
|
|
2031
|
+
if (!recommendation || typeof recommendation !== "object" || Array.isArray(recommendation)) {
|
|
2032
|
+
return miss("Latest startup benchmark report has no startup_recommendation object.");
|
|
2033
|
+
}
|
|
2034
|
+
const rec = recommendation;
|
|
2035
|
+
const caveats = [];
|
|
2036
|
+
let action = rec.action;
|
|
2037
|
+
if (!BENCHMARK_RECOMMENDATION_ACTIONS.has(action)) {
|
|
2038
|
+
action = "omitted_by_privacy_boundary";
|
|
2039
|
+
caveats.push("Startup benchmark recommendation action was omitted because it is not a supported bounded value.");
|
|
2040
|
+
}
|
|
2041
|
+
let trigger = rec.measured_trigger;
|
|
2042
|
+
if (typeof trigger === "string" && safeBenchmarkLabel(trigger) === null) {
|
|
2043
|
+
trigger = "omitted_by_privacy_boundary";
|
|
2044
|
+
caveats.push("Startup benchmark recommendation trigger was omitted at the privacy boundary.");
|
|
2045
|
+
}
|
|
2046
|
+
else if (typeof trigger !== "string") {
|
|
2047
|
+
trigger = null;
|
|
2048
|
+
}
|
|
2049
|
+
const rationalePresent = typeof rec.rationale === "string" && rec.rationale.trim().length > 0;
|
|
2050
|
+
if (rationalePresent)
|
|
2051
|
+
caveats.push("Startup benchmark recommendation rationale is present but not emitted from retained benchmark JSON.");
|
|
2052
|
+
return {
|
|
2053
|
+
status: "available", source_label: BENCHMARK_LATEST_REPORT_LABEL, source_provenance: source,
|
|
2054
|
+
action, measured_trigger: trigger, rationale: null, rationale_present: rationalePresent,
|
|
2055
|
+
rationale_boundary: "not_emitted_from_retained_report",
|
|
2056
|
+
implementation_recommended: Boolean(report.implementation_recommended), caveats,
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
function benchmarkManualRefresh(complete, latestReport, stateMetrics) {
|
|
2060
|
+
const caveats = ["The CLI did not run `mage bench:startupState`; benchmark refresh is manual-only by design."];
|
|
2061
|
+
let status;
|
|
2062
|
+
if (["missing", "empty", "malformed", "unreadable"].includes(latestReport.status)) {
|
|
2063
|
+
status = "requires_manual_run";
|
|
2064
|
+
caveats.push("Retained startup benchmark evidence is absent or invalid; run the manual benchmark before using it for optimization decisions.");
|
|
2065
|
+
}
|
|
2066
|
+
else if (stateMetrics.total_state_sequences === 0) {
|
|
2067
|
+
status = "requires_manual_run";
|
|
2068
|
+
caveats.push("Retained startup benchmark evidence has zero state-gathering sequences; refresh or gather better evidence before optimizing from it.");
|
|
2069
|
+
}
|
|
2070
|
+
else if (complete) {
|
|
2071
|
+
status = "available";
|
|
2072
|
+
}
|
|
2073
|
+
else {
|
|
2074
|
+
status = "requires_manual_run";
|
|
2075
|
+
}
|
|
2076
|
+
return { status, command: "mage bench:startupState", execution_status: "not_run_by_design", auto_run: false, caveats: uniqueList(caveats) };
|
|
2077
|
+
}
|
|
2078
|
+
function benchmarkPrivacyBoundary() {
|
|
2079
|
+
return {
|
|
2080
|
+
status: "enforced",
|
|
2081
|
+
source_provenance: sourceProvenance("benchmark_context", BENCHMARK_CONTEXT_CMD, "privacy_boundary"),
|
|
2082
|
+
user_local_benchmark_reads: "cli_internal_summary_only",
|
|
2083
|
+
normal_agent_file_reads: "last_resort_diagnostics_only",
|
|
2084
|
+
raw_paths_emitted: false, raw_report_bodies_emitted: false, forbidden_outputs: BENCHMARK_FORBIDDEN_OUTPUTS,
|
|
2085
|
+
allowed_outputs: [
|
|
2086
|
+
"canonical source labels", "canonical runtime labels", "canonical artifact labels",
|
|
2087
|
+
"bounded counts and rates", "token estimate aggregates", "comparison null reasons", "manual refresh command",
|
|
2088
|
+
],
|
|
2089
|
+
};
|
|
2090
|
+
}
|
|
2091
|
+
function optimeraBenchmarkContext(capability) {
|
|
2092
|
+
if (capability !== "optimera")
|
|
2093
|
+
return null;
|
|
2094
|
+
const benchmarkDir = startupBenchmarkDir();
|
|
2095
|
+
const [latestStatus, latestData, latestCaveats] = readBenchmarkJson(path.join(benchmarkDir, "latest-report.json"), BENCHMARK_LATEST_REPORT_LABEL);
|
|
2096
|
+
const [historyStatus, historyRows, historyCaveats] = readBenchmarkHistory(path.join(benchmarkDir, "runs.jsonl"));
|
|
2097
|
+
const latestReport = latestBenchmarkSummary(latestStatus, latestData, latestCaveats);
|
|
2098
|
+
const historySummary = historyBenchmarkSummary(historyStatus, historyRows, historyCaveats);
|
|
2099
|
+
const runtimeCoverage = runtimeBenchmarkCoverage(latestStatus, latestData);
|
|
2100
|
+
const stateMetrics = stateAccessBenchmarkMetrics(latestStatus, latestData);
|
|
2101
|
+
const tokenImpact = tokenBenchmarkImpact(latestStatus, latestData);
|
|
2102
|
+
const comparison = benchmarkComparison(latestStatus, latestData);
|
|
2103
|
+
const recommendation = benchmarkRecommendation(latestStatus, latestData);
|
|
2104
|
+
const privacyBoundary = benchmarkPrivacyBoundary();
|
|
2105
|
+
const requiredState = {
|
|
2106
|
+
latest_report: latestReport.status === "available" && Boolean(latestReport.non_empty_evidence_present),
|
|
2107
|
+
history_summary: ["available", "empty"].includes(historySummary.status),
|
|
2108
|
+
runtime_coverage: runtimeCoverage.status !== "missing",
|
|
2109
|
+
state_access_metrics: stateMetrics.status === "available",
|
|
2110
|
+
token_impact_status: ["available", "missing"].includes(tokenImpact.status),
|
|
2111
|
+
recommendation_status: ["available", "missing"].includes(recommendation.status),
|
|
2112
|
+
source_contract: true,
|
|
2113
|
+
};
|
|
2114
|
+
const missingRequired = Object.entries(requiredState).filter(([, present]) => !present).map(([k]) => k);
|
|
2115
|
+
const complete = missingRequired.length === 0;
|
|
2116
|
+
const manualRefresh = benchmarkManualRefresh(complete, latestReport, stateMetrics);
|
|
2117
|
+
const benchmarkSourceCaveats = [...latestCaveats, ...historyCaveats];
|
|
2118
|
+
const retainedOutputs = [
|
|
2119
|
+
{ source_label: BENCHMARK_LATEST_REPORT_LABEL, filename: "latest-report.json", status: latestStatus },
|
|
2120
|
+
{ source_label: BENCHMARK_HISTORY_LABEL, filename: "runs.jsonl", status: historyStatus },
|
|
2121
|
+
{ source_label: "startup_benchmark_latest_markdown", filename: "latest-report.md", status: "not_read_by_context" },
|
|
2122
|
+
];
|
|
2123
|
+
const caveats = uniqueList([
|
|
2124
|
+
...benchmarkSourceCaveats,
|
|
2125
|
+
...(latestReport.caveats ?? []),
|
|
2126
|
+
...(historySummary.caveats ?? []),
|
|
2127
|
+
...(runtimeCoverage.caveats ?? []),
|
|
2128
|
+
...(stateMetrics.caveats ?? []),
|
|
2129
|
+
...(tokenImpact.caveats ?? []),
|
|
2130
|
+
...(comparison.caveats ?? []),
|
|
2131
|
+
...(recommendation.caveats ?? []),
|
|
2132
|
+
...(manualRefresh.caveats ?? []),
|
|
2133
|
+
]);
|
|
2134
|
+
const fallbackCommands = ["agentera docs --format json", "agentera query --list-artifacts --format json"];
|
|
2135
|
+
return {
|
|
2136
|
+
capability: "optimera",
|
|
2137
|
+
benchmark_source: {
|
|
2138
|
+
status: latestStatus === "available" && ["available", "empty"].includes(historyStatus) ? "available" : "incomplete",
|
|
2139
|
+
source_provenance: sourceProvenance("benchmark_context", BENCHMARK_CONTEXT_CMD, "benchmark_source"),
|
|
2140
|
+
retained_outputs: retainedOutputs,
|
|
2141
|
+
non_empty_evidence_present: Boolean(latestReport.non_empty_evidence_present) || Boolean(historySummary.non_empty_evidence_present),
|
|
2142
|
+
normal_read_policy: "Agents consume this CLI summary first; direct retained benchmark file reads are last-resort diagnostics.",
|
|
2143
|
+
caveats: benchmarkSourceCaveats,
|
|
2144
|
+
},
|
|
2145
|
+
latest_report: latestReport,
|
|
2146
|
+
history_summary: historySummary,
|
|
2147
|
+
runtime_coverage: runtimeCoverage,
|
|
2148
|
+
state_access_metrics: stateMetrics,
|
|
2149
|
+
token_impact: tokenImpact,
|
|
2150
|
+
comparison,
|
|
2151
|
+
recommendation,
|
|
2152
|
+
manual_refresh: manualRefresh,
|
|
2153
|
+
privacy_boundary: privacyBoundary,
|
|
2154
|
+
state_family_caveats: caveats,
|
|
2155
|
+
fallback_commands: fallbackCommands,
|
|
2156
|
+
source_contract: {
|
|
2157
|
+
complete_for_benchmark_context: complete,
|
|
2158
|
+
caveated: caveats.length > 0,
|
|
2159
|
+
raw_artifact_reads_required: false,
|
|
2160
|
+
raw_artifact_read_policy: "Use this benchmark_context from `agentera prime --context optimera --format json` first. " +
|
|
2161
|
+
"If incomplete, follow fallback_commands and manual_refresh before any last-resort direct latest-report.json, " +
|
|
2162
|
+
"latest-report.md, or runs.jsonl diagnostic read.",
|
|
2163
|
+
benchmark_state_families: [
|
|
2164
|
+
"latest_report", "history_summary", "runtime_coverage", "state_access_metrics", "token_impact",
|
|
2165
|
+
"comparison", "recommendation", "manual_refresh", "privacy_boundary",
|
|
2166
|
+
],
|
|
2167
|
+
required_benchmark_state: requiredState,
|
|
2168
|
+
missing_required_benchmark_state: missingRequired,
|
|
2169
|
+
source_labels: BENCHMARK_CONTEXT_SOURCE_LABELS,
|
|
2170
|
+
fallback_commands: fallbackCommands,
|
|
2171
|
+
manual_refresh_status: manualRefresh.status,
|
|
2172
|
+
privacy_boundary: { raw_paths_emitted: false, raw_report_bodies_emitted: false, forbidden_outputs: BENCHMARK_FORBIDDEN_OUTPUTS },
|
|
2173
|
+
caveats,
|
|
2174
|
+
owns: [
|
|
2175
|
+
"retained startup benchmark source status",
|
|
2176
|
+
"latest report summary",
|
|
2177
|
+
"aggregate history summary",
|
|
2178
|
+
"runtime coverage caveats",
|
|
2179
|
+
"state-access rates and counts",
|
|
2180
|
+
"token-impact estimates",
|
|
2181
|
+
"comparison null reasons",
|
|
2182
|
+
"startup recommendation action",
|
|
2183
|
+
"manual refresh guidance",
|
|
2184
|
+
"privacy boundary",
|
|
2185
|
+
"raw-read-last-resort policy",
|
|
2186
|
+
"truthful completeness metadata",
|
|
2187
|
+
],
|
|
2188
|
+
deferred: ["2.3.12 Realisera execution-context state"],
|
|
2189
|
+
},
|
|
2190
|
+
};
|
|
2191
|
+
}
|
|
2192
|
+
// ── dokumentera closeout bespoke context ────────────────────────────
|
|
2193
|
+
import { execFileSync } from "node:child_process";
|
|
2194
|
+
function closeoutArtifactMappings(docs) {
|
|
2195
|
+
const mapping = asList(docs.mapping);
|
|
2196
|
+
const ok = Boolean(docs.exists) && mapping.length > 0;
|
|
2197
|
+
return {
|
|
2198
|
+
status: ok ? "available" : "unavailable",
|
|
2199
|
+
source_provenance: sourceProvenance("docs", "agentera docs --format json", "summary.mapping"),
|
|
2200
|
+
entries: mapping,
|
|
2201
|
+
mapping_entries: mapping.length,
|
|
2202
|
+
caveats: ok ? [] : ["Docs artifact mappings are unavailable or empty in CLI docs state."],
|
|
2203
|
+
};
|
|
2204
|
+
}
|
|
2205
|
+
function closeoutVersionPolicy(docs) {
|
|
2206
|
+
const conventions = docsConventions(docs);
|
|
2207
|
+
const semverPolicy = conventions.semver_policy && typeof conventions.semver_policy === "object" && !Array.isArray(conventions.semver_policy) ? conventions.semver_policy : {};
|
|
2208
|
+
const versionFiles = asList(conventions.version_files);
|
|
2209
|
+
const registry = conventions.version_files_registry ?? null;
|
|
2210
|
+
const available = Object.keys(semverPolicy).length > 0;
|
|
2211
|
+
return {
|
|
2212
|
+
status: available ? "available" : "unavailable",
|
|
2213
|
+
source_provenance: sourceProvenance("docs", "agentera docs --format json", "summary.conventions"),
|
|
2214
|
+
version_files: versionFiles,
|
|
2215
|
+
version_files_registry: registry,
|
|
2216
|
+
semver_policy: semverPolicy,
|
|
2217
|
+
caveats: available ? [] : ["Docs version policy is unavailable in CLI docs state."],
|
|
2218
|
+
};
|
|
2219
|
+
}
|
|
2220
|
+
function closeoutTodoBlockers(schemas, todoItems) {
|
|
2221
|
+
const info = schemas.todo ?? { path: "TODO.md", record: undefined, schema: {}, fields: {} };
|
|
2222
|
+
const exists = fs.existsSync(artifactPath(info, "todo"));
|
|
2223
|
+
return {
|
|
2224
|
+
status: exists ? "available" : "unavailable",
|
|
2225
|
+
source_provenance: sourceProvenance("todo", "agentera todo --format json"),
|
|
2226
|
+
open_count: todoItems.length,
|
|
2227
|
+
items: todoItems,
|
|
2228
|
+
caveats: exists ? [] : ["TODO state is unavailable in CLI state."],
|
|
2229
|
+
};
|
|
2230
|
+
}
|
|
2231
|
+
function closeoutBenchmarkEvidence(docs) {
|
|
2232
|
+
const coverage = docs.coverage && typeof docs.coverage === "object" && !Array.isArray(docs.coverage) ? docs.coverage : {};
|
|
2233
|
+
const testsSummary = String(coverage.tests ?? "").trim();
|
|
2234
|
+
if (testsSummary && testsSummary.toLowerCase().includes("benchmark")) {
|
|
2235
|
+
return {
|
|
2236
|
+
status: "available",
|
|
2237
|
+
source_provenance: sourceProvenance("docs", "agentera docs --format json", "summary.coverage.tests"),
|
|
2238
|
+
summary_present: true,
|
|
2239
|
+
non_empty_evidence_present: true,
|
|
2240
|
+
history_scope: "cli_visible_summary",
|
|
2241
|
+
user_local_benchmark_reads_required: false,
|
|
2242
|
+
summary: testsSummary,
|
|
2243
|
+
caveats: [],
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
return {
|
|
2247
|
+
status: "unavailable",
|
|
2248
|
+
source_provenance: sourceProvenance("docs", "agentera docs --format json", "summary.coverage.tests"),
|
|
2249
|
+
summary_present: false,
|
|
2250
|
+
non_empty_evidence_present: false,
|
|
2251
|
+
history_scope: "not_exposed_by_supported_cli_state",
|
|
2252
|
+
user_local_benchmark_reads_required: false,
|
|
2253
|
+
summary: null,
|
|
2254
|
+
caveats: ["Supported CLI/state summaries do not expose benchmark evidence; do not read user-local benchmark files for normal closeout."],
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
2257
|
+
function localGitTagEvidence(targetVersion) {
|
|
2258
|
+
const tag = targetVersion ? `v${targetVersion}` : null;
|
|
2259
|
+
const source = { source_family: "local_git", command: "git tag --list <target-tag>", remote: false };
|
|
2260
|
+
if (!tag) {
|
|
2261
|
+
return { status: "unavailable", tag: null, source_provenance: source, object_type: null, caveats: ["No selected target version is available for local tag evidence."] };
|
|
2262
|
+
}
|
|
2263
|
+
let stdout;
|
|
2264
|
+
try {
|
|
2265
|
+
stdout = execFileSync("git", ["tag", "--list", tag], { cwd: process.cwd(), encoding: "utf8", timeout: 2000 });
|
|
2266
|
+
}
|
|
2267
|
+
catch (exc) {
|
|
2268
|
+
const e = exc;
|
|
2269
|
+
if (typeof e.status === "number" && e.status !== 0) {
|
|
2270
|
+
return { status: "unavailable", tag, source_provenance: source, object_type: null, caveats: ["Local git tag evidence is unavailable because this project is not a git worktree."] };
|
|
2271
|
+
}
|
|
2272
|
+
return { status: "unavailable", tag, source_provenance: source, object_type: null, caveats: [`Local git tag evidence is unavailable: ${exc.message}`] };
|
|
2273
|
+
}
|
|
2274
|
+
if (!stdout.split(/\r\n|\r|\n/).includes(tag)) {
|
|
2275
|
+
return { status: "absent", tag, source_provenance: source, object_type: null, caveats: [`Local tag ${tag} is not present.`] };
|
|
2276
|
+
}
|
|
2277
|
+
let objectType = null;
|
|
2278
|
+
try {
|
|
2279
|
+
const out = execFileSync("git", ["cat-file", "-t", tag], { cwd: process.cwd(), encoding: "utf8", timeout: 2000 });
|
|
2280
|
+
objectType = out.trim() || null;
|
|
2281
|
+
}
|
|
2282
|
+
catch {
|
|
2283
|
+
objectType = null;
|
|
2284
|
+
}
|
|
2285
|
+
return {
|
|
2286
|
+
status: "available",
|
|
2287
|
+
tag,
|
|
2288
|
+
source_provenance: source,
|
|
2289
|
+
object_type: objectType,
|
|
2290
|
+
caveats: objectType ? [] : [`Local tag ${tag} exists but object type could not be verified.`],
|
|
2291
|
+
};
|
|
2292
|
+
}
|
|
2293
|
+
function closeoutReleaseBoundary(changelogBoundary, bundle) {
|
|
2294
|
+
const targetVersion = changelogBoundary.selected_target_version;
|
|
2295
|
+
const localTag = localGitTagEvidence(targetVersion ? String(targetVersion) : null);
|
|
2296
|
+
const metadataRecorded = Boolean(changelogBoundary.selected_target_recorded);
|
|
2297
|
+
const caveats = [...(changelogBoundary.caveats ?? []), ...(localTag.caveats ?? [])];
|
|
2298
|
+
if (!metadataRecorded && localTag.status !== "available") {
|
|
2299
|
+
caveats.push("No local closeout metadata or local tag evidence is recorded for the selected target.");
|
|
2300
|
+
}
|
|
2301
|
+
return {
|
|
2302
|
+
status: changelogBoundary.boundary_present ? "available" : "incomplete",
|
|
2303
|
+
selected_target_version: targetVersion ?? null,
|
|
2304
|
+
boundary: changelogBoundary.boundary ?? null,
|
|
2305
|
+
local_metadata_evidence: {
|
|
2306
|
+
status: metadataRecorded ? "recorded" : "not_recorded",
|
|
2307
|
+
source_provenance: changelogBoundary.source_provenance ?? null,
|
|
2308
|
+
field: "changelog_boundary.selected_target_recorded",
|
|
2309
|
+
},
|
|
2310
|
+
local_tag_evidence: localTag,
|
|
2311
|
+
publication_evidence: {
|
|
2312
|
+
package_publication: "not_recorded_in_cli_state",
|
|
2313
|
+
remote_push: "not_recorded_in_cli_state",
|
|
2314
|
+
remote_checks_performed: false,
|
|
2315
|
+
registry_checks_performed: false,
|
|
2316
|
+
source_provenance: sourceProvenance("closeout_context", "agentera prime --context dokumentera --format json", "release_boundary.publication_evidence"),
|
|
2317
|
+
caveats: ["Closeout context does not contact remotes or package registries."],
|
|
2318
|
+
},
|
|
2319
|
+
app_refresh_evidence: {
|
|
2320
|
+
installed_app_status: bundle.status ?? null,
|
|
2321
|
+
refresh: "not_recorded_in_cli_state",
|
|
2322
|
+
approval_recorded: false,
|
|
2323
|
+
source_provenance: sourceProvenance("hej", "agentera hej --format json", "bundle.status"),
|
|
2324
|
+
},
|
|
2325
|
+
caveats,
|
|
2326
|
+
};
|
|
2327
|
+
}
|
|
2328
|
+
function dokumenteraCloseoutContext(capability, schemas, plan, progress, todoItems, docs, profile, bundle) {
|
|
2329
|
+
if (capability !== "dokumentera")
|
|
2330
|
+
return null;
|
|
2331
|
+
const capabilityContract = capabilityContext(capability) ?? {};
|
|
2332
|
+
const artifactMappings = closeoutArtifactMappings(docs);
|
|
2333
|
+
const versionPolicy = closeoutVersionPolicy(docs);
|
|
2334
|
+
const todoBlockers = closeoutTodoBlockers(schemas, todoItems);
|
|
2335
|
+
const changelogBoundary = closeoutChangelogBoundary(schemas, plan);
|
|
2336
|
+
const progressEvidence = progressVerificationSummary(progress);
|
|
2337
|
+
const benchmarkEvidence = closeoutBenchmarkEvidence(docs);
|
|
2338
|
+
const releaseBoundary = closeoutReleaseBoundary(changelogBoundary, bundle);
|
|
2339
|
+
const reviewPressure = decisionReviewPressure(schemas);
|
|
2340
|
+
const requiredState = {
|
|
2341
|
+
artifact_mappings: artifactMappings.status === "available",
|
|
2342
|
+
version_policy: versionPolicy.status === "available",
|
|
2343
|
+
todo_blockers: todoBlockers.status === "available",
|
|
2344
|
+
changelog_boundary: Boolean(changelogBoundary.boundary_present),
|
|
2345
|
+
progress_evidence: progressEvidence.status === "available",
|
|
2346
|
+
benchmark_evidence_or_caveat: benchmarkEvidence.status === "available" || (benchmarkEvidence.caveats ?? []).length > 0,
|
|
2347
|
+
decision_review_pressure: reviewPressure.status !== "review_required",
|
|
2348
|
+
};
|
|
2349
|
+
const missingRequired = Object.entries(requiredState).filter(([, present]) => !present).map(([k]) => k);
|
|
2350
|
+
let stateCaveats = [];
|
|
2351
|
+
for (const family of (capabilityContract.missing_state_families ?? [])) {
|
|
2352
|
+
stateCaveats.push(`${family} state is not included in prime --context startup context.`);
|
|
2353
|
+
}
|
|
2354
|
+
for (const component of [artifactMappings, versionPolicy, todoBlockers, changelogBoundary, releaseBoundary, progressEvidence, benchmarkEvidence, reviewPressure]) {
|
|
2355
|
+
stateCaveats.push(...(component.caveats ?? []));
|
|
2356
|
+
}
|
|
2357
|
+
if (bundle.status !== "up_to_date")
|
|
2358
|
+
stateCaveats.push("Agentera app files are not up to date; this is a caveat, not approval to repair or update app files.");
|
|
2359
|
+
if (profile.status !== "loaded")
|
|
2360
|
+
stateCaveats.push("profile-derived state is unavailable in prime --context response.");
|
|
2361
|
+
else if (profile.stale === true)
|
|
2362
|
+
stateCaveats.push("profile-derived state is stale; this is a caveat, not approval to refresh profile state.");
|
|
2363
|
+
stateCaveats = uniqueList(stateCaveats);
|
|
2364
|
+
const fallbackCommands = uniqueList([
|
|
2365
|
+
"agentera todo --format json",
|
|
2366
|
+
"agentera docs --format json",
|
|
2367
|
+
"agentera progress --format json",
|
|
2368
|
+
"agentera query changelog --format json",
|
|
2369
|
+
"agentera query --list-artifacts --format json",
|
|
2370
|
+
...(capabilityContract.cli_fallback ?? []),
|
|
2371
|
+
]);
|
|
2372
|
+
return {
|
|
2373
|
+
capability: "dokumentera",
|
|
2374
|
+
artifact_mappings: artifactMappings,
|
|
2375
|
+
version_policy: versionPolicy,
|
|
2376
|
+
todo_blockers: todoBlockers,
|
|
2377
|
+
changelog_boundary: changelogBoundary,
|
|
2378
|
+
release_boundary: releaseBoundary,
|
|
2379
|
+
progress_evidence: progressEvidence,
|
|
2380
|
+
benchmark_evidence: benchmarkEvidence,
|
|
2381
|
+
decision_review_pressure: reviewPressure,
|
|
2382
|
+
state_family_caveats: stateCaveats,
|
|
2383
|
+
fallback_commands: fallbackCommands,
|
|
2384
|
+
source_contract: {
|
|
2385
|
+
complete_for_closeout_context: missingRequired.length === 0,
|
|
2386
|
+
caveated: stateCaveats.length > 0,
|
|
2387
|
+
raw_artifact_reads_required: false,
|
|
2388
|
+
raw_artifact_read_policy: "Use this closeout_context and included hej state first. Run listed routine/query CLI fallback commands " +
|
|
2389
|
+
"for missing or incomplete closeout state; raw artifact reads are last-resort diagnostics, not normal closeout behavior.",
|
|
2390
|
+
included_state_families: capabilityContract.included_state_families ?? [],
|
|
2391
|
+
missing_state_families: capabilityContract.missing_state_families ?? [],
|
|
2392
|
+
closeout_state_families: ["docs", "todo", "changelog", "progress", "benchmark_evidence", "decisions"],
|
|
2393
|
+
required_closeout_state: requiredState,
|
|
2394
|
+
missing_required_closeout_state: missingRequired,
|
|
2395
|
+
fallback_commands: fallbackCommands,
|
|
2396
|
+
caveats: stateCaveats,
|
|
2397
|
+
owns: [
|
|
2398
|
+
"artifact mappings",
|
|
2399
|
+
"version policy",
|
|
2400
|
+
"TODO blockers",
|
|
2401
|
+
"changelog or no-release boundary",
|
|
2402
|
+
"local metadata/tag versus publication boundary",
|
|
2403
|
+
"latest progress evidence",
|
|
2404
|
+
"benchmark evidence pointer or unavailable caveat",
|
|
2405
|
+
"protected decision review pressure",
|
|
2406
|
+
"provenance pointers and non-empty evidence flags",
|
|
2407
|
+
"fallback commands",
|
|
2408
|
+
"raw-read policy",
|
|
2409
|
+
"truthful completeness flag",
|
|
2410
|
+
],
|
|
2411
|
+
},
|
|
2412
|
+
};
|
|
2413
|
+
}
|
|
2414
|
+
function slimCloseoutContext(value) {
|
|
2415
|
+
const compact = { ...value };
|
|
2416
|
+
compact.benchmark_evidence = compactItemsState(value.benchmark_evidence, 0, 220);
|
|
2417
|
+
compact.todo_blockers = compactItemsState(value.todo_blockers, 3, 160);
|
|
2418
|
+
compact.progress_evidence = compactProgressVerification(value.progress_evidence);
|
|
2419
|
+
return compact;
|
|
2420
|
+
}
|
|
2421
|
+
//# sourceMappingURL=capabilityContext.js.map
|