agentera 3.0.0-dev.5 → 3.0.0-dev.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/bundle/references/adapters/opencode.md +1 -1
- package/bundle/references/adapters/package-registry.yaml +0 -1
- package/bundle/references/adapters/package-surface-characterization.md +1 -2
- package/bundle/references/analysis/startup-measurement-contract.yaml +11 -11
- package/bundle/references/cli/audience-namespace-cli-migration.yaml +40 -26
- package/bundle/references/cli/bundle-skill-vocabulary.yaml +1 -29
- package/bundle/references/cli/coexistence-probe.yaml +9 -0
- package/bundle/references/cli/single-name-protocol.yaml +42 -0
- package/bundle/references/cli/update-channels.yaml +1 -0
- package/bundle/references/cli/upgrade-repair-wording.md +31 -0
- package/bundle/references/cli/v3-handoff-manifest.schema.yaml +98 -0
- package/bundle/references/cli/vocabulary.md +12 -10
- package/bundle/references/meta/documentation-inventory.md +9 -3
- package/bundle/skills/agentera/SKILL.md +14 -14
- package/dist/analytics/extractCorpus/cli.js +101 -0
- package/dist/analytics/extractCorpus/cli.js.map +1 -0
- package/dist/analytics/extractCorpus/copilotSessions.js +231 -0
- package/dist/analytics/extractCorpus/copilotSessions.js.map +1 -0
- package/dist/analytics/extractCorpus/core.js +357 -0
- package/dist/analytics/extractCorpus/core.js.map +1 -0
- package/dist/analytics/extractCorpus/corpus.js +132 -0
- package/dist/analytics/extractCorpus/corpus.js.map +1 -0
- package/dist/analytics/extractCorpus/cursorSessions.js +420 -0
- package/dist/analytics/extractCorpus/cursorSessions.js.map +1 -0
- package/dist/analytics/extractCorpus/filesystemSources.js +122 -0
- package/dist/analytics/extractCorpus/filesystemSources.js.map +1 -0
- package/dist/analytics/extractCorpus/index.js +9 -0
- package/dist/analytics/extractCorpus/index.js.map +1 -0
- package/dist/analytics/extractCorpus/jsonlSessions.js +185 -0
- package/dist/analytics/extractCorpus/jsonlSessions.js.map +1 -0
- package/dist/analytics/extractCorpus/sqliteSessions.js +275 -0
- package/dist/analytics/extractCorpus/sqliteSessions.js.map +1 -0
- package/dist/analytics/extractCorpus.js +2 -1790
- package/dist/analytics/extractCorpus.js.map +1 -1
- package/dist/analytics/usageStats.js +1 -1
- package/dist/analytics/usageStats.js.map +1 -1
- package/dist/cli/capabilityContext/benchmark.js +557 -0
- package/dist/cli/capabilityContext/benchmark.js.map +1 -0
- package/dist/cli/capabilityContext/bespoke.js +25 -0
- package/dist/cli/capabilityContext/bespoke.js.map +1 -0
- package/dist/cli/capabilityContext/closeout.js +230 -0
- package/dist/cli/capabilityContext/closeout.js.map +1 -0
- package/dist/cli/capabilityContext/contract.js +186 -0
- package/dist/cli/capabilityContext/contract.js.map +1 -0
- package/dist/cli/capabilityContext/evidence.js +446 -0
- package/dist/cli/capabilityContext/evidence.js.map +1 -0
- package/dist/cli/capabilityContext/index.js +4 -0
- package/dist/cli/capabilityContext/index.js.map +1 -0
- package/dist/cli/capabilityContext/orchestration.js +107 -0
- package/dist/cli/capabilityContext/orchestration.js.map +1 -0
- package/dist/cli/capabilityContext/planState.js +271 -0
- package/dist/cli/capabilityContext/planState.js.map +1 -0
- package/dist/cli/capabilityContext/progress.js +96 -0
- package/dist/cli/capabilityContext/progress.js.map +1 -0
- package/dist/cli/capabilityContext/realisera.js +174 -0
- package/dist/cli/capabilityContext/realisera.js.map +1 -0
- package/dist/cli/capabilityContext/shared.js +94 -0
- package/dist/cli/capabilityContext/shared.js.map +1 -0
- package/dist/cli/capabilityContext/slim.js +106 -0
- package/dist/cli/capabilityContext/slim.js.map +1 -0
- package/dist/cli/capabilityContext/startup.js +208 -0
- package/dist/cli/capabilityContext/startup.js.map +1 -0
- package/dist/cli/capabilityContext/types.js +43 -0
- package/dist/cli/capabilityContext/types.js.map +1 -0
- package/dist/cli/capabilityContext.js +1 -2486
- package/dist/cli/capabilityContext.js.map +1 -1
- package/dist/cli/commands/backfill.js +84 -0
- package/dist/cli/commands/backfill.js.map +1 -0
- package/dist/cli/commands/compact.js +1 -1
- package/dist/cli/commands/compact.js.map +1 -1
- package/dist/cli/commands/doctor.js +24 -7
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/lint.js +4 -36
- package/dist/cli/commands/lint.js.map +1 -1
- package/dist/cli/commands/query.js +1 -1
- package/dist/cli/commands/query.js.map +1 -1
- package/dist/cli/commands/report.js +11 -2
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/schema.js +1 -2
- package/dist/cli/commands/schema.js.map +1 -1
- package/dist/cli/commands/state/decisions.js +397 -0
- package/dist/cli/commands/state/decisions.js.map +1 -0
- package/dist/cli/commands/state/docs.js +93 -0
- package/dist/cli/commands/state/docs.js.map +1 -0
- package/dist/cli/commands/state/experiments.js +67 -0
- package/dist/cli/commands/state/experiments.js.map +1 -0
- package/dist/cli/commands/state/health.js +114 -0
- package/dist/cli/commands/state/health.js.map +1 -0
- package/dist/cli/commands/state/index.js +51 -0
- package/dist/cli/commands/state/index.js.map +1 -0
- package/dist/cli/commands/state/objective.js +68 -0
- package/dist/cli/commands/state/objective.js.map +1 -0
- package/dist/cli/commands/state/plan.js +172 -0
- package/dist/cli/commands/state/plan.js.map +1 -0
- package/dist/cli/commands/state/progress.js +47 -0
- package/dist/cli/commands/state/progress.js.map +1 -0
- package/dist/cli/commands/state/shared.js +15 -0
- package/dist/cli/commands/state/shared.js.map +1 -0
- package/dist/cli/commands/state/todo.js +121 -0
- package/dist/cli/commands/state/todo.js.map +1 -0
- package/dist/cli/commands/state.js +7 -7
- package/dist/cli/commands/state.js.map +1 -1
- package/dist/cli/commands/validate.js +32 -18
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/cli/dispatch/check.js +328 -0
- package/dist/cli/dispatch/check.js.map +1 -0
- package/dist/cli/dispatch/index.js +177 -0
- package/dist/cli/dispatch/index.js.map +1 -0
- package/dist/cli/dispatch/lifecycle.js +492 -0
- package/dist/cli/dispatch/lifecycle.js.map +1 -0
- package/dist/cli/dispatch/prime.js +100 -0
- package/dist/cli/dispatch/prime.js.map +1 -0
- package/dist/cli/dispatch/shared.js +64 -0
- package/dist/cli/dispatch/shared.js.map +1 -0
- package/dist/cli/dispatch/state.js +149 -0
- package/dist/cli/dispatch/state.js.map +1 -0
- package/dist/cli/dispatch.js +1 -1293
- package/dist/cli/dispatch.js.map +1 -1
- package/dist/cli/help.js +0 -2
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/orientation.js +1 -1
- package/dist/cli/orientation.js.map +1 -1
- package/dist/core/git.js +43 -0
- package/dist/core/git.js.map +1 -0
- package/dist/hooks/common.js +8 -23
- package/dist/hooks/common.js.map +1 -1
- package/dist/hooks/compaction/apply.js +244 -0
- package/dist/hooks/compaction/apply.js.map +1 -0
- package/dist/hooks/compaction/dryRun.js +181 -0
- package/dist/hooks/compaction/dryRun.js.map +1 -0
- package/dist/hooks/compaction/index.js +23 -0
- package/dist/hooks/compaction/index.js.map +1 -0
- package/dist/hooks/compaction/parse.js +129 -0
- package/dist/hooks/compaction/parse.js.map +1 -0
- package/dist/hooks/compaction/retention.js +197 -0
- package/dist/hooks/compaction/retention.js.map +1 -0
- package/dist/hooks/compaction/status.js +243 -0
- package/dist/hooks/compaction/status.js.map +1 -0
- package/dist/hooks/compaction/types.js +6 -0
- package/dist/hooks/compaction/types.js.map +1 -0
- package/dist/hooks/compaction.js +20 -20
- package/dist/hooks/compaction.js.map +1 -1
- package/dist/hooks/cursorPreToolUse.js +1 -1
- package/dist/hooks/cursorPreToolUse.js.map +1 -1
- package/dist/hooks/sessionStart.js +4 -4
- package/dist/hooks/sessionStart.js.map +1 -1
- package/dist/hooks/sessionStop.js +3 -12
- package/dist/hooks/sessionStop.js.map +1 -1
- package/dist/hooks/validateArtifact/agentFacing.js +10 -0
- package/dist/hooks/validateArtifact/agentFacing.js.map +1 -0
- package/dist/hooks/validateArtifact/index.js +149 -0
- package/dist/hooks/validateArtifact/index.js.map +1 -0
- package/dist/hooks/validateArtifact/markdown.js +95 -0
- package/dist/hooks/validateArtifact/markdown.js.map +1 -0
- package/dist/hooks/validateArtifact/runtime.js +83 -0
- package/dist/hooks/validateArtifact/runtime.js.map +1 -0
- package/dist/hooks/validateArtifact/schema.js +455 -0
- package/dist/hooks/validateArtifact/schema.js.map +1 -0
- package/dist/hooks/validateArtifact/traversal.js +105 -0
- package/dist/hooks/validateArtifact/traversal.js.map +1 -0
- package/dist/hooks/validateArtifact/violations.js +105 -0
- package/dist/hooks/validateArtifact/violations.js.map +1 -0
- package/dist/hooks/validateArtifact.js +29 -40
- package/dist/hooks/validateArtifact.js.map +1 -1
- package/dist/migrate/v2HandoffManifest.js +333 -0
- package/dist/migrate/v2HandoffManifest.js.map +1 -0
- package/dist/registries/artifactProtocolIds.js +77 -0
- package/dist/registries/artifactProtocolIds.js.map +1 -0
- package/dist/release/releaseMetadata.js +235 -0
- package/dist/release/releaseMetadata.js.map +1 -0
- package/dist/setup/codex/agents.js +96 -0
- package/dist/setup/codex/agents.js.map +1 -0
- package/dist/setup/codex/cli.js +161 -0
- package/dist/setup/codex/cli.js.map +1 -0
- package/dist/setup/codex/configToml.js +644 -0
- package/dist/setup/codex/configToml.js.map +1 -0
- package/dist/setup/codex/constants.js +29 -0
- package/dist/setup/codex/constants.js.map +1 -0
- package/dist/setup/codex/installRoot.js +64 -0
- package/dist/setup/codex/installRoot.js.map +1 -0
- package/dist/setup/codex/state.js +270 -0
- package/dist/setup/codex/state.js.map +1 -0
- package/dist/setup/codex.js +11 -1196
- package/dist/setup/codex.js.map +1 -1
- package/dist/setup/doctor/core.js +300 -0
- package/dist/setup/doctor/core.js.map +1 -0
- package/dist/setup/doctor/diagnostics.js +247 -0
- package/dist/setup/doctor/diagnostics.js.map +1 -0
- package/dist/setup/doctor/opencode.js +281 -0
- package/dist/setup/doctor/opencode.js.map +1 -0
- package/dist/setup/doctor/report.js +474 -0
- package/dist/setup/doctor/report.js.map +1 -0
- package/dist/setup/doctor.js +9 -1296
- package/dist/setup/doctor.js.map +1 -1
- package/dist/setup/opencode.js +13 -0
- package/dist/setup/opencode.js.map +1 -0
- package/dist/setup/smokeChecks.js +1 -1
- package/dist/setup/smokeChecks.js.map +1 -1
- package/dist/state/progressCommit.js +289 -0
- package/dist/state/progressCommit.js.map +1 -0
- package/dist/state/startupAnalysis/benchmark.js +367 -0
- package/dist/state/startupAnalysis/benchmark.js.map +1 -0
- package/dist/state/startupAnalysis/contract.js +122 -0
- package/dist/state/startupAnalysis/contract.js.map +1 -0
- package/dist/state/startupAnalysis/helpers.js +332 -0
- package/dist/state/startupAnalysis/helpers.js.map +1 -0
- package/dist/state/startupAnalysis/index.js +7 -0
- package/dist/state/startupAnalysis/index.js.map +1 -0
- package/dist/state/startupAnalysis/metrics.js +334 -0
- package/dist/state/startupAnalysis/metrics.js.map +1 -0
- package/dist/state/startupAnalysis/records.js +195 -0
- package/dist/state/startupAnalysis/records.js.map +1 -0
- package/dist/state/startupAnalysis/report.js +123 -0
- package/dist/state/startupAnalysis/report.js.map +1 -0
- package/dist/state/startupAnalysis/threshold.js +500 -0
- package/dist/state/startupAnalysis/threshold.js.map +1 -0
- package/dist/state/startupAnalysis.js +2 -1952
- package/dist/state/startupAnalysis.js.map +1 -1
- package/dist/upgrade/coexistenceProbe.js +83 -0
- package/dist/upgrade/coexistenceProbe.js.map +1 -0
- package/dist/upgrade/doctor.js +41 -2
- package/dist/upgrade/doctor.js.map +1 -1
- package/dist/upgrade/migrateArtifactsV2ToV3.js +5 -83
- package/dist/upgrade/migrateArtifactsV2ToV3.js.map +1 -1
- package/dist/upgrade/nextMajorDoctor.js +16 -1
- package/dist/upgrade/nextMajorDoctor.js.map +1 -1
- package/dist/upgrade/projectIntegration.js +3 -1
- package/dist/upgrade/projectIntegration.js.map +1 -1
- package/dist/upgrade/runtimeMigration.js +13 -2
- package/dist/upgrade/runtimeMigration.js.map +1 -1
- package/dist/upgrade/v3CapabilitySurface.js +15 -0
- package/dist/upgrade/v3CapabilitySurface.js.map +1 -0
- package/dist/upgrade/versionResolution.js +8 -0
- package/dist/upgrade/versionResolution.js.map +1 -1
- package/dist/validate/appHomeContract.js +3 -3
- package/dist/validate/appHomeContract.js.map +1 -1
- package/dist/validate/selfAudit.js +62 -20
- package/dist/validate/selfAudit.js.map +1 -1
- package/dist/validate/vocabularyAuthority.js +298 -0
- package/dist/validate/vocabularyAuthority.js.map +1 -0
- package/package.json +3 -3
- package/bundle/references/v1-section-mapping.md +0 -47
- package/bundle/skills/hej/.claude-plugin/plugin.json +0 -6
- package/bundle/skills/hej/SKILL.md +0 -69
- package/bundle/skills/hej/agents/hej.toml +0 -11
- package/bundle/skills/hej/agents/openai.yaml +0 -8
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown validation for human-facing artifacts (TODO.md, CHANGELOG.md,
|
|
3
|
+
* SKILL.md, ...). Walks the artifact's schema looking for any group
|
|
4
|
+
* with required `field` entries and dispatches to the appropriate
|
|
5
|
+
* section check (ITEM, RELEASE, TOKEN).
|
|
6
|
+
*/
|
|
7
|
+
import { isMapping } from "./schema.js";
|
|
8
|
+
const SKIP_META = new Set(["meta", "GROUP_PREFIXES", "BUDGET", "COMPACTION", "VALIDATION", "CONVENTION"]);
|
|
9
|
+
export function validateMd(content, name, schema = null) {
|
|
10
|
+
const violations = [];
|
|
11
|
+
if (!content.trim())
|
|
12
|
+
violations.push(`${name}: empty content`);
|
|
13
|
+
const fences = (content.match(/^```/gm) ?? []).length;
|
|
14
|
+
if (fences % 2)
|
|
15
|
+
violations.push(`${name}: unclosed code fence`);
|
|
16
|
+
if (schema)
|
|
17
|
+
violations.push(...validateMdSchema(content, name, schema));
|
|
18
|
+
return violations;
|
|
19
|
+
}
|
|
20
|
+
export function validateMdSchema(content, name, schema) {
|
|
21
|
+
const violations = [];
|
|
22
|
+
if (!content.trim())
|
|
23
|
+
return violations;
|
|
24
|
+
for (const [groupKey, groupValue] of Object.entries(schema)) {
|
|
25
|
+
if (SKIP_META.has(groupKey) || !isMapping(groupValue))
|
|
26
|
+
continue;
|
|
27
|
+
const hasRequired = Object.values(groupValue).some((e) => isMapping(e) && e.required && e.field);
|
|
28
|
+
if (!hasRequired)
|
|
29
|
+
continue;
|
|
30
|
+
if (groupKey === "ITEM")
|
|
31
|
+
validateMdItems(content, name, violations);
|
|
32
|
+
else if (groupKey === "RELEASE")
|
|
33
|
+
validateMdReleases(content, name, violations);
|
|
34
|
+
else if (groupKey === "TOKEN")
|
|
35
|
+
validateMdTokens(content, name, violations);
|
|
36
|
+
}
|
|
37
|
+
return violations;
|
|
38
|
+
}
|
|
39
|
+
export function validateMdItems(content, name, violations) {
|
|
40
|
+
const versionHeading = /^##\s+/m.exec(content);
|
|
41
|
+
if (!versionHeading) {
|
|
42
|
+
violations.push(`${name}: missing severity sections (expected '## <glyph> <name>' headings)`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const severityGlyphs = ["⇶", "⇉", "→", "⇢"];
|
|
46
|
+
const requiredItemGlyphs = new Set(["⇶"]);
|
|
47
|
+
let found = false;
|
|
48
|
+
for (const glyph of severityGlyphs) {
|
|
49
|
+
const g = glyph.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
50
|
+
if (new RegExp(`^##\\s*${g}`, "m").test(content)) {
|
|
51
|
+
found = true;
|
|
52
|
+
const sectionStart = new RegExp(`^##\\s*${g}.+$`, "m").exec(content);
|
|
53
|
+
if (sectionStart) {
|
|
54
|
+
const idx = sectionStart.index + sectionStart[0].length;
|
|
55
|
+
const nextMatch = content.slice(idx).match(/\n##\s/);
|
|
56
|
+
let bodyStart = idx;
|
|
57
|
+
if (content.startsWith("\r\n", bodyStart))
|
|
58
|
+
bodyStart += 2;
|
|
59
|
+
else if (content[bodyStart] === "\n")
|
|
60
|
+
bodyStart += 1;
|
|
61
|
+
const sectionEnd = nextMatch ? idx + nextMatch.index : content.length;
|
|
62
|
+
const sectionBody = content.slice(bodyStart, sectionEnd);
|
|
63
|
+
if (requiredItemGlyphs.has(glyph) && !/^\s*-/m.test(sectionBody)) {
|
|
64
|
+
const headingSlice = content.slice(sectionStart.index, sectionStart.index + sectionStart[0].length);
|
|
65
|
+
const glyphNameMatch = new RegExp(`^##\\s*(${g}.+)$`, "m").exec(headingSlice);
|
|
66
|
+
const headingText = glyphNameMatch ? glyphNameMatch[1] : glyph;
|
|
67
|
+
violations.push(`${name}: severity section '${headingText}' has no list entries (expected '- [type]' items)`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!found) {
|
|
73
|
+
violations.push(`${name}: missing severity glyph in section headings (expected '## ⇶ Critical', '## ⇉ Degraded', '## → Normal', '## ⇢ Annoying')`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function validateMdReleases(content, name, violations) {
|
|
77
|
+
if (!/^##\s*\[/m.test(content)) {
|
|
78
|
+
violations.push(`${name}: missing version header (expected '## [X.Y.Z]')`);
|
|
79
|
+
}
|
|
80
|
+
const changeSections = new Set(["### Added", "### Changed", "### Fixed", "### Removed"]);
|
|
81
|
+
const lines = new Set(content.split("\n"));
|
|
82
|
+
if (![...changeSections].some((s) => lines.has(s))) {
|
|
83
|
+
violations.push(`${name}: missing change sections (expected '### Added', '### Changed', '### Fixed', or '### Removed')`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function validateMdTokens(content, name, violations) {
|
|
87
|
+
if (!/^##\s/m.test(content)) {
|
|
88
|
+
violations.push(`${name}: missing section heading (expected '## SectionName')`);
|
|
89
|
+
}
|
|
90
|
+
const yamlBlocks = (content.match(/^```yaml\s*$/gm) ?? []).length;
|
|
91
|
+
if (!yamlBlocks) {
|
|
92
|
+
violations.push(`${name}: missing YAML code block with token definitions (expected '\`\`\`yaml')`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../../src/hooks/validateArtifact/markdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime event parsing for agent artifact writes.
|
|
3
|
+
*
|
|
4
|
+
* Detects which file a runtime wants to write (or which apply_patch
|
|
5
|
+
* headers describe) and packages it as an `ArtifactWrite`. The CLI
|
|
6
|
+
* adapter uses this on the JSON payload that Claude, OpenCode, Codex,
|
|
7
|
+
* and Copilot hand to a PostToolUse hook.
|
|
8
|
+
*/
|
|
9
|
+
import { isMapping } from "./schema.js";
|
|
10
|
+
export class ArtifactWrite {
|
|
11
|
+
file_path;
|
|
12
|
+
content;
|
|
13
|
+
constructor(filePath, content = null) {
|
|
14
|
+
this.file_path = filePath;
|
|
15
|
+
this.content = content;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class RuntimeEventParser {
|
|
19
|
+
parseClaude(data) {
|
|
20
|
+
const ti = data.tool_input;
|
|
21
|
+
if (!isMapping(ti))
|
|
22
|
+
return null;
|
|
23
|
+
const fp = ti.file_path;
|
|
24
|
+
if (fp)
|
|
25
|
+
return new ArtifactWrite(String(fp), ti.content ?? null);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
parseOpencode(data) {
|
|
29
|
+
const inp = data.input;
|
|
30
|
+
if (!isMapping(inp))
|
|
31
|
+
return null;
|
|
32
|
+
const fp = inp.path;
|
|
33
|
+
if (fp)
|
|
34
|
+
return new ArtifactWrite(String(fp), inp.content ?? null);
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
parseCodex(data) {
|
|
38
|
+
const ti = data.tool_input;
|
|
39
|
+
if (!isMapping(ti))
|
|
40
|
+
return null;
|
|
41
|
+
const fp = ti.path;
|
|
42
|
+
const patchBody = ti.patch || ti.command || "";
|
|
43
|
+
if (fp)
|
|
44
|
+
return new ArtifactWrite(String(fp));
|
|
45
|
+
if (typeof patchBody === "string") {
|
|
46
|
+
const headers = [...patchBody.matchAll(/^\*\*\*\s+(?:Add File|Update File):\s+(.+?)\s*$/gm)];
|
|
47
|
+
if (headers.length > 0)
|
|
48
|
+
return new ArtifactWrite(headers[0][1]);
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
parseCopilot(data) {
|
|
53
|
+
const inp = data.input;
|
|
54
|
+
if (!isMapping(inp))
|
|
55
|
+
return null;
|
|
56
|
+
const fp = inp.filePath || inp.file_path;
|
|
57
|
+
if (fp)
|
|
58
|
+
return new ArtifactWrite(String(fp), inp.content ?? null);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
parse(data) {
|
|
62
|
+
const tn = data.tool_name ?? "";
|
|
63
|
+
if (tn === "apply_patch") {
|
|
64
|
+
const candidate = this.parseCodex(data);
|
|
65
|
+
if (candidate)
|
|
66
|
+
return candidate;
|
|
67
|
+
}
|
|
68
|
+
if (tn === "Edit" || tn === "Write" || (isMapping(data.tool_input) && "file_path" in data.tool_input)) {
|
|
69
|
+
const candidate = this.parseClaude(data);
|
|
70
|
+
if (candidate)
|
|
71
|
+
return candidate;
|
|
72
|
+
}
|
|
73
|
+
if (isMapping(data.input)) {
|
|
74
|
+
const inp = data.input;
|
|
75
|
+
if ("filePath" in inp || "file_path" in inp)
|
|
76
|
+
return this.parseCopilot(data);
|
|
77
|
+
if ("path" in inp)
|
|
78
|
+
return this.parseOpencode(data);
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/hooks/validateArtifact/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema-driven validation helpers for the v2 artifact protocol.
|
|
3
|
+
*
|
|
4
|
+
* Walks the schema (skills/agentera/schemas/artifacts/<name>.yaml) and
|
|
5
|
+
* a parsed artifact mapping, producing a list of human-readable
|
|
6
|
+
* violation strings. The helpers in this file are pure (no I/O) and
|
|
7
|
+
* are orchestrated by `violations.ts` for the YAML side and by
|
|
8
|
+
* `markdown.ts` for the human-facing side.
|
|
9
|
+
*/
|
|
10
|
+
export function isMapping(v) {
|
|
11
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
12
|
+
}
|
|
13
|
+
export function pyTypeName(v) {
|
|
14
|
+
if (v === null || v === undefined)
|
|
15
|
+
return "NoneType";
|
|
16
|
+
if (typeof v === "boolean")
|
|
17
|
+
return "bool";
|
|
18
|
+
if (typeof v === "number")
|
|
19
|
+
return Number.isInteger(v) ? "int" : "float";
|
|
20
|
+
if (typeof v === "string")
|
|
21
|
+
return "str";
|
|
22
|
+
if (Array.isArray(v))
|
|
23
|
+
return "list";
|
|
24
|
+
if (typeof v === "object")
|
|
25
|
+
return "dict";
|
|
26
|
+
return typeof v;
|
|
27
|
+
}
|
|
28
|
+
export function wordCount(text) {
|
|
29
|
+
return (text.match(/\S+/g) ?? []).length;
|
|
30
|
+
}
|
|
31
|
+
const SKIP_META = new Set(["meta", "GROUP_PREFIXES", "BUDGET", "COMPACTION", "VALIDATION", "CONVENTION"]);
|
|
32
|
+
const LIST_INDICATORS = new Set(["number", "entry", "summary"]);
|
|
33
|
+
const SEQUENCE_KEYS_BY_ARTIFACT = {
|
|
34
|
+
decisions: { DECISION: "decisions", ARCHIVE: "archive" },
|
|
35
|
+
docs: { MAPPING: "mapping", INDEX: "index", AUDIT_LOG: "audit_log" },
|
|
36
|
+
experiments: { EXPERIMENT: "experiments", ARCHIVE: "archive" },
|
|
37
|
+
plan: { TASK: "tasks" },
|
|
38
|
+
progress: { CYCLE: "cycles", ARCHIVE: "archive" },
|
|
39
|
+
session: { BOOKMARK: "bookmarks" },
|
|
40
|
+
vision: { PERSONA: "personas", PRINCIPLE: "principles" },
|
|
41
|
+
};
|
|
42
|
+
const NESTED_SEQUENCE_KEYS = [[["DECISION", "ALTERNATIVE"], "alternatives"]];
|
|
43
|
+
const SEQUENCE_ORDER_BY_ARTIFACT = { "progress\0cycles": "descending" };
|
|
44
|
+
export function collectSingletonGroups(schema) {
|
|
45
|
+
const result = [];
|
|
46
|
+
for (const [gk, gv] of Object.entries(schema)) {
|
|
47
|
+
if (SKIP_META.has(gk) || !isMapping(gv))
|
|
48
|
+
continue;
|
|
49
|
+
let isListOrSub = false;
|
|
50
|
+
for (const e of Object.values(gv)) {
|
|
51
|
+
if (isMapping(e) && (LIST_INDICATORS.has(e.field) || e.parent)) {
|
|
52
|
+
isListOrSub = true;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (isListOrSub)
|
|
57
|
+
continue;
|
|
58
|
+
const fields = [];
|
|
59
|
+
for (const e of Object.values(gv)) {
|
|
60
|
+
if (isMapping(e) && e.required && "field" in e)
|
|
61
|
+
fields.push(e.field);
|
|
62
|
+
}
|
|
63
|
+
if (fields.length > 0)
|
|
64
|
+
result.push([gk, gk.toLowerCase(), fields]);
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
export function isEmptyRequired(value) {
|
|
69
|
+
if (value === null || value === undefined)
|
|
70
|
+
return true;
|
|
71
|
+
if (typeof value === "string")
|
|
72
|
+
return !value.trim();
|
|
73
|
+
if (Array.isArray(value))
|
|
74
|
+
return value.length === 0;
|
|
75
|
+
if (typeof value === "object")
|
|
76
|
+
return Object.keys(value).length === 0;
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
export function* iterGroupEntries(schema, group) {
|
|
80
|
+
const gv = schema[group];
|
|
81
|
+
if (!isMapping(gv))
|
|
82
|
+
return;
|
|
83
|
+
for (const entry of Object.values(gv)) {
|
|
84
|
+
if (isMapping(entry) && "field" in entry)
|
|
85
|
+
yield entry;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export function validateField(violations, name, scope, field, p) {
|
|
89
|
+
const fullPath = p ? `${p}.${field}` : field;
|
|
90
|
+
if (!(field in scope)) {
|
|
91
|
+
violations.push(`${name}: missing required field '${fullPath}'`);
|
|
92
|
+
}
|
|
93
|
+
else if (isEmptyRequired(scope[field])) {
|
|
94
|
+
violations.push(`${name}: empty required field '${fullPath}'`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export function allowedValues(entry) {
|
|
98
|
+
for (const rule of entry.validation ?? []) {
|
|
99
|
+
if (typeof rule === "string" && rule.startsWith("Must be one of: ")) {
|
|
100
|
+
return rule.slice("Must be one of: ".length).split(",").map((v) => v.trim());
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
export function validateAllowedValue(violations, name, scope, entry, p) {
|
|
106
|
+
const field = entry.field;
|
|
107
|
+
const allowed = allowedValues(entry);
|
|
108
|
+
if (!field || allowed.length === 0 || !(field in scope) || isEmptyRequired(scope[field]))
|
|
109
|
+
return;
|
|
110
|
+
const value = scope[field];
|
|
111
|
+
if (typeof value === "string" && !allowed.includes(value)) {
|
|
112
|
+
violations.push(`${name}: invalid value '${value}' for '${p}.${field}' (expected one of: ${allowed.join(", ")})`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export function validateFieldType(violations, name, scope, entry, p) {
|
|
116
|
+
const field = entry.field;
|
|
117
|
+
if (!field || !(field in scope))
|
|
118
|
+
return true;
|
|
119
|
+
const value = scope[field];
|
|
120
|
+
const expectedType = entry.type;
|
|
121
|
+
if (!expectedType || isEmptyRequired(value))
|
|
122
|
+
return true;
|
|
123
|
+
const fullPath = p ? `${p}.${field}` : field;
|
|
124
|
+
let isValid = true;
|
|
125
|
+
if (expectedType === "integer") {
|
|
126
|
+
if (typeof value === "boolean" || !(typeof value === "number" && Number.isInteger(value))) {
|
|
127
|
+
violations.push(`${name}: '${fullPath}' must be an integer, got ${pyTypeName(value)}`);
|
|
128
|
+
isValid = false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else if (expectedType === "string") {
|
|
132
|
+
if (typeof value !== "string") {
|
|
133
|
+
violations.push(`${name}: '${fullPath}' must be a string, got ${pyTypeName(value)}`);
|
|
134
|
+
isValid = false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else if (expectedType === "map") {
|
|
138
|
+
if (!isMapping(value)) {
|
|
139
|
+
violations.push(`${name}: '${fullPath}' must be a mapping, got ${pyTypeName(value)}`);
|
|
140
|
+
isValid = false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else if (expectedType === "list[string]") {
|
|
144
|
+
if (!Array.isArray(value) || !value.every((x) => typeof x === "string")) {
|
|
145
|
+
violations.push(`${name}: '${fullPath}' must be a list of strings`);
|
|
146
|
+
isValid = false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else if (expectedType === "list[map]") {
|
|
150
|
+
if (!Array.isArray(value) || !value.every((x) => isMapping(x))) {
|
|
151
|
+
violations.push(`${name}: '${fullPath}' must be a list of mappings`);
|
|
152
|
+
isValid = false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return isValid;
|
|
156
|
+
}
|
|
157
|
+
export function validateFieldConstraints(violations, name, scope, entry, p) {
|
|
158
|
+
const field = entry.field;
|
|
159
|
+
if (!field || !(field in scope))
|
|
160
|
+
return;
|
|
161
|
+
const value = scope[field];
|
|
162
|
+
for (const rule of entry.validation ?? []) {
|
|
163
|
+
if (typeof rule !== "string")
|
|
164
|
+
continue;
|
|
165
|
+
if (rule === "Must be a positive integer") {
|
|
166
|
+
if (typeof value === "boolean" || !(typeof value === "number" && Number.isInteger(value)) || value <= 0) {
|
|
167
|
+
const fullPath = p ? `${p}.${field}` : field;
|
|
168
|
+
violations.push(`${name}: '${fullPath}' must be a positive integer`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
export function validateRequiredFields(violations, name, schema, group, scope, p) {
|
|
174
|
+
for (const entry of iterGroupEntries(schema, group)) {
|
|
175
|
+
const field = entry.field;
|
|
176
|
+
if (entry.parent || field === "entry")
|
|
177
|
+
continue;
|
|
178
|
+
if (entry.required)
|
|
179
|
+
validateField(violations, name, scope, field, p);
|
|
180
|
+
if (field in scope && !isEmptyRequired(scope[field])) {
|
|
181
|
+
if (validateFieldType(violations, name, scope, entry, p)) {
|
|
182
|
+
validateAllowedValue(violations, name, scope, entry, p);
|
|
183
|
+
validateFieldConstraints(violations, name, scope, entry, p);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const value = scope[field];
|
|
187
|
+
if (isMapping(value)) {
|
|
188
|
+
for (const child of entry.children ?? []) {
|
|
189
|
+
if (isMapping(child) && child.field) {
|
|
190
|
+
const childField = child.field;
|
|
191
|
+
const childPath = p ? `${p}.${field}` : field;
|
|
192
|
+
if (child.required)
|
|
193
|
+
validateField(violations, name, value, childField, childPath);
|
|
194
|
+
if (childField in value && !isEmptyRequired(value[childField])) {
|
|
195
|
+
if (validateFieldType(violations, name, value, child, childPath)) {
|
|
196
|
+
validateAllowedValue(violations, name, value, child, childPath);
|
|
197
|
+
validateFieldConstraints(violations, name, value, child, childPath);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
export function validateSingletonGroup(violations, name, schema, group, scope, p) {
|
|
206
|
+
for (const entry of iterGroupEntries(schema, group)) {
|
|
207
|
+
const field = entry.field;
|
|
208
|
+
if (entry.parent || field === "entry")
|
|
209
|
+
continue;
|
|
210
|
+
if (entry.required)
|
|
211
|
+
validateField(violations, name, scope, field, p);
|
|
212
|
+
if (field in scope && !isEmptyRequired(scope[field])) {
|
|
213
|
+
if (validateFieldType(violations, name, scope, entry, p)) {
|
|
214
|
+
validateAllowedValue(violations, name, scope, entry, p);
|
|
215
|
+
validateFieldConstraints(violations, name, scope, entry, p);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
export function schemaFieldNames(schema, group) {
|
|
221
|
+
const out = new Set();
|
|
222
|
+
for (const entry of iterGroupEntries(schema, group)) {
|
|
223
|
+
if (entry.field && !entry.parent && entry.field !== "entry")
|
|
224
|
+
out.add(entry.field);
|
|
225
|
+
}
|
|
226
|
+
return out;
|
|
227
|
+
}
|
|
228
|
+
export function validateUnknownFields(violations, name, scope, allowed, p) {
|
|
229
|
+
for (const field of Object.keys(scope)) {
|
|
230
|
+
if (!allowed.has(field)) {
|
|
231
|
+
const fullPath = p ? `${p}.${field}` : field;
|
|
232
|
+
violations.push(`${name}: unsupported field '${fullPath}'`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
export function validatePlanKnownFields(data, schema, violations) {
|
|
237
|
+
const groupedScopes = { header: "HEADER", scope: "SCOPE" };
|
|
238
|
+
const sequenceKeys = new Set(Object.values(SEQUENCE_KEYS_BY_ARTIFACT.plan ?? {}));
|
|
239
|
+
const allowedTopLevel = new Set([
|
|
240
|
+
...schemaFieldNames(schema, "PLAN"),
|
|
241
|
+
...Object.keys(groupedScopes),
|
|
242
|
+
...sequenceKeys,
|
|
243
|
+
]);
|
|
244
|
+
validateUnknownFields(violations, "plan", data, allowedTopLevel, "");
|
|
245
|
+
for (const [key, group] of Object.entries(groupedScopes)) {
|
|
246
|
+
const scope = data[key];
|
|
247
|
+
if (isMapping(scope)) {
|
|
248
|
+
validateUnknownFields(violations, "plan", scope, schemaFieldNames(schema, group), key);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
export function validateFullPlanContract(data, violations) {
|
|
253
|
+
const header = isMapping(data.header) ? data.header : {};
|
|
254
|
+
if (String(header.level ?? "").toLowerCase() !== "full")
|
|
255
|
+
return;
|
|
256
|
+
for (const field of ["reviewed", "critic_issues"]) {
|
|
257
|
+
validateField(violations, "plan", header, field, "header");
|
|
258
|
+
}
|
|
259
|
+
validateField(violations, "plan", data, "design", "");
|
|
260
|
+
const criticIssues = header.critic_issues;
|
|
261
|
+
if (!isEmptyRequired(criticIssues)) {
|
|
262
|
+
const match = /^\s*(\d+)\s+found,\s*(\d+)\s+addressed,\s*(\d+)\s+dismissed\s*$/.exec(String(criticIssues));
|
|
263
|
+
if (!match) {
|
|
264
|
+
violations.push("plan: header.critic_issues must match 'N found, M addressed, K dismissed'");
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
const [found, addressed, dismissed] = [match[1], match[2], match[3]].map((v) => parseInt(v, 10));
|
|
268
|
+
if (found < 1)
|
|
269
|
+
violations.push("plan: header.critic_issues must record at least 1 found issue");
|
|
270
|
+
if (addressed + dismissed !== found) {
|
|
271
|
+
violations.push("plan: header.critic_issues counts must satisfy addressed + dismissed == found");
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const tasks = data.tasks;
|
|
276
|
+
if (!Array.isArray(tasks))
|
|
277
|
+
return;
|
|
278
|
+
tasks.forEach((task, index) => {
|
|
279
|
+
if (isMapping(task))
|
|
280
|
+
validateField(violations, "plan", task, "acceptance", `tasks[${index}]`);
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
export function entryMinCount(schema, group) {
|
|
284
|
+
for (const entry of iterGroupEntries(schema, group)) {
|
|
285
|
+
if (entry.field === "entry" && entry.required)
|
|
286
|
+
return entry.min_count || 1;
|
|
287
|
+
}
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
export function parentRequirements(schema, parentGroup) {
|
|
291
|
+
const requirements = {};
|
|
292
|
+
const prefix = `${parentGroup}.`;
|
|
293
|
+
for (const group of Object.keys(schema)) {
|
|
294
|
+
if (SKIP_META.has(group))
|
|
295
|
+
continue;
|
|
296
|
+
for (const entry of iterGroupEntries(schema, group)) {
|
|
297
|
+
const parent = entry.parent;
|
|
298
|
+
if (typeof parent === "string" &&
|
|
299
|
+
parent.startsWith(prefix) &&
|
|
300
|
+
parent !== `${group}.entry` &&
|
|
301
|
+
entry.required &&
|
|
302
|
+
entry.field) {
|
|
303
|
+
const parentField = parent.slice(prefix.length);
|
|
304
|
+
(requirements[parentField] ??= []).push(entry.field);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return requirements;
|
|
309
|
+
}
|
|
310
|
+
export function entryRequirements(schema, group) {
|
|
311
|
+
const parent = `${group}.entry`;
|
|
312
|
+
const out = [];
|
|
313
|
+
for (const entry of iterGroupEntries(schema, group)) {
|
|
314
|
+
if (entry.parent === parent && entry.required && entry.field)
|
|
315
|
+
out.push(entry.field);
|
|
316
|
+
}
|
|
317
|
+
return out;
|
|
318
|
+
}
|
|
319
|
+
export function validateSequences(data, schema, name, violations) {
|
|
320
|
+
for (const [group, key] of Object.entries(SEQUENCE_KEYS_BY_ARTIFACT[name] ?? {})) {
|
|
321
|
+
const seq = data[key];
|
|
322
|
+
if (seq === null || seq === undefined)
|
|
323
|
+
continue;
|
|
324
|
+
if (!Array.isArray(seq)) {
|
|
325
|
+
violations.push(`${name}: '${key}' must be a list`);
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (seq.length === 0) {
|
|
329
|
+
violations.push(`${name}: '${key}' requires at least 1 entry`);
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
const childRequirements = parentRequirements(schema, group);
|
|
333
|
+
seq.forEach((item, index) => {
|
|
334
|
+
const p = `${key}[${index}]`;
|
|
335
|
+
if (!isMapping(item)) {
|
|
336
|
+
violations.push(`${name}: '${p}' must be a mapping`);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
validateRequiredFields(violations, name, schema, group, item, p);
|
|
340
|
+
for (const [parentField, childFields] of Object.entries(childRequirements)) {
|
|
341
|
+
const childScope = item[parentField];
|
|
342
|
+
if (!isMapping(childScope))
|
|
343
|
+
continue;
|
|
344
|
+
for (const childField of childFields) {
|
|
345
|
+
validateField(violations, name, childScope, childField, `${p}.${parentField}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
for (const [[parentGroup, childGroup], childKey] of NESTED_SEQUENCE_KEYS) {
|
|
349
|
+
if (parentGroup !== group)
|
|
350
|
+
continue;
|
|
351
|
+
const childSeq = item[childKey];
|
|
352
|
+
const minCount = entryMinCount(schema, childGroup);
|
|
353
|
+
if (minCount && (!Array.isArray(childSeq) || childSeq.length < minCount)) {
|
|
354
|
+
violations.push(`${name}: '${p}.${childKey}' requires at least ${minCount} entry`);
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
if (!Array.isArray(childSeq))
|
|
358
|
+
continue;
|
|
359
|
+
const required = entryRequirements(schema, childGroup);
|
|
360
|
+
childSeq.forEach((child, childIndex) => {
|
|
361
|
+
const childPath = `${p}.${childKey}[${childIndex}]`;
|
|
362
|
+
if (!isMapping(child)) {
|
|
363
|
+
violations.push(`${name}: '${childPath}' must be a mapping`);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
for (const field of required)
|
|
367
|
+
validateField(violations, name, child, field, childPath);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
export function validateDecisionAlternatives(data, name) {
|
|
374
|
+
const violations = [];
|
|
375
|
+
(data.decisions ?? []).forEach((decision, index) => {
|
|
376
|
+
if (!isMapping(decision))
|
|
377
|
+
return;
|
|
378
|
+
const alternatives = decision.alternatives ?? [];
|
|
379
|
+
if (!Array.isArray(alternatives))
|
|
380
|
+
return;
|
|
381
|
+
const chosen = alternatives.filter((alt) => isMapping(alt) && alt.status === "chosen");
|
|
382
|
+
if (chosen.length !== 1) {
|
|
383
|
+
violations.push(`${name}: 'decisions[${index}].alternatives' must have exactly one chosen entry`);
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
return violations;
|
|
387
|
+
}
|
|
388
|
+
export function validateDecisionSatisfaction(data, name) {
|
|
389
|
+
const violations = [];
|
|
390
|
+
const allowedStates = new Set(["open", "provisionally_satisfied", "user_confirmed_satisfied"]);
|
|
391
|
+
const entries = [
|
|
392
|
+
...(data.decisions ?? []).map((d, i) => [`decisions[${i}]`, d]),
|
|
393
|
+
...(data.archive ?? []).map((d, i) => [`archive[${i}]`, d]),
|
|
394
|
+
];
|
|
395
|
+
for (const [entryPath, decision] of entries) {
|
|
396
|
+
if (!isMapping(decision) || !("satisfaction" in decision))
|
|
397
|
+
continue;
|
|
398
|
+
const p = `${entryPath}.satisfaction`;
|
|
399
|
+
const satisfaction = decision.satisfaction;
|
|
400
|
+
if (!isMapping(satisfaction)) {
|
|
401
|
+
violations.push(`${name}: '${p}' must be a mapping`);
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
validateUnknownFields(violations, name, satisfaction, new Set(["state", "evidence", "user_confirmation"]), p);
|
|
405
|
+
const state = satisfaction.state;
|
|
406
|
+
if (typeof state !== "string" || !state.trim()) {
|
|
407
|
+
violations.push(`${name}: missing required field '${p}.state'`);
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
if (!allowedStates.has(state)) {
|
|
411
|
+
violations.push(`${name}: invalid value '${state}' for '${p}.state' (expected one of: open, provisionally_satisfied, user_confirmed_satisfied)`);
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
if (state === "provisionally_satisfied" && isEmptyRequired(satisfaction.evidence)) {
|
|
415
|
+
violations.push(`${name}: '${p}.evidence' is required for provisionally_satisfied`);
|
|
416
|
+
}
|
|
417
|
+
if (state === "user_confirmed_satisfied") {
|
|
418
|
+
const confirmation = satisfaction.user_confirmation;
|
|
419
|
+
if (confirmation === null || confirmation === undefined) {
|
|
420
|
+
violations.push(`${name}: '${p}.user_confirmation' is required for user_confirmed_satisfied`);
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
if (!isMapping(confirmation)) {
|
|
424
|
+
violations.push(`${name}: '${p}.user_confirmation' must be a mapping with confirmed_by and confirmed_at, got ${pyTypeName(confirmation)}`);
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
for (const field of ["confirmed_by", "confirmed_at"]) {
|
|
428
|
+
if (isEmptyRequired(confirmation[field])) {
|
|
429
|
+
violations.push(`${name}: missing required field '${p}.user_confirmation.${field}'`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return violations;
|
|
435
|
+
}
|
|
436
|
+
export function validationRuleSeverity(schema, rule) {
|
|
437
|
+
for (const groupKey of ["VALIDATION", "VALIDATION_RULES"]) {
|
|
438
|
+
for (const entry of Object.values(schema[groupKey] ?? {})) {
|
|
439
|
+
if (isMapping(entry) && entry.rule === rule) {
|
|
440
|
+
const severity = entry.severity;
|
|
441
|
+
return severity ? String(severity) : null;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
export function expectedSequenceOrder(name, key) {
|
|
448
|
+
return SEQUENCE_ORDER_BY_ARTIFACT[`${name}\0${key}`] ?? "ascending";
|
|
449
|
+
}
|
|
450
|
+
export function sequenceInOrder(nums, direction) {
|
|
451
|
+
const reverse = direction === "descending";
|
|
452
|
+
const sorted = [...nums].sort((a, b) => (reverse ? b - a : a - b));
|
|
453
|
+
return nums.length === sorted.length && nums.every((n, i) => n === sorted[i]);
|
|
454
|
+
}
|
|
455
|
+
//# sourceMappingURL=schema.js.map
|