@aragon-agent/cli 0.6.43 → 0.6.45
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/CHANGELOG.md +44 -0
- package/README.md +69 -2
- package/dist/agent/controller.d.ts +69 -0
- package/dist/agent/controller.js +224 -11
- package/dist/agent/governance-view.d.ts +40 -0
- package/dist/agent/governance-view.js +40 -0
- package/dist/agent/governance-wiring.d.ts +162 -0
- package/dist/agent/governance-wiring.js +470 -0
- package/dist/agent/plan-prompt.d.ts +7 -0
- package/dist/agent/plan-prompt.js +2 -1
- package/dist/agent/reducer.d.ts +33 -1
- package/dist/agent/reducer.js +45 -9
- package/dist/agent/system-prompt.d.ts +19 -0
- package/dist/agent/system-prompt.js +9 -1
- package/dist/cli.js +45 -1
- package/dist/commands/builtins.js +10 -4
- package/dist/commands/governance-command.d.ts +35 -0
- package/dist/commands/governance-command.js +292 -0
- package/dist/commands/governance-runs-table.d.ts +40 -0
- package/dist/commands/governance-runs-table.js +130 -0
- package/dist/commands/registry.d.ts +12 -0
- package/dist/config/app-paths.d.ts +10 -0
- package/dist/config/app-paths.js +12 -0
- package/dist/config/cli-commands.d.ts +18 -0
- package/dist/config/cli-commands.js +47 -1
- package/dist/config/env.d.ts +2 -1
- package/dist/config/env.js +10 -1
- package/dist/config/governance-config.d.ts +125 -0
- package/dist/config/governance-config.js +164 -0
- package/dist/config/governance-launch.d.ts +71 -0
- package/dist/config/governance-launch.js +58 -0
- package/dist/config/load.d.ts +15 -0
- package/dist/config/load.js +26 -1
- package/dist/config/model-profile-store.d.ts +4 -2
- package/dist/config/schema.d.ts +22 -0
- package/dist/config/schema.js +6 -0
- package/dist/config/store.js +14 -1
- package/dist/config/ui-state.d.ts +11 -0
- package/dist/config/ui-state.js +11 -0
- package/dist/diagnostics/doctor.js +20 -0
- package/dist/governance/card-normalize.d.ts +24 -0
- package/dist/governance/card-normalize.js +76 -0
- package/dist/governance/copy.d.ts +109 -0
- package/dist/governance/copy.js +223 -0
- package/dist/governance/engine/aftermath.d.ts +49 -0
- package/dist/governance/engine/aftermath.js +134 -0
- package/dist/governance/engine/artifact-persist.d.ts +35 -0
- package/dist/governance/engine/artifact-persist.js +86 -0
- package/dist/governance/engine/budget.d.ts +52 -0
- package/dist/governance/engine/budget.js +112 -0
- package/dist/governance/engine/cards.d.ts +18 -0
- package/dist/governance/engine/cards.js +186 -0
- package/dist/governance/engine/digest.d.ts +32 -0
- package/dist/governance/engine/digest.js +63 -0
- package/dist/governance/engine/engine-core.d.ts +219 -0
- package/dist/governance/engine/engine-core.js +562 -0
- package/dist/governance/engine/engine.d.ts +68 -0
- package/dist/governance/engine/engine.js +314 -0
- package/dist/governance/engine/estimate.d.ts +38 -0
- package/dist/governance/engine/estimate.js +46 -0
- package/dist/governance/engine/inspector.d.ts +16 -0
- package/dist/governance/engine/inspector.js +396 -0
- package/dist/governance/engine/integration.d.ts +35 -0
- package/dist/governance/engine/integration.js +220 -0
- package/dist/governance/engine/lifecycle.d.ts +29 -0
- package/dist/governance/engine/lifecycle.js +656 -0
- package/dist/governance/engine/pause.d.ts +56 -0
- package/dist/governance/engine/pause.js +107 -0
- package/dist/governance/engine/phase-members.d.ts +15 -0
- package/dist/governance/engine/phase-members.js +34 -0
- package/dist/governance/engine/phase-planner.d.ts +53 -0
- package/dist/governance/engine/phase-planner.js +282 -0
- package/dist/governance/engine/rebuild.d.ts +27 -0
- package/dist/governance/engine/rebuild.js +57 -0
- package/dist/governance/engine/report.d.ts +66 -0
- package/dist/governance/engine/report.js +220 -0
- package/dist/governance/engine/run-digest.d.ts +20 -0
- package/dist/governance/engine/run-digest.js +42 -0
- package/dist/governance/engine/spec-builder.d.ts +117 -0
- package/dist/governance/engine/spec-builder.js +321 -0
- package/dist/governance/engine/stage-runner.d.ts +22 -0
- package/dist/governance/engine/stage-runner.js +664 -0
- package/dist/governance/engine/start-input.d.ts +19 -0
- package/dist/governance/engine/start-input.js +111 -0
- package/dist/governance/engine/state-machine.d.ts +101 -0
- package/dist/governance/engine/state-machine.js +586 -0
- package/dist/governance/engine/submission-queries.d.ts +42 -0
- package/dist/governance/engine/submission-queries.js +97 -0
- package/dist/governance/engine/submission-render.d.ts +12 -0
- package/dist/governance/engine/submission-render.js +92 -0
- package/dist/governance/engine/submissions.d.ts +27 -0
- package/dist/governance/engine/submissions.js +455 -0
- package/dist/governance/engine/visibility.d.ts +104 -0
- package/dist/governance/engine/visibility.js +223 -0
- package/dist/governance/governed-dispatch.d.ts +19 -0
- package/dist/governance/governed-dispatch.js +17 -0
- package/dist/governance/limits.d.ts +107 -0
- package/dist/governance/limits.js +131 -0
- package/dist/governance/posture-transition.d.ts +38 -0
- package/dist/governance/posture-transition.js +44 -0
- package/dist/governance/posture.d.ts +35 -0
- package/dist/governance/posture.js +59 -0
- package/dist/governance/profiles.d.ts +48 -0
- package/dist/governance/profiles.js +140 -0
- package/dist/governance/prompts/orchestrator-block.d.ts +40 -0
- package/dist/governance/prompts/orchestrator-block.js +128 -0
- package/dist/governance/prompts/plan-addendum.d.ts +22 -0
- package/dist/governance/prompts/plan-addendum.js +60 -0
- package/dist/governance/prompts/refusals.d.ts +48 -0
- package/dist/governance/prompts/refusals.js +82 -0
- package/dist/governance/prompts/role-blocks.d.ts +40 -0
- package/dist/governance/prompts/role-blocks.js +255 -0
- package/dist/governance/report-reader.d.ts +31 -0
- package/dist/governance/report-reader.js +57 -0
- package/dist/governance/roles.d.ts +83 -0
- package/dist/governance/roles.js +142 -0
- package/dist/governance/rules/claims.d.ts +83 -0
- package/dist/governance/rules/claims.js +250 -0
- package/dist/governance/rules/elimination.d.ts +66 -0
- package/dist/governance/rules/elimination.js +105 -0
- package/dist/governance/rules/events-table.d.ts +46 -0
- package/dist/governance/rules/events-table.js +94 -0
- package/dist/governance/rules/failure-analysis.d.ts +28 -0
- package/dist/governance/rules/failure-analysis.js +93 -0
- package/dist/governance/rules/ledger-replay.d.ts +53 -0
- package/dist/governance/rules/ledger-replay.js +53 -0
- package/dist/governance/rules/ledger-state.d.ts +85 -0
- package/dist/governance/rules/ledger-state.js +175 -0
- package/dist/governance/rules/ledger.d.ts +228 -0
- package/dist/governance/rules/ledger.js +560 -0
- package/dist/governance/rules/rca.d.ts +68 -0
- package/dist/governance/rules/rca.js +220 -0
- package/dist/governance/rules/settlement.d.ts +129 -0
- package/dist/governance/rules/settlement.js +363 -0
- package/dist/governance/rules/suspicion.d.ts +66 -0
- package/dist/governance/rules/suspicion.js +169 -0
- package/dist/governance/store/artifacts.d.ts +81 -0
- package/dist/governance/store/artifacts.js +173 -0
- package/dist/governance/store/atomic.d.ts +79 -0
- package/dist/governance/store/atomic.js +119 -0
- package/dist/governance/store/events-log.d.ts +35 -0
- package/dist/governance/store/events-log.js +86 -0
- package/dist/governance/store/run-lock.d.ts +51 -0
- package/dist/governance/store/run-lock.js +122 -0
- package/dist/governance/store/run-store.d.ts +229 -0
- package/dist/governance/store/run-store.js +644 -0
- package/dist/governance/store/scoreboard-export.d.ts +53 -0
- package/dist/governance/store/scoreboard-export.js +65 -0
- package/dist/governance/tools/child-tools.d.ts +31 -0
- package/dist/governance/tools/child-tools.js +65 -0
- package/dist/governance/tools/guard.d.ts +71 -0
- package/dist/governance/tools/guard.js +194 -0
- package/dist/governance/tools/inspector-tools.d.ts +63 -0
- package/dist/governance/tools/inspector-tools.js +424 -0
- package/dist/governance/tools/lead-tools.d.ts +42 -0
- package/dist/governance/tools/lead-tools.js +226 -0
- package/dist/governance/tools/normalize-util.d.ts +101 -0
- package/dist/governance/tools/normalize-util.js +230 -0
- package/dist/governance/tools/scan-incomplete.d.ts +79 -0
- package/dist/governance/tools/scan-incomplete.js +486 -0
- package/dist/governance/tools/shared-tools.d.ts +44 -0
- package/dist/governance/tools/shared-tools.js +125 -0
- package/dist/governance/tools/submit-normalizers.d.ts +28 -0
- package/dist/governance/tools/submit-normalizers.js +474 -0
- package/dist/governance/tools/submit-tools.d.ts +26 -0
- package/dist/governance/tools/submit-tools.js +171 -0
- package/dist/governance/types.d.ts +661 -0
- package/dist/governance/types.js +27 -0
- package/dist/proc/kill-tree.d.ts +19 -1
- package/dist/proc/kill-tree.js +32 -5
- package/dist/proc/limits.d.ts +19 -0
- package/dist/proc/limits.js +19 -0
- package/dist/proc/supervisor.js +8 -1
- package/dist/session/validate-session.js +32 -1
- package/dist/team/runtime.d.ts +8 -0
- package/dist/team/runtime.js +9 -0
- package/dist/team/subagent.d.ts +23 -0
- package/dist/team/subagent.js +14 -3
- package/dist/team/types.d.ts +6 -0
- package/dist/tools/bounded-spawn.d.ts +56 -0
- package/dist/tools/bounded-spawn.js +76 -0
- package/dist/tools/index.d.ts +30 -7
- package/dist/tools/index.js +14 -0
- package/dist/tools/plan-tools.d.ts +14 -2
- package/dist/tools/plan-tools.js +5 -2
- package/dist/tools/search-tools.js +52 -44
- package/dist/ui/App.js +145 -25
- package/dist/ui/BottomStatusRow.js +1 -1
- package/dist/ui/Composer.d.ts +7 -1
- package/dist/ui/Composer.js +9 -6
- package/dist/ui/GovernancePanel.d.ts +50 -0
- package/dist/ui/GovernancePanel.js +77 -0
- package/dist/ui/GovernanceRun.d.ts +18 -0
- package/dist/ui/GovernanceRun.js +11 -0
- package/dist/ui/ScrollbackShell.d.ts +35 -7
- package/dist/ui/ScrollbackShell.js +1 -1
- package/dist/ui/StatusBar.d.ts +15 -2
- package/dist/ui/StatusBar.js +10 -4
- package/dist/ui/Transcript.js +3 -0
- package/dist/ui/entries/GovernanceCard.d.ts +30 -0
- package/dist/ui/entries/GovernanceCard.js +32 -0
- package/dist/ui/governance-copy.d.ts +69 -0
- package/dist/ui/governance-copy.js +104 -0
- package/dist/ui/governance-status.d.ts +145 -0
- package/dist/ui/governance-status.js +326 -0
- package/dist/ui/interaction-copy.d.ts +4 -0
- package/dist/ui/interaction-copy.js +5 -0
- package/dist/ui/interaction-hints.d.ts +17 -0
- package/dist/ui/interaction-hints.js +14 -2
- package/dist/ui/layout/governance-board.d.ts +121 -0
- package/dist/ui/layout/governance-board.js +382 -0
- package/dist/ui/layout/governance-card.d.ts +53 -0
- package/dist/ui/layout/governance-card.js +134 -0
- package/dist/ui/layout/governance-chip.d.ts +38 -0
- package/dist/ui/layout/governance-chip.js +64 -0
- package/dist/ui/layout/governance-panel.d.ts +85 -0
- package/dist/ui/layout/governance-panel.js +358 -0
- package/dist/ui/layout/governance-roster.d.ts +65 -0
- package/dist/ui/layout/governance-roster.js +275 -0
- package/dist/ui/layout/status-detail-layout.js +3 -3
- package/dist/ui/layout/status-layout.d.ts +5 -2
- package/dist/ui/layout/status-layout.js +10 -6
- package/dist/ui/layout/virtual-window.js +11 -0
- package/dist/ui/overlays/GovernanceBoardOverlay.d.ts +41 -0
- package/dist/ui/overlays/GovernanceBoardOverlay.js +76 -0
- package/dist/ui/overlays/GovernanceSettingsPage.d.ts +48 -0
- package/dist/ui/overlays/GovernanceSettingsPage.js +93 -0
- package/dist/ui/overlays/HelpOverlay.js +4 -3
- package/dist/ui/overlays/QuestionOverlay.d.ts +9 -1
- package/dist/ui/overlays/QuestionOverlay.js +4 -2
- package/dist/ui/overlays/SettingsScreen.d.ts +7 -0
- package/dist/ui/overlays/SettingsScreen.js +21 -1
- package/dist/ui/overlays/governance-settings.d.ts +79 -0
- package/dist/ui/overlays/governance-settings.js +140 -0
- package/dist/ui/overlays/model-profile-state.d.ts +6 -1
- package/dist/ui/overlays/model-profile-state.js +10 -0
- package/dist/ui/palettes.d.ts +17 -0
- package/dist/ui/palettes.js +9 -0
- package/dist/ui/scrollback/LiveTail.d.ts +3 -1
- package/dist/ui/scrollback/budget.d.ts +12 -6
- package/dist/ui/scrollback/budget.js +11 -4
- package/dist/ui/scrollback/live-preview.js +2 -0
- package/dist/ui/scrollback/settled-boundary.js +3 -0
- package/dist/ui/scrollback/tail-anchor.d.ts +5 -2
- package/dist/ui/scrollback/tail-anchor.js +3 -1
- package/dist/ui/shortcuts.js +2 -2
- package/dist/ui/theme.d.ts +7 -0
- package/dist/ui/theme.js +1 -0
- package/dist/ui/transcript-text.js +9 -0
- package/dist/ui/use-wheel-routing.js +3 -1
- package/package.json +2 -2
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizers for `gov_submit_work` / `gov_submit_review` / `gov_submit_test`
|
|
3
|
+
* (governance-mode 5.9, 7.1, 7.3).
|
|
4
|
+
*
|
|
5
|
+
* ASCII ONLY (`governance/` is glyph-scanned). Pure: no I/O, no clock.
|
|
6
|
+
*
|
|
7
|
+
* THE OUTPUT IS BUILT FROM KNOWN FIELDS ONLY. Each normalizer copies the keys it knows into
|
|
8
|
+
* a fresh object, so an `agentId` (or any other key) in the arguments can never reach the
|
|
9
|
+
* engine: identity is closed over by the tool factory, not argued by the model (5.9).
|
|
10
|
+
*
|
|
11
|
+
* EXPORTED FOR THE ENGINE. A submission that arrives by another path (resume, replay, a
|
|
12
|
+
* future REST surface) is validated by the same functions and gets the same verdict.
|
|
13
|
+
*
|
|
14
|
+
* CONTRADICTIONS ARE REJECTED, NOT GUESSED (D-15). A `PASS` verdict next to a blocker
|
|
15
|
+
* finding, or an `ALL_PASS` next to a `gap` row, is not repairable: either the verdict or
|
|
16
|
+
* the evidence is wrong and only the author knows which.
|
|
17
|
+
*/
|
|
18
|
+
import { GOV_LIMITS } from '../limits.js';
|
|
19
|
+
import { Issues, SUBMIT_LIMITS as L, isRecord, readBool, readInteger, } from './normalize-util.js';
|
|
20
|
+
const VERDICTS = ['PASS', 'PASS_WITH_FIXES', 'NEEDS_REWORK'];
|
|
21
|
+
const TEST_VERDICTS = [
|
|
22
|
+
'ALL_PASS',
|
|
23
|
+
'ISSUES_FOUND',
|
|
24
|
+
'SPEC_MISMATCH',
|
|
25
|
+
'INCOMPLETE_IMPL',
|
|
26
|
+
];
|
|
27
|
+
const SEVERITIES = ['blocker', 'major', 'minor', 'nit'];
|
|
28
|
+
const SEVERITY_ALIASES = {
|
|
29
|
+
critical: 'blocker',
|
|
30
|
+
high: 'major',
|
|
31
|
+
medium: 'minor',
|
|
32
|
+
low: 'nit',
|
|
33
|
+
};
|
|
34
|
+
const CHECK_STATUSES = ['pass', 'fail', 'na'];
|
|
35
|
+
const CHECK_STATUS_ALIASES = {
|
|
36
|
+
ok: 'pass',
|
|
37
|
+
passed: 'pass',
|
|
38
|
+
yes: 'pass',
|
|
39
|
+
failed: 'fail',
|
|
40
|
+
no: 'fail',
|
|
41
|
+
n_a: 'na',
|
|
42
|
+
not_applicable: 'na',
|
|
43
|
+
};
|
|
44
|
+
const TRACE_STATUSES = ['covered', 'gap', 'unverified'];
|
|
45
|
+
const TRACE_ALIASES = {
|
|
46
|
+
missing: 'gap',
|
|
47
|
+
not_verified: 'unverified',
|
|
48
|
+
};
|
|
49
|
+
const CLAIM_KINDS = [
|
|
50
|
+
'missed_by_review',
|
|
51
|
+
'security',
|
|
52
|
+
'false_positive',
|
|
53
|
+
'same_issue',
|
|
54
|
+
'incomplete_impl',
|
|
55
|
+
];
|
|
56
|
+
const VERIFIED_VALUES = ['YES', 'NOT_APPLICABLE'];
|
|
57
|
+
const VERIFIED_ALIASES = {
|
|
58
|
+
true: 'YES',
|
|
59
|
+
y: 'YES',
|
|
60
|
+
n_a: 'NOT_APPLICABLE',
|
|
61
|
+
na: 'NOT_APPLICABLE',
|
|
62
|
+
};
|
|
63
|
+
const DECISIONS = ['uphold', 'withdraw'];
|
|
64
|
+
const DECISION_ALIASES = {
|
|
65
|
+
upheld: 'uphold',
|
|
66
|
+
withdrawn: 'withdraw',
|
|
67
|
+
};
|
|
68
|
+
const CHECKLIST_KEYS = ['correctness', 'quality', 'security', 'integration', 'compliance'];
|
|
69
|
+
/** The args object, or `{}` with an error recorded. */
|
|
70
|
+
function argsOf(raw, issues, expected) {
|
|
71
|
+
if (isRecord(raw))
|
|
72
|
+
return raw;
|
|
73
|
+
issues.fail(`the arguments must be an object with ${expected}.`);
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
76
|
+
function readInterfaceContracts(raw, issues) {
|
|
77
|
+
const out = [];
|
|
78
|
+
issues.items(raw, 'interfaceContracts', L.contractsMax).forEach((item, i) => {
|
|
79
|
+
const at = `interfaceContracts[${i}]`;
|
|
80
|
+
if (!isRecord(item)) {
|
|
81
|
+
issues.fail(`${at} must be {interface, role, verified, method, peer}.`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const verified = issues.oneOf(item.verified, `${at}.verified`, VERIFIED_VALUES, {
|
|
85
|
+
aliases: VERIFIED_ALIASES,
|
|
86
|
+
required: true,
|
|
87
|
+
});
|
|
88
|
+
const role = issues.oneOf(item.role, `${at}.role`, ['PRODUCER', 'CONSUMER'], {
|
|
89
|
+
required: true,
|
|
90
|
+
});
|
|
91
|
+
out.push({
|
|
92
|
+
interface: issues.str(item.interface, `${at}.interface`, L.shortChars, true),
|
|
93
|
+
role: role ?? 'PRODUCER',
|
|
94
|
+
verified: verified ?? 'NOT_APPLICABLE',
|
|
95
|
+
method: issues.str(item.method, `${at}.method`, L.evidenceChars, verified === 'YES'),
|
|
96
|
+
peer: issues.str(item.peer, `${at}.peer`, L.shortChars),
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
function readVerification(raw, issues) {
|
|
102
|
+
const source = Array.isArray(raw) ? raw : isRecord(raw) ? raw.commands : undefined;
|
|
103
|
+
if (raw !== undefined && raw !== null && source === undefined) {
|
|
104
|
+
issues.fail('verification must be {commands: [{cmd, result, note?}]}.');
|
|
105
|
+
}
|
|
106
|
+
const commands = [];
|
|
107
|
+
issues.items(source, 'verification.commands', L.commandsMax).forEach((item, i) => {
|
|
108
|
+
const at = `verification.commands[${i}]`;
|
|
109
|
+
if (!isRecord(item)) {
|
|
110
|
+
issues.fail(`${at} must be {cmd, result, note?}.`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const note = issues.str(item.note, `${at}.note`, L.resultChars);
|
|
114
|
+
commands.push({
|
|
115
|
+
cmd: issues.str(item.cmd, `${at}.cmd`, L.commandChars, true),
|
|
116
|
+
result: issues.str(item.result, `${at}.result`, L.resultChars, true),
|
|
117
|
+
...(note !== '' ? { note } : {}),
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
return { commands };
|
|
121
|
+
}
|
|
122
|
+
function readAppeal(raw, issues) {
|
|
123
|
+
if (raw === undefined || raw === null)
|
|
124
|
+
return undefined;
|
|
125
|
+
if (!isRecord(raw)) {
|
|
126
|
+
issues.fail('appeal must be {findingIds: [..], argument}.');
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
const findingIds = issues.list(raw.findingIds, 'appeal.findingIds', L.findingIdsMax, L.idChars);
|
|
130
|
+
if (findingIds.length === 0)
|
|
131
|
+
issues.fail('appeal.findingIds must name at least one finding.');
|
|
132
|
+
const argument = issues.str(raw.argument, 'appeal.argument', L.argumentChars, true);
|
|
133
|
+
return { findingIds, argument };
|
|
134
|
+
}
|
|
135
|
+
/** Validate and repair a `gov_submit_work` argument object. Pure. */
|
|
136
|
+
export function normalizeWorkSubmission(raw) {
|
|
137
|
+
const issues = new Issues();
|
|
138
|
+
const args = argsOf(raw, issues, 'wpId and summary');
|
|
139
|
+
const wpId = issues.wpId(args.wpId);
|
|
140
|
+
const summary = issues.str(args.summary, 'summary', L.summaryChars, true);
|
|
141
|
+
const filesChanged = issues.list(args.filesChanged, 'filesChanged', L.filesMax, L.pathChars);
|
|
142
|
+
if (filesChanged.length === 0) {
|
|
143
|
+
issues.note('filesChanged is empty: say in the summary why no file needed to change.');
|
|
144
|
+
}
|
|
145
|
+
const appeal = readAppeal(args.appeal, issues);
|
|
146
|
+
const escalate = readBool(args.escalate);
|
|
147
|
+
return issues.result({
|
|
148
|
+
wpId,
|
|
149
|
+
summary,
|
|
150
|
+
filesChanged,
|
|
151
|
+
interfaceContracts: readInterfaceContracts(args.interfaceContracts, issues),
|
|
152
|
+
verification: readVerification(args.verification, issues),
|
|
153
|
+
...(appeal ? { appeal } : {}),
|
|
154
|
+
...(escalate !== undefined ? { escalate } : {}),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function readChecklistItem(raw, at, issues) {
|
|
158
|
+
if (!isRecord(raw)) {
|
|
159
|
+
issues.fail(`${at} must be {status: pass | fail | na, evidence}.`);
|
|
160
|
+
return { status: 'na', evidence: '' };
|
|
161
|
+
}
|
|
162
|
+
const status = issues.oneOf(raw.status, `${at}.status`, CHECK_STATUSES, {
|
|
163
|
+
aliases: CHECK_STATUS_ALIASES,
|
|
164
|
+
required: true,
|
|
165
|
+
}) ?? 'na';
|
|
166
|
+
const evidence = issues.str(raw.evidence, `${at}.evidence`, L.evidenceChars, status !== 'na');
|
|
167
|
+
return { status, evidence: status === 'na' && evidence === '' ? 'not applicable' : evidence };
|
|
168
|
+
}
|
|
169
|
+
function readChecklist(raw, issues) {
|
|
170
|
+
if (!isRecord(raw)) {
|
|
171
|
+
issues.fail('checklist is required: an object with correctness, quality, security, integration and ' +
|
|
172
|
+
'compliance, each {status, evidence}.');
|
|
173
|
+
}
|
|
174
|
+
const source = isRecord(raw) ? raw : {};
|
|
175
|
+
const out = {};
|
|
176
|
+
for (const key of CHECKLIST_KEYS) {
|
|
177
|
+
out[key] = readChecklistItem(source[key], `checklist.${key}`, issues);
|
|
178
|
+
}
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
181
|
+
function readFindings(raw, issues) {
|
|
182
|
+
const used = new Set();
|
|
183
|
+
const out = [];
|
|
184
|
+
issues.items(raw, 'findings', L.findingsMax).forEach((item, i) => {
|
|
185
|
+
const at = `findings[${i}]`;
|
|
186
|
+
if (!isRecord(item)) {
|
|
187
|
+
issues.fail(`${at} must be {id, severity, file, line?, issue, requiredChange, mustFix?}.`);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
let id = issues.str(item.id, `${at}.id`, L.idChars);
|
|
191
|
+
if (id === '' || used.has(id)) {
|
|
192
|
+
id = `F${i + 1}`;
|
|
193
|
+
issues.note(`${at} was given the id ${id}.`);
|
|
194
|
+
}
|
|
195
|
+
used.add(id);
|
|
196
|
+
const line = readInteger(item.line);
|
|
197
|
+
const mustFix = readBool(item.mustFix);
|
|
198
|
+
const severity = issues.oneOf(item.severity, `${at}.severity`, SEVERITIES, {
|
|
199
|
+
aliases: SEVERITY_ALIASES,
|
|
200
|
+
required: true,
|
|
201
|
+
});
|
|
202
|
+
out.push({
|
|
203
|
+
id,
|
|
204
|
+
severity: severity ?? 'minor',
|
|
205
|
+
file: issues.str(item.file, `${at}.file`, L.pathChars, true),
|
|
206
|
+
...(line !== undefined && line > 0 ? { line } : {}),
|
|
207
|
+
issue: issues.str(item.issue, `${at}.issue`, L.issueChars, true),
|
|
208
|
+
requiredChange: issues.str(item.requiredChange, `${at}.requiredChange`, L.issueChars, true),
|
|
209
|
+
...(mustFix !== undefined ? { mustFix } : {}),
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
return out;
|
|
213
|
+
}
|
|
214
|
+
function readFixes(raw, issues) {
|
|
215
|
+
const out = [];
|
|
216
|
+
issues.items(raw, 'fixesApplied', L.fixesMax).forEach((item, i) => {
|
|
217
|
+
const at = `fixesApplied[${i}]`;
|
|
218
|
+
if (!isRecord(item)) {
|
|
219
|
+
issues.fail(`${at} must be {file, rationale}.`);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
out.push({
|
|
223
|
+
file: issues.str(item.file, `${at}.file`, L.pathChars, true),
|
|
224
|
+
rationale: issues.str(item.rationale, `${at}.rationale`, L.rationaleChars, true),
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
return out;
|
|
228
|
+
}
|
|
229
|
+
function readAppealResponses(raw, issues) {
|
|
230
|
+
const out = [];
|
|
231
|
+
issues.items(raw, 'appealResponses', L.responsesMax).forEach((item, i) => {
|
|
232
|
+
const at = `appealResponses[${i}]`;
|
|
233
|
+
if (!isRecord(item)) {
|
|
234
|
+
issues.fail(`${at} must be {findingId, decision: uphold | withdraw, note?}.`);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const note = issues.str(item.note, `${at}.note`, L.rationaleChars);
|
|
238
|
+
const decision = issues.oneOf(item.decision, `${at}.decision`, DECISIONS, {
|
|
239
|
+
aliases: DECISION_ALIASES,
|
|
240
|
+
required: true,
|
|
241
|
+
});
|
|
242
|
+
out.push({
|
|
243
|
+
findingId: issues.str(item.findingId, `${at}.findingId`, L.idChars, true),
|
|
244
|
+
decision: decision ?? 'uphold',
|
|
245
|
+
...(note !== '' ? { note } : {}),
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
return out;
|
|
249
|
+
}
|
|
250
|
+
/** `{a, b}` where both are required text, or `undefined` when the field is absent. */
|
|
251
|
+
function readTextPair(raw, label, keys, issues) {
|
|
252
|
+
if (raw === undefined || raw === null)
|
|
253
|
+
return undefined;
|
|
254
|
+
if (!isRecord(raw)) {
|
|
255
|
+
issues.fail(`${label} must be {${keys[0]}, ${keys[1]}}.`);
|
|
256
|
+
return undefined;
|
|
257
|
+
}
|
|
258
|
+
const field = (key) => issues.str(raw[key], `${label}.${key}`, L.evidenceChars, true);
|
|
259
|
+
return { [keys[0]]: field(keys[0]), [keys[1]]: field(keys[1]) };
|
|
260
|
+
}
|
|
261
|
+
/** The only feedback surface a developer sees (D-7): the findings that are binding. */
|
|
262
|
+
function deriveRequiredChanges(findings) {
|
|
263
|
+
return findings
|
|
264
|
+
.filter((f) => f.mustFix === true || f.severity === 'blocker' || f.severity === 'major')
|
|
265
|
+
.map((f) => ({ file: f.file, issue: f.issue, change: f.requiredChange }));
|
|
266
|
+
}
|
|
267
|
+
/** Self-contradiction rules between the verdict and the rest of the review. */
|
|
268
|
+
function crossCheckReview(value, issues) {
|
|
269
|
+
const hasBlocker = value.findings.some((f) => f.severity === 'blocker');
|
|
270
|
+
const hasFailedItem = CHECKLIST_KEYS.some((k) => value.checklist[k].status === 'fail');
|
|
271
|
+
switch (value.verdict) {
|
|
272
|
+
case 'PASS':
|
|
273
|
+
if (value.requiredChanges.length > 0 || hasFailedItem) {
|
|
274
|
+
issues.fail('verdict PASS contradicts the evidence (a failed checklist item or a blocker, ' +
|
|
275
|
+
'major or mustFix finding). Use NEEDS_REWORK, or remove the finding.');
|
|
276
|
+
}
|
|
277
|
+
if (value.fixesApplied.length > 0) {
|
|
278
|
+
issues.fail('fixesApplied is not empty: a review that fixed files is PASS_WITH_FIXES.');
|
|
279
|
+
}
|
|
280
|
+
break;
|
|
281
|
+
case 'PASS_WITH_FIXES':
|
|
282
|
+
if (value.fixesApplied.length === 0) {
|
|
283
|
+
issues.fail('PASS_WITH_FIXES needs fixesApplied: one {file, rationale} per fixed file.');
|
|
284
|
+
}
|
|
285
|
+
if (hasBlocker) {
|
|
286
|
+
issues.fail('a blocker finding contradicts PASS_WITH_FIXES. Use NEEDS_REWORK.');
|
|
287
|
+
}
|
|
288
|
+
break;
|
|
289
|
+
case 'NEEDS_REWORK':
|
|
290
|
+
if (value.requiredChanges.length === 0) {
|
|
291
|
+
issues.fail('NEEDS_REWORK needs at least one finding with severity blocker or major, or ' +
|
|
292
|
+
'mustFix:true, each with a concrete requiredChange.');
|
|
293
|
+
}
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/** Validate and repair a `gov_submit_review` argument object. Pure. */
|
|
298
|
+
export function normalizeReviewSubmission(raw) {
|
|
299
|
+
const issues = new Issues();
|
|
300
|
+
const args = argsOf(raw, issues, 'wpId, verdict and checklist');
|
|
301
|
+
const wpId = issues.wpId(args.wpId);
|
|
302
|
+
const verdict = issues.oneOf(args.verdict, 'verdict', VERDICTS, {
|
|
303
|
+
aliases: { rework: 'NEEDS_REWORK' },
|
|
304
|
+
required: true,
|
|
305
|
+
});
|
|
306
|
+
const findings = readFindings(args.findings, issues);
|
|
307
|
+
const excellence = readTextPair(args.excellence, 'excellence', ['claim', 'evidence'], issues);
|
|
308
|
+
const security = readTextPair(args.securityFinding, 'securityFinding', ['description', 'evidence'], issues);
|
|
309
|
+
const value = {
|
|
310
|
+
wpId,
|
|
311
|
+
verdict: verdict ?? 'NEEDS_REWORK',
|
|
312
|
+
checklist: readChecklist(args.checklist, issues),
|
|
313
|
+
findings,
|
|
314
|
+
fixesApplied: readFixes(args.fixesApplied, issues),
|
|
315
|
+
integrationAssessment: issues.str(args.integrationAssessment, 'integrationAssessment', L.summaryChars, true),
|
|
316
|
+
appealResponses: readAppealResponses(args.appealResponses, issues),
|
|
317
|
+
requiredChanges: deriveRequiredChanges(findings),
|
|
318
|
+
...(excellence ? { excellence } : {}),
|
|
319
|
+
...(security ? { securityFinding: security } : {}),
|
|
320
|
+
};
|
|
321
|
+
if (verdict !== undefined)
|
|
322
|
+
crossCheckReview(value, issues);
|
|
323
|
+
return issues.result(value);
|
|
324
|
+
}
|
|
325
|
+
function readTraceability(raw, issues) {
|
|
326
|
+
const out = [];
|
|
327
|
+
issues.items(raw, 'traceability', L.traceMax).forEach((item, i) => {
|
|
328
|
+
const at = `traceability[${i}]`;
|
|
329
|
+
if (!isRecord(item)) {
|
|
330
|
+
issues.fail(`${at} must be {requirement, evidence, status: covered | gap | unverified}.`);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const status = issues.oneOf(item.status, `${at}.status`, TRACE_STATUSES, {
|
|
334
|
+
aliases: TRACE_ALIASES,
|
|
335
|
+
required: true,
|
|
336
|
+
}) ?? 'unverified';
|
|
337
|
+
out.push({
|
|
338
|
+
requirement: issues.str(item.requirement, `${at}.requirement`, L.issueChars, true),
|
|
339
|
+
evidence: issues.str(item.evidence, `${at}.evidence`, L.evidenceChars, status === 'covered'),
|
|
340
|
+
status,
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
return out;
|
|
344
|
+
}
|
|
345
|
+
function readTestCommands(raw, issues) {
|
|
346
|
+
const out = [];
|
|
347
|
+
issues.items(raw, 'commands', L.commandsMax).forEach((item, i) => {
|
|
348
|
+
const at = `commands[${i}]`;
|
|
349
|
+
if (!isRecord(item)) {
|
|
350
|
+
issues.fail(`${at} must be {cmd, exit, summary}.`);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
const exit = readInteger(item.exit);
|
|
354
|
+
if (exit === undefined)
|
|
355
|
+
issues.fail(`${at}.exit must be the integer exit code.`);
|
|
356
|
+
out.push({
|
|
357
|
+
cmd: issues.str(item.cmd, `${at}.cmd`, L.commandChars, true),
|
|
358
|
+
exit: exit ?? -1,
|
|
359
|
+
summary: issues.str(item.summary, `${at}.summary`, L.resultChars),
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
return out;
|
|
363
|
+
}
|
|
364
|
+
function readTestIssues(raw, issues) {
|
|
365
|
+
const out = [];
|
|
366
|
+
issues.items(raw, 'issues', L.issuesMax).forEach((item, i) => {
|
|
367
|
+
const at = `issues[${i}]`;
|
|
368
|
+
if (!isRecord(item)) {
|
|
369
|
+
issues.fail(`${at} must be {severity, description, missedByReview?, ...}.`);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const missed = readBool(item.missedByReview);
|
|
373
|
+
const justification = issues.str(item.reviewMissJustification, `${at}.reviewMissJustification`, L.rationaleChars, missed === true);
|
|
374
|
+
const severity = issues.oneOf(item.severity, `${at}.severity`, SEVERITIES, {
|
|
375
|
+
aliases: SEVERITY_ALIASES,
|
|
376
|
+
required: true,
|
|
377
|
+
});
|
|
378
|
+
out.push({
|
|
379
|
+
severity: severity ?? 'minor',
|
|
380
|
+
description: issues.str(item.description, `${at}.description`, L.issueChars, true),
|
|
381
|
+
...(missed !== undefined ? { missedByReview: missed } : {}),
|
|
382
|
+
...(justification !== '' ? { reviewMissJustification: justification } : {}),
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
return out;
|
|
386
|
+
}
|
|
387
|
+
function readClaims(raw, issues) {
|
|
388
|
+
const out = [];
|
|
389
|
+
issues.items(raw, 'claims', L.claimsMax).forEach((item, i) => {
|
|
390
|
+
const at = `claims[${i}]`;
|
|
391
|
+
if (!isRecord(item)) {
|
|
392
|
+
issues.fail(`${at} must be {kind, justification, against?}.`);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
const against = issues.str(item.against, `${at}.against`, L.idChars);
|
|
396
|
+
const kind = issues.oneOf(item.kind, `${at}.kind`, CLAIM_KINDS, { required: true });
|
|
397
|
+
out.push({
|
|
398
|
+
kind: kind ?? 'missed_by_review',
|
|
399
|
+
justification: issues.str(item.justification, `${at}.justification`, L.argumentChars, true),
|
|
400
|
+
...(against !== '' ? { against } : {}),
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
return out;
|
|
404
|
+
}
|
|
405
|
+
/** A ratio in [0, 1]; a value in (1, 100] is read as a percentage and rescaled with a note. */
|
|
406
|
+
function readSkeletonRatio(raw, issues) {
|
|
407
|
+
if (raw === undefined || raw === null || raw === '')
|
|
408
|
+
return undefined;
|
|
409
|
+
const n = typeof raw === 'number' ? raw : Number(String(raw).trim().replace(/%$/, ''));
|
|
410
|
+
if (!Number.isFinite(n) || n < 0 || n > 100) {
|
|
411
|
+
issues.fail('skeletonRatio must be a number between 0 and 1 (the gov_scan ratio).');
|
|
412
|
+
return undefined;
|
|
413
|
+
}
|
|
414
|
+
if (n > 1) {
|
|
415
|
+
issues.note(`skeletonRatio ${n} was read as a percentage and stored as ${n / 100}.`);
|
|
416
|
+
return n / 100;
|
|
417
|
+
}
|
|
418
|
+
return n;
|
|
419
|
+
}
|
|
420
|
+
/** Self-contradiction rules between the verdict and the rest of the test report. */
|
|
421
|
+
function crossCheckTest(value, issues) {
|
|
422
|
+
const hasGap = value.traceability.some((r) => r.status === 'gap');
|
|
423
|
+
switch (value.verdict) {
|
|
424
|
+
case 'ALL_PASS':
|
|
425
|
+
checkAllPass(value, hasGap, issues);
|
|
426
|
+
break;
|
|
427
|
+
case 'SPEC_MISMATCH':
|
|
428
|
+
if (!hasGap) {
|
|
429
|
+
issues.fail('SPEC_MISMATCH needs a traceability row with status gap naming the need.');
|
|
430
|
+
}
|
|
431
|
+
break;
|
|
432
|
+
case 'ISSUES_FOUND':
|
|
433
|
+
case 'INCOMPLETE_IMPL':
|
|
434
|
+
if (value.issues.length === 0) {
|
|
435
|
+
issues.fail(`${value.verdict} needs at least one entry in issues.`);
|
|
436
|
+
}
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
if ((value.skeletonRatio ?? 0) > GOV_LIMITS.skeletonRatioThreshold && !value.skeletonEvidence) {
|
|
440
|
+
issues.fail('skeletonRatio above the threshold needs skeletonEvidence (the gov_scan counts).');
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
function checkAllPass(value, hasGap, issues) {
|
|
444
|
+
const hasSerious = value.issues.some((x) => x.severity === 'blocker' || x.severity === 'major');
|
|
445
|
+
if (value.traceability.length === 0) {
|
|
446
|
+
issues.fail('ALL_PASS needs a traceability row for each requirement you verified.');
|
|
447
|
+
}
|
|
448
|
+
if (hasGap || hasSerious) {
|
|
449
|
+
issues.fail('ALL_PASS contradicts a gap row or a blocker/major issue. Use ISSUES_FOUND.');
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
/** Validate and repair a `gov_submit_test` argument object. Pure. */
|
|
453
|
+
export function normalizeTestSubmission(raw) {
|
|
454
|
+
const issues = new Issues();
|
|
455
|
+
const args = argsOf(raw, issues, 'wpId and verdict');
|
|
456
|
+
const wpId = issues.wpId(args.wpId);
|
|
457
|
+
const verdict = issues.oneOf(args.verdict, 'verdict', TEST_VERDICTS, { required: true });
|
|
458
|
+
const skeletonRatio = readSkeletonRatio(args.skeletonRatio, issues);
|
|
459
|
+
const skeletonEvidence = issues.str(args.skeletonEvidence, 'skeletonEvidence', L.evidenceChars);
|
|
460
|
+
const value = {
|
|
461
|
+
wpId,
|
|
462
|
+
verdict: verdict ?? 'ISSUES_FOUND',
|
|
463
|
+
traceability: readTraceability(args.traceability, issues),
|
|
464
|
+
commands: readTestCommands(args.commands, issues),
|
|
465
|
+
issues: readTestIssues(args.issues, issues),
|
|
466
|
+
boundaryTestsWritten: readBool(args.boundaryTestsWritten) ?? false,
|
|
467
|
+
claims: readClaims(args.claims, issues),
|
|
468
|
+
...(skeletonRatio !== undefined ? { skeletonRatio } : {}),
|
|
469
|
+
...(skeletonEvidence !== '' ? { skeletonEvidence } : {}),
|
|
470
|
+
};
|
|
471
|
+
if (verdict !== undefined)
|
|
472
|
+
crossCheckTest(value, issues);
|
|
473
|
+
return issues.result(value);
|
|
474
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `gov_submit_work` / `gov_submit_review` / `gov_submit_test` (governance-mode 7.1, 7.3).
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY (`governance/` is glyph-scanned).
|
|
5
|
+
*
|
|
6
|
+
* THE SCHEMAS DECLARE SHAPE, NOT POLICY (the `plan-tools.ts` P0-4 lesson): `type` and a
|
|
7
|
+
* minimal `required` stay; every bound lives in the `description` and is enforced by the
|
|
8
|
+
* normalizers in `submit-normalizers.ts`. `required` is kept to the one or two fields without
|
|
9
|
+
* which nothing can be routed (`wpId`, the verdict); everything else is checked by the
|
|
10
|
+
* normalizer so that a missing field produces a `NOT ACCEPTED:` text and not an executor
|
|
11
|
+
* validation ERROR (D-15: a tool-layer mistake is neither an error result nor a penalty).
|
|
12
|
+
*
|
|
13
|
+
* IDENTITY IS CLOSED OVER (5.9, 7.1 rule 4). Each factory binds the child's `Identity`; the
|
|
14
|
+
* model's arguments cannot name a different submitter because the normalizers copy only the
|
|
15
|
+
* fields they know and the engine call receives the bound identity.
|
|
16
|
+
*/
|
|
17
|
+
import { type AgentTool } from '@aragon-agent/core';
|
|
18
|
+
import type { GovernanceEngineApi, Identity } from '../types.js';
|
|
19
|
+
export * from './normalize-util.js';
|
|
20
|
+
export * from './submit-normalizers.js';
|
|
21
|
+
/** `gov_submit_work`: DEV (and the FIX developer). */
|
|
22
|
+
export declare function makeSubmitWorkTool(engine: GovernanceEngineApi, identity: Identity): AgentTool;
|
|
23
|
+
/** `gov_submit_review`: REV. One call per assigned work package. */
|
|
24
|
+
export declare function makeSubmitReviewTool(engine: GovernanceEngineApi, identity: Identity): AgentTool;
|
|
25
|
+
/** `gov_submit_test`: TEST. One call per assigned work package. */
|
|
26
|
+
export declare function makeSubmitTestTool(engine: GovernanceEngineApi, identity: Identity): AgentTool;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `gov_submit_work` / `gov_submit_review` / `gov_submit_test` (governance-mode 7.1, 7.3).
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY (`governance/` is glyph-scanned).
|
|
5
|
+
*
|
|
6
|
+
* THE SCHEMAS DECLARE SHAPE, NOT POLICY (the `plan-tools.ts` P0-4 lesson): `type` and a
|
|
7
|
+
* minimal `required` stay; every bound lives in the `description` and is enforced by the
|
|
8
|
+
* normalizers in `submit-normalizers.ts`. `required` is kept to the one or two fields without
|
|
9
|
+
* which nothing can be routed (`wpId`, the verdict); everything else is checked by the
|
|
10
|
+
* normalizer so that a missing field produces a `NOT ACCEPTED:` text and not an executor
|
|
11
|
+
* validation ERROR (D-15: a tool-layer mistake is neither an error result nor a penalty).
|
|
12
|
+
*
|
|
13
|
+
* IDENTITY IS CLOSED OVER (5.9, 7.1 rule 4). Each factory binds the child's `Identity`; the
|
|
14
|
+
* model's arguments cannot name a different submitter because the normalizers copy only the
|
|
15
|
+
* fields they know and the engine call receives the bound identity.
|
|
16
|
+
*/
|
|
17
|
+
import { textResult } from '@aragon-agent/core';
|
|
18
|
+
import { formatNotAccepted, withNotes } from './normalize-util.js';
|
|
19
|
+
import { normalizeReviewSubmission, normalizeTestSubmission, normalizeWorkSubmission, } from './submit-normalizers.js';
|
|
20
|
+
export * from './normalize-util.js';
|
|
21
|
+
export * from './submit-normalizers.js';
|
|
22
|
+
const TEXT = { type: 'string' };
|
|
23
|
+
const TEXT_LIST = { type: 'array', items: TEXT };
|
|
24
|
+
function textWith(description) {
|
|
25
|
+
return { type: 'string', description };
|
|
26
|
+
}
|
|
27
|
+
function objectOf(properties, description) {
|
|
28
|
+
return { type: 'object', properties, ...(description ? { description } : {}) };
|
|
29
|
+
}
|
|
30
|
+
function listOf(items, description) {
|
|
31
|
+
return { type: 'array', items, ...(description ? { description } : {}) };
|
|
32
|
+
}
|
|
33
|
+
const WORK_DESCRIPTION = 'Submit your finished work for ONE work package (call once per work package). Give wpId, ' +
|
|
34
|
+
'a summary, filesChanged, interfaceContracts (every interface you produce or consume, with ' +
|
|
35
|
+
'how you verified it) and the verification commands you ran with their results. To ' +
|
|
36
|
+
'dispute a rejection use appeal {findingIds, argument} instead of reworking (once per ' +
|
|
37
|
+
'work package); escalate:true takes a lost appeal to the Inspector. Over-long fields are ' +
|
|
38
|
+
'truncated. A NOT ACCEPTED answer lists what to fix and costs nothing.';
|
|
39
|
+
const REVIEW_DESCRIPTION = 'Submit your review of ONE work package (call once per work package). verdict: PASS, ' +
|
|
40
|
+
'PASS_WITH_FIXES (needs fixesApplied) or NEEDS_REWORK (needs a blocker or major finding, ' +
|
|
41
|
+
'or mustFix:true). checklist covers correctness, quality, security, integration and ' +
|
|
42
|
+
'compliance, each {status: pass | fail | na, evidence}. Each finding needs id, severity ' +
|
|
43
|
+
'(blocker | major | minor | nit), file, issue and a concrete requiredChange; the binding ' +
|
|
44
|
+
'ones are sent to the developer as the required changes. integrationAssessment must cite ' +
|
|
45
|
+
'contract names or signatures. A NOT ACCEPTED answer lists what to fix and costs nothing.';
|
|
46
|
+
const TEST_DESCRIPTION = 'Submit your test report for ONE work package (call once per work package). verdict: ' +
|
|
47
|
+
'ALL_PASS, ISSUES_FOUND, SPEC_MISMATCH (needs a gap row in traceability) or ' +
|
|
48
|
+
'INCOMPLETE_IMPL. traceability has one {requirement, evidence, status: covered | gap | ' +
|
|
49
|
+
'unverified} row per requirement; commands lists what you ran as {cmd, exit, summary}; ' +
|
|
50
|
+
'boundaryTestsWritten says whether you wrote cross-module tests. For stubs report ' +
|
|
51
|
+
'skeletonRatio and skeletonEvidence from gov_scan. claims {kind, justification, against?} ' +
|
|
52
|
+
'raises a claim the Inspector will rule on. A NOT ACCEPTED answer lists what to fix and ' +
|
|
53
|
+
'costs nothing.';
|
|
54
|
+
/** Run a normalizer, then forward the value to the engine exactly once on success. */
|
|
55
|
+
function runSubmission(toolName, normalized, forward) {
|
|
56
|
+
if (!normalized.ok)
|
|
57
|
+
return textResult(formatNotAccepted(toolName, normalized.errors));
|
|
58
|
+
return textResult(withNotes(forward(normalized.value), normalized.notes));
|
|
59
|
+
}
|
|
60
|
+
/** `gov_submit_work`: DEV (and the FIX developer). */
|
|
61
|
+
export function makeSubmitWorkTool(engine, identity) {
|
|
62
|
+
const contractItem = objectOf({
|
|
63
|
+
interface: TEXT,
|
|
64
|
+
role: textWith('PRODUCER | CONSUMER'),
|
|
65
|
+
verified: textWith('YES | NOT_APPLICABLE'),
|
|
66
|
+
method: textWith('How you verified it.'),
|
|
67
|
+
peer: textWith('The other side (work package or file).'),
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
name: 'gov_submit_work',
|
|
71
|
+
label: 'Submit work',
|
|
72
|
+
description: WORK_DESCRIPTION,
|
|
73
|
+
parameters: {
|
|
74
|
+
type: 'object',
|
|
75
|
+
properties: {
|
|
76
|
+
wpId: textWith('The work package this submission is for.'),
|
|
77
|
+
summary: textWith('What you built and how it meets the acceptance criteria.'),
|
|
78
|
+
filesChanged: { ...TEXT_LIST, description: 'Every file you created or changed.' },
|
|
79
|
+
interfaceContracts: listOf(contractItem, 'One entry per interface produced or consumed.'),
|
|
80
|
+
verification: objectOf({
|
|
81
|
+
commands: listOf(objectOf({ cmd: TEXT, result: TEXT, note: TEXT })),
|
|
82
|
+
}),
|
|
83
|
+
appeal: objectOf({ findingIds: TEXT_LIST, argument: TEXT }, 'Dispute a rejection instead of reworking (once per work package).'),
|
|
84
|
+
escalate: { type: 'boolean', description: 'Take a lost appeal to the Inspector.' },
|
|
85
|
+
},
|
|
86
|
+
required: ['wpId', 'summary'],
|
|
87
|
+
},
|
|
88
|
+
async execute(_id, params) {
|
|
89
|
+
return runSubmission('gov_submit_work', normalizeWorkSubmission(params), (value) => engine.submitWork(identity, value));
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** `gov_submit_review`: REV. One call per assigned work package. */
|
|
94
|
+
export function makeSubmitReviewTool(engine, identity) {
|
|
95
|
+
const checkItem = objectOf({ status: textWith('pass | fail | na'), evidence: TEXT });
|
|
96
|
+
const finding = objectOf({
|
|
97
|
+
id: TEXT,
|
|
98
|
+
severity: textWith('blocker | major | minor | nit'),
|
|
99
|
+
file: TEXT,
|
|
100
|
+
line: { type: 'number' },
|
|
101
|
+
issue: TEXT,
|
|
102
|
+
requiredChange: TEXT,
|
|
103
|
+
mustFix: { type: 'boolean' },
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
name: 'gov_submit_review',
|
|
107
|
+
label: 'Submit review',
|
|
108
|
+
description: REVIEW_DESCRIPTION,
|
|
109
|
+
parameters: {
|
|
110
|
+
type: 'object',
|
|
111
|
+
properties: {
|
|
112
|
+
wpId: textWith('The work package this review is for.'),
|
|
113
|
+
verdict: textWith('PASS | PASS_WITH_FIXES | NEEDS_REWORK'),
|
|
114
|
+
checklist: objectOf({
|
|
115
|
+
correctness: checkItem,
|
|
116
|
+
quality: checkItem,
|
|
117
|
+
security: checkItem,
|
|
118
|
+
integration: checkItem,
|
|
119
|
+
compliance: checkItem,
|
|
120
|
+
}),
|
|
121
|
+
findings: listOf(finding),
|
|
122
|
+
fixesApplied: listOf(objectOf({ file: TEXT, rationale: TEXT })),
|
|
123
|
+
integrationAssessment: TEXT,
|
|
124
|
+
appealResponses: listOf(objectOf({ findingId: TEXT, decision: textWith('uphold | withdraw'), note: TEXT })),
|
|
125
|
+
excellence: objectOf({ claim: TEXT, evidence: TEXT }, 'Optional: proactive excellence you observed.'),
|
|
126
|
+
securityFinding: objectOf({ description: TEXT, evidence: TEXT }, 'Optional: a security flaw, with evidence.'),
|
|
127
|
+
},
|
|
128
|
+
required: ['wpId', 'verdict'],
|
|
129
|
+
},
|
|
130
|
+
async execute(_id, params) {
|
|
131
|
+
return runSubmission('gov_submit_review', normalizeReviewSubmission(params), (value) => engine.submitReview(identity, value));
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/** `gov_submit_test`: TEST. One call per assigned work package. */
|
|
136
|
+
export function makeSubmitTestTool(engine, identity) {
|
|
137
|
+
const trace = objectOf({
|
|
138
|
+
requirement: TEXT,
|
|
139
|
+
evidence: TEXT,
|
|
140
|
+
status: textWith('covered | gap | unverified'),
|
|
141
|
+
});
|
|
142
|
+
const issue = objectOf({
|
|
143
|
+
severity: textWith('blocker | major | minor | nit'),
|
|
144
|
+
description: TEXT,
|
|
145
|
+
missedByReview: { type: 'boolean' },
|
|
146
|
+
reviewMissJustification: TEXT,
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
name: 'gov_submit_test',
|
|
150
|
+
label: 'Submit test report',
|
|
151
|
+
description: TEST_DESCRIPTION,
|
|
152
|
+
parameters: {
|
|
153
|
+
type: 'object',
|
|
154
|
+
properties: {
|
|
155
|
+
wpId: textWith('The work package this report is for.'),
|
|
156
|
+
verdict: textWith('ALL_PASS | ISSUES_FOUND | SPEC_MISMATCH | INCOMPLETE_IMPL'),
|
|
157
|
+
traceability: listOf(trace),
|
|
158
|
+
commands: listOf(objectOf({ cmd: TEXT, exit: { type: 'number' }, summary: TEXT })),
|
|
159
|
+
issues: listOf(issue),
|
|
160
|
+
boundaryTestsWritten: { type: 'boolean' },
|
|
161
|
+
skeletonRatio: { type: 'number', description: 'From gov_scan (0 to 1).' },
|
|
162
|
+
skeletonEvidence: TEXT,
|
|
163
|
+
claims: listOf(objectOf({ kind: TEXT, justification: TEXT, against: TEXT })),
|
|
164
|
+
},
|
|
165
|
+
required: ['wpId', 'verdict'],
|
|
166
|
+
},
|
|
167
|
+
async execute(_id, params) {
|
|
168
|
+
return runSubmission('gov_submit_test', normalizeTestSubmission(params), (value) => engine.submitTest(identity, value));
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|