@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,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Orchestrator's two tools, `gov_run` and `gov_dispatch` (governance-mode 7.2).
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY (`governance/` is glyph-scanned).
|
|
5
|
+
*
|
|
6
|
+
* THE SCHEMAS DECLARE SHAPE, NOT POLICY (the `plan-tools.ts` P0-4 lesson). `type`,
|
|
7
|
+
* `required` and `default` stay; every bound lives in the `description`, which is what the
|
|
8
|
+
* model reads, and is enforced by the engine's own validation. Putting `maxItems` or an
|
|
9
|
+
* `enum` in the schema would make behaviour depend on whether `ajv`, an OPTIONAL dependency
|
|
10
|
+
* of core, happened to install: with it a bad call is rejected by the executor as a tool
|
|
11
|
+
* ERROR (inviting an identical retry), without it the same call reaches the engine. One
|
|
12
|
+
* design, two behaviours.
|
|
13
|
+
*
|
|
14
|
+
* RESULTS ARE ALWAYS `textResult` (7.1 rule 2), aborts and partial failures included - an
|
|
15
|
+
* error result invites the most expensive possible reaction, a retry of the whole stage.
|
|
16
|
+
* The engine owns validation and the `NOT ACCEPTED:` / `NEXT:` vocabulary; these tools only
|
|
17
|
+
* route.
|
|
18
|
+
*/
|
|
19
|
+
import { textResult } from '@aragon-agent/core';
|
|
20
|
+
import { GOV_LIMITS } from '../limits.js';
|
|
21
|
+
/** The `gov_run` actions, in the order the description lists them. */
|
|
22
|
+
export const GOV_RUN_ACTIONS = [
|
|
23
|
+
'start',
|
|
24
|
+
'status',
|
|
25
|
+
'resume',
|
|
26
|
+
'close',
|
|
27
|
+
'abandon',
|
|
28
|
+
'resolve',
|
|
29
|
+
];
|
|
30
|
+
const GOV_RUN_DESCRIPTION = 'Define and manage the governed run. action: ' +
|
|
31
|
+
'"start" (needs goal, spec, workPackages; optional contracts, integrationCommands, ' +
|
|
32
|
+
'profile) creates the run and the phase plan; "status" prints the board and the legal ' +
|
|
33
|
+
'next actions; "resume" (optional runId) reloads the latest unfinished run; "close" ' +
|
|
34
|
+
'(optional summary) finishes a fully integrated run; "abandon" (needs reason) gives up the ' +
|
|
35
|
+
'whole run; "resolve" (needs escalationId and ruling, optional favor and disposition) ' +
|
|
36
|
+
'records the user\'s decision on an ESCALATION and can dispose of the escalated object ' +
|
|
37
|
+
'(disposition: retry, accept_as_is, drop or accept_failing, matching the escalation kind). ' +
|
|
38
|
+
`Sizes: goal <= ${GOV_LIMITS.goalChars} chars, spec <= ${GOV_LIMITS.specChars} chars; ` +
|
|
39
|
+
`each work package: id <= ${GOV_LIMITS.wpIdChars} chars, title <= ${GOV_LIMITS.wpTitleChars}, ` +
|
|
40
|
+
`goal <= ${GOV_LIMITS.wpGoalChars}, <= ${GOV_LIMITS.wpFilesMax} files (include its test ` +
|
|
41
|
+
`paths), <= ${GOV_LIMITS.wpAcceptanceMax} acceptance criteria of <= ` +
|
|
42
|
+
`${GOV_LIMITS.wpAcceptanceChars} chars; <= ${GOV_LIMITS.contractsMax} contracts; <= ` +
|
|
43
|
+
`${GOV_LIMITS.integrationCommandsMax} integration commands; ruling <= ` +
|
|
44
|
+
`${GOV_LIMITS.rulingChars} chars. Work package and phase counts are capped (never more ` +
|
|
45
|
+
`than ${GOV_LIMITS.hardMaxWorkPackages} work packages or ${GOV_LIMITS.hardMaxPhases} ` +
|
|
46
|
+
'phases); a call over a limit is answered with NOT ACCEPTED and the limit that applies.';
|
|
47
|
+
const GOV_DISPATCH_DESCRIPTION = 'Run ONE stage of the governed run with a team of agents and return a fixed-size ' +
|
|
48
|
+
'summary ending in NEXT. stage: dev, review, test, settle, integrate or fix. wps ' +
|
|
49
|
+
'(optional) narrows the stage to those work packages; omit it to take every eligible ' +
|
|
50
|
+
`one. notes maps a work package id to a short note (<= ${GOV_LIMITS.noteChars} chars) and ` +
|
|
51
|
+
'is accepted for dev and fix only; for other stages it is dropped and the result says so. ' +
|
|
52
|
+
'The engine builds every brief; do not relay review or test material. A stage can take ' +
|
|
53
|
+
'minutes. An illegal stage order is answered with NOT ACCEPTED and the legal NEXT calls.';
|
|
54
|
+
const WORK_PACKAGE_SCHEMA = {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties: {
|
|
57
|
+
id: { type: 'string', description: 'Short slug, e.g. "auth-api".' },
|
|
58
|
+
title: { type: 'string' },
|
|
59
|
+
goal: { type: 'string', description: 'What this work package must deliver.' },
|
|
60
|
+
files: {
|
|
61
|
+
type: 'array',
|
|
62
|
+
items: { type: 'string' },
|
|
63
|
+
description: 'Owned files and globs, INCLUDING this work package\'s test paths.',
|
|
64
|
+
},
|
|
65
|
+
acceptance: {
|
|
66
|
+
type: 'array',
|
|
67
|
+
items: { type: 'string' },
|
|
68
|
+
description: 'Verifiable acceptance criteria.',
|
|
69
|
+
},
|
|
70
|
+
dependsOn: {
|
|
71
|
+
type: 'array',
|
|
72
|
+
items: { type: 'string' },
|
|
73
|
+
description: 'Ids of work packages that must be integrated first.',
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
required: ['id', 'title', 'goal', 'files', 'acceptance'],
|
|
77
|
+
};
|
|
78
|
+
const CONTRACT_SCHEMA = {
|
|
79
|
+
type: 'object',
|
|
80
|
+
properties: {
|
|
81
|
+
id: { type: 'string' },
|
|
82
|
+
name: { type: 'string' },
|
|
83
|
+
producer: { type: 'string', description: 'Work package id that provides the interface.' },
|
|
84
|
+
consumers: { type: 'array', items: { type: 'string' } },
|
|
85
|
+
definition: { type: 'string', description: 'Exact signature or data shape.' },
|
|
86
|
+
},
|
|
87
|
+
required: ['id', 'name', 'producer', 'consumers', 'definition'],
|
|
88
|
+
};
|
|
89
|
+
/** Text returned when the tool call's signal was already aborted at entry. */
|
|
90
|
+
export const DISPATCH_ABORTED_AT_ENTRY = 'PAUSED: the call was interrupted before the stage started, so nothing was dispatched.\n' +
|
|
91
|
+
'NEXT: gov_run {action:"resume"} to continue, or gov_run {action:"abandon", reason:"..."} ' +
|
|
92
|
+
'to stop the run.';
|
|
93
|
+
/** Text returned when a stage was aborted while the engine was running it. */
|
|
94
|
+
function dispatchAbortedMidway() {
|
|
95
|
+
return ('PAUSED: the stage was interrupted. Completed submissions were kept.\n' +
|
|
96
|
+
'NEXT: gov_run {action:"resume"} to continue, or gov_run {action:"abandon", ' +
|
|
97
|
+
'reason:"..."} to stop the run.');
|
|
98
|
+
}
|
|
99
|
+
function recordOf(value) {
|
|
100
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
101
|
+
? value
|
|
102
|
+
: {};
|
|
103
|
+
}
|
|
104
|
+
function text(value) {
|
|
105
|
+
return typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined;
|
|
106
|
+
}
|
|
107
|
+
function notAccepted(reason) {
|
|
108
|
+
return textResult(`NOT ACCEPTED: ${reason}`);
|
|
109
|
+
}
|
|
110
|
+
async function runAction(engine, action, params, signal) {
|
|
111
|
+
// The engine validates payload shape; `action` is routing only and must not leak into it.
|
|
112
|
+
const payload = { ...params };
|
|
113
|
+
delete payload.action;
|
|
114
|
+
switch (action) {
|
|
115
|
+
case 'start':
|
|
116
|
+
return textResult(await engine.start(payload, signal));
|
|
117
|
+
case 'status':
|
|
118
|
+
return textResult(engine.status());
|
|
119
|
+
case 'resume':
|
|
120
|
+
return textResult(await engine.resume(text(params.runId)));
|
|
121
|
+
case 'close':
|
|
122
|
+
return textResult(await engine.close(text(params.summary)));
|
|
123
|
+
case 'abandon': {
|
|
124
|
+
const reason = text(params.reason);
|
|
125
|
+
if (!reason)
|
|
126
|
+
return notAccepted('abandon needs a reason (a short sentence).');
|
|
127
|
+
return textResult(await engine.abandon(reason.slice(0, GOV_LIMITS.reasonChars)));
|
|
128
|
+
}
|
|
129
|
+
case 'resolve':
|
|
130
|
+
return textResult(await engine.resolve(payload));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* `gov_run`: start, inspect, resume, close, abandon and resolve a governed run.
|
|
135
|
+
*
|
|
136
|
+
* An unknown `action` is a non-error `NOT ACCEPTED` that lists the valid ones: the model
|
|
137
|
+
* guessed a verb, and a list repairs that in one turn.
|
|
138
|
+
*/
|
|
139
|
+
export function makeGovRunTool(engine) {
|
|
140
|
+
return {
|
|
141
|
+
name: 'gov_run',
|
|
142
|
+
label: 'Governed run',
|
|
143
|
+
description: GOV_RUN_DESCRIPTION,
|
|
144
|
+
parameters: {
|
|
145
|
+
type: 'object',
|
|
146
|
+
properties: {
|
|
147
|
+
action: { type: 'string', description: GOV_RUN_ACTIONS.join(' | ') },
|
|
148
|
+
goal: { type: 'string', description: 'One-sentence goal (start).' },
|
|
149
|
+
spec: { type: 'string', description: 'The complete requirement or approved plan (start).' },
|
|
150
|
+
profile: { type: 'string', description: 'general | ml | product | api-security (start).' },
|
|
151
|
+
workPackages: { type: 'array', items: WORK_PACKAGE_SCHEMA, description: 'start only.' },
|
|
152
|
+
contracts: { type: 'array', items: CONTRACT_SCHEMA, description: 'Shared interfaces.' },
|
|
153
|
+
integrationCommands: {
|
|
154
|
+
type: 'array',
|
|
155
|
+
items: { type: 'string' },
|
|
156
|
+
description: 'Test commands the inspector may run (start).',
|
|
157
|
+
},
|
|
158
|
+
runId: { type: 'string', description: 'resume: a specific run id.' },
|
|
159
|
+
summary: { type: 'string', description: 'close: closing note.' },
|
|
160
|
+
reason: { type: 'string', description: 'abandon: why.' },
|
|
161
|
+
escalationId: { type: 'string', description: 'resolve: the ESC id.' },
|
|
162
|
+
ruling: { type: 'string', description: 'resolve: the user\'s ruling.' },
|
|
163
|
+
favor: { type: 'string', description: 'resolve: whom the ruling favors.' },
|
|
164
|
+
disposition: {
|
|
165
|
+
type: 'string',
|
|
166
|
+
description: 'resolve: retry | accept_as_is | drop | accept_failing.',
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
required: ['action'],
|
|
170
|
+
},
|
|
171
|
+
async execute(_id, params, ctx) {
|
|
172
|
+
const args = recordOf(params);
|
|
173
|
+
const action = text(args.action)?.toLowerCase();
|
|
174
|
+
if (!action || !GOV_RUN_ACTIONS.includes(action)) {
|
|
175
|
+
return notAccepted(`unknown action ${JSON.stringify(args.action ?? null)}. Valid actions: ` +
|
|
176
|
+
`${GOV_RUN_ACTIONS.join(', ')}.`);
|
|
177
|
+
}
|
|
178
|
+
return runAction(engine, action, args, ctx.signal);
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* `gov_dispatch`: run one stage.
|
|
184
|
+
*
|
|
185
|
+
* ABORT HANDLING (D-21). The executor timer is disabled for this tool (`gov_dispatch: 0`),
|
|
186
|
+
* which is safe ONLY because the tool honours `ctx.signal`: (1) a signal that is already
|
|
187
|
+
* aborted returns the paused text at once, without touching the engine; (2) the signal is
|
|
188
|
+
* handed to the engine so the stage can stop; (3) if the engine rejects after an abort, that
|
|
189
|
+
* is a pause, not a tool failure. Any other rejection is a real defect and propagates.
|
|
190
|
+
*/
|
|
191
|
+
export function makeGovDispatchTool(engine) {
|
|
192
|
+
return {
|
|
193
|
+
name: 'gov_dispatch',
|
|
194
|
+
label: 'Dispatch governed stage',
|
|
195
|
+
description: GOV_DISPATCH_DESCRIPTION,
|
|
196
|
+
parameters: {
|
|
197
|
+
type: 'object',
|
|
198
|
+
properties: {
|
|
199
|
+
stage: { type: 'string', description: 'dev | review | test | settle | integrate | fix' },
|
|
200
|
+
wps: {
|
|
201
|
+
type: 'array',
|
|
202
|
+
items: { type: 'string' },
|
|
203
|
+
description: 'Work package ids to include. Omit for every eligible one.',
|
|
204
|
+
},
|
|
205
|
+
notes: {
|
|
206
|
+
type: 'object',
|
|
207
|
+
description: 'Map of work package id to a short note (dev and fix only).',
|
|
208
|
+
additionalProperties: { type: 'string' },
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
required: ['stage'],
|
|
212
|
+
},
|
|
213
|
+
async execute(_id, params, ctx) {
|
|
214
|
+
if (ctx.signal?.aborted)
|
|
215
|
+
return textResult(DISPATCH_ABORTED_AT_ENTRY);
|
|
216
|
+
try {
|
|
217
|
+
return textResult(await engine.dispatchStage(recordOf(params), ctx.signal));
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
if (ctx.signal?.aborted)
|
|
221
|
+
return textResult(dispatchAbortedMidway());
|
|
222
|
+
throw err;
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coercion helpers shared by every governance tool normalizer (governance-mode 5.9, 7.1).
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY (`governance/` is glyph-scanned). Pure: no I/O, no clock.
|
|
5
|
+
*
|
|
6
|
+
* REPAIR FIRST, REJECT SECOND (the `normalizeQuestions` discipline). A value the model got
|
|
7
|
+
* slightly wrong - an upper-case verdict, a status spelled `n/a`, a single path where a list
|
|
8
|
+
* was expected, an over-long summary - is fixed and accepted, with a NOTE saying what was
|
|
9
|
+
* changed. Only what cannot be repaired is an ERROR, and every error is collected before the
|
|
10
|
+
* answer is built, so one `NOT ACCEPTED:` text names ALL the problems (one round trip, not
|
|
11
|
+
* one per mistake).
|
|
12
|
+
*
|
|
13
|
+
* A REJECTED SUBMISSION IS NEVER A PENALTY (D-15): a tool-layer mistake is not a character
|
|
14
|
+
* flaw. It is also never an error result - that invites an identical retry loop.
|
|
15
|
+
*/
|
|
16
|
+
/** Marker appended to any clipped field. Stable: tests and models rely on it. */
|
|
17
|
+
export declare const TRUNCATION_MARK = "[truncated]";
|
|
18
|
+
/**
|
|
19
|
+
* Per-field bounds for submissions. STRUCTURAL (what the store and the digests can carry),
|
|
20
|
+
* enforced by clipping here and never declared in a schema (7.1 rule 3). The worst-case sum
|
|
21
|
+
* is far above `GOV_LIMITS.submissionBytes`, which is re-checked on the normalized result.
|
|
22
|
+
*/
|
|
23
|
+
export declare const SUBMIT_LIMITS: {
|
|
24
|
+
readonly summaryChars: 2000;
|
|
25
|
+
readonly evidenceChars: 800;
|
|
26
|
+
readonly issueChars: 800;
|
|
27
|
+
readonly rationaleChars: 600;
|
|
28
|
+
readonly argumentChars: 1200;
|
|
29
|
+
readonly pathChars: 300;
|
|
30
|
+
readonly commandChars: 300;
|
|
31
|
+
readonly resultChars: 600;
|
|
32
|
+
readonly shortChars: 200;
|
|
33
|
+
readonly idChars: 40;
|
|
34
|
+
readonly filesMax: 200;
|
|
35
|
+
readonly contractsMax: 24;
|
|
36
|
+
readonly commandsMax: 20;
|
|
37
|
+
readonly findingsMax: 30;
|
|
38
|
+
readonly fixesMax: 30;
|
|
39
|
+
readonly responsesMax: 30;
|
|
40
|
+
readonly traceMax: 60;
|
|
41
|
+
readonly issuesMax: 30;
|
|
42
|
+
readonly claimsMax: 5;
|
|
43
|
+
readonly findingIdsMax: 30;
|
|
44
|
+
};
|
|
45
|
+
/** How many problems one NOT ACCEPTED answer lists (the rest are counted, not printed). */
|
|
46
|
+
export declare const ERRORS_SHOWN = 8;
|
|
47
|
+
export type Normalized<T> = {
|
|
48
|
+
ok: true;
|
|
49
|
+
value: T;
|
|
50
|
+
notes: string[];
|
|
51
|
+
} | {
|
|
52
|
+
ok: false;
|
|
53
|
+
errors: string[];
|
|
54
|
+
};
|
|
55
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
56
|
+
/** Clip to `max` characters, marker included, so the result never exceeds `max`. */
|
|
57
|
+
export declare function clipText(text: string, max: number): {
|
|
58
|
+
text: string;
|
|
59
|
+
clipped: boolean;
|
|
60
|
+
};
|
|
61
|
+
/** `true` / `false` or their string spellings; anything else is "not given". */
|
|
62
|
+
export declare function readBool(raw: unknown): boolean | undefined;
|
|
63
|
+
/** An integer, or a string of digits read as one; anything else is "not given". */
|
|
64
|
+
export declare function readInteger(raw: unknown): number | undefined;
|
|
65
|
+
export interface OneOfOptions<T extends string> {
|
|
66
|
+
/** Extra spellings, keyed by their `enumKey` form (lower case, underscores). */
|
|
67
|
+
aliases?: Readonly<Record<string, T>>;
|
|
68
|
+
required?: boolean;
|
|
69
|
+
}
|
|
70
|
+
/** Collects what cannot be repaired (`errors`) and what was repaired (`notes`). */
|
|
71
|
+
export declare class Issues {
|
|
72
|
+
readonly errors: string[];
|
|
73
|
+
readonly notes: string[];
|
|
74
|
+
fail(message: string): void;
|
|
75
|
+
note(message: string): void;
|
|
76
|
+
/**
|
|
77
|
+
* Close the collection: the value on success, every error on failure. The serialized size
|
|
78
|
+
* is re-checked here because the per-field clips alone do not bound the whole.
|
|
79
|
+
*/
|
|
80
|
+
result<T>(value: T): Normalized<T>;
|
|
81
|
+
/**
|
|
82
|
+
* A text field. Numbers and booleans are stringified (a model that sends `exit: 0` where
|
|
83
|
+
* text was expected means 0); objects and arrays are not text and are rejected.
|
|
84
|
+
*/
|
|
85
|
+
str(raw: unknown, label: string, max: number, required?: boolean): string;
|
|
86
|
+
/**
|
|
87
|
+
* A work package id: letters, digits, `.`, `_`, `-`, at most `GOV_LIMITS.wpIdChars`.
|
|
88
|
+
* Never clipped (a clipped id names a different work package); an unusable one is an error.
|
|
89
|
+
*/
|
|
90
|
+
wpId(raw: unknown, label?: string, required?: boolean): string;
|
|
91
|
+
/** A list of short strings. A single string is a list (newline-separated: several). */
|
|
92
|
+
list(raw: unknown, label: string, maxItems: number, maxChars: number): string[];
|
|
93
|
+
/** A list of records. A single object is a one-item list; anything else is an error. */
|
|
94
|
+
items(raw: unknown, label: string, maxItems: number): unknown[];
|
|
95
|
+
/** One member of `allowed`, matched leniently (case, camelCase, spaces, hyphens, aliases). */
|
|
96
|
+
oneOf<T extends string>(raw: unknown, label: string, allowed: readonly T[], options?: OneOfOptions<T>): T | undefined;
|
|
97
|
+
}
|
|
98
|
+
/** `NOT ACCEPTED:` text for a failed normalization. Never an error result, never a penalty. */
|
|
99
|
+
export declare function formatNotAccepted(tool: string, errors: readonly string[]): string;
|
|
100
|
+
/** Append the repair notes (what was clipped, rescaled, renumbered) to an engine answer. */
|
|
101
|
+
export declare function withNotes(text: string, notes: readonly string[]): string;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coercion helpers shared by every governance tool normalizer (governance-mode 5.9, 7.1).
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY (`governance/` is glyph-scanned). Pure: no I/O, no clock.
|
|
5
|
+
*
|
|
6
|
+
* REPAIR FIRST, REJECT SECOND (the `normalizeQuestions` discipline). A value the model got
|
|
7
|
+
* slightly wrong - an upper-case verdict, a status spelled `n/a`, a single path where a list
|
|
8
|
+
* was expected, an over-long summary - is fixed and accepted, with a NOTE saying what was
|
|
9
|
+
* changed. Only what cannot be repaired is an ERROR, and every error is collected before the
|
|
10
|
+
* answer is built, so one `NOT ACCEPTED:` text names ALL the problems (one round trip, not
|
|
11
|
+
* one per mistake).
|
|
12
|
+
*
|
|
13
|
+
* A REJECTED SUBMISSION IS NEVER A PENALTY (D-15): a tool-layer mistake is not a character
|
|
14
|
+
* flaw. It is also never an error result - that invites an identical retry loop.
|
|
15
|
+
*/
|
|
16
|
+
import { GOV_LIMITS } from '../limits.js';
|
|
17
|
+
/** Marker appended to any clipped field. Stable: tests and models rely on it. */
|
|
18
|
+
export const TRUNCATION_MARK = '[truncated]';
|
|
19
|
+
/**
|
|
20
|
+
* Per-field bounds for submissions. STRUCTURAL (what the store and the digests can carry),
|
|
21
|
+
* enforced by clipping here and never declared in a schema (7.1 rule 3). The worst-case sum
|
|
22
|
+
* is far above `GOV_LIMITS.submissionBytes`, which is re-checked on the normalized result.
|
|
23
|
+
*/
|
|
24
|
+
export const SUBMIT_LIMITS = {
|
|
25
|
+
summaryChars: 2_000,
|
|
26
|
+
evidenceChars: 800,
|
|
27
|
+
issueChars: 800,
|
|
28
|
+
rationaleChars: 600,
|
|
29
|
+
argumentChars: 1_200,
|
|
30
|
+
pathChars: 300,
|
|
31
|
+
commandChars: 300,
|
|
32
|
+
resultChars: 600,
|
|
33
|
+
shortChars: 200,
|
|
34
|
+
idChars: 40,
|
|
35
|
+
filesMax: 200,
|
|
36
|
+
contractsMax: 24,
|
|
37
|
+
commandsMax: 20,
|
|
38
|
+
findingsMax: 30,
|
|
39
|
+
fixesMax: 30,
|
|
40
|
+
responsesMax: 30,
|
|
41
|
+
traceMax: 60,
|
|
42
|
+
issuesMax: 30,
|
|
43
|
+
claimsMax: 5,
|
|
44
|
+
findingIdsMax: 30,
|
|
45
|
+
};
|
|
46
|
+
const WP_ID_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
47
|
+
/** How many problems one NOT ACCEPTED answer lists (the rest are counted, not printed). */
|
|
48
|
+
export const ERRORS_SHOWN = 8;
|
|
49
|
+
export function isRecord(value) {
|
|
50
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
51
|
+
}
|
|
52
|
+
/** Clip to `max` characters, marker included, so the result never exceeds `max`. */
|
|
53
|
+
export function clipText(text, max) {
|
|
54
|
+
if (text.length <= max)
|
|
55
|
+
return { text, clipped: false };
|
|
56
|
+
const keep = Math.max(0, max - TRUNCATION_MARK.length - 1);
|
|
57
|
+
return { text: `${text.slice(0, keep)} ${TRUNCATION_MARK}`, clipped: true };
|
|
58
|
+
}
|
|
59
|
+
/** `true` / `false` or their string spellings; anything else is "not given". */
|
|
60
|
+
export function readBool(raw) {
|
|
61
|
+
if (typeof raw === 'boolean')
|
|
62
|
+
return raw;
|
|
63
|
+
if (typeof raw === 'string') {
|
|
64
|
+
const t = raw.trim().toLowerCase();
|
|
65
|
+
if (t === 'true' || t === 'yes')
|
|
66
|
+
return true;
|
|
67
|
+
if (t === 'false' || t === 'no')
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
/** An integer, or a string of digits read as one; anything else is "not given". */
|
|
73
|
+
export function readInteger(raw) {
|
|
74
|
+
if (typeof raw === 'number' && Number.isInteger(raw))
|
|
75
|
+
return raw;
|
|
76
|
+
if (typeof raw === 'string' && /^-?\d+$/.test(raw.trim()))
|
|
77
|
+
return Number(raw.trim());
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
/** Case-, camelCase-, space-, slash- and hyphen-insensitive key for enum matching. */
|
|
81
|
+
function enumKey(text) {
|
|
82
|
+
return text
|
|
83
|
+
.trim()
|
|
84
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1_$2')
|
|
85
|
+
.toLowerCase()
|
|
86
|
+
.replace(/[\s\-/]+/g, '_');
|
|
87
|
+
}
|
|
88
|
+
/** Collects what cannot be repaired (`errors`) and what was repaired (`notes`). */
|
|
89
|
+
export class Issues {
|
|
90
|
+
errors = [];
|
|
91
|
+
notes = [];
|
|
92
|
+
fail(message) {
|
|
93
|
+
this.errors.push(message);
|
|
94
|
+
}
|
|
95
|
+
note(message) {
|
|
96
|
+
this.notes.push(message);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Close the collection: the value on success, every error on failure. The serialized size
|
|
100
|
+
* is re-checked here because the per-field clips alone do not bound the whole.
|
|
101
|
+
*/
|
|
102
|
+
result(value) {
|
|
103
|
+
if (this.errors.length > 0)
|
|
104
|
+
return { ok: false, errors: this.errors };
|
|
105
|
+
const bytes = Buffer.byteLength(JSON.stringify(value), 'utf8');
|
|
106
|
+
if (bytes > GOV_LIMITS.submissionBytes) {
|
|
107
|
+
return {
|
|
108
|
+
ok: false,
|
|
109
|
+
errors: [
|
|
110
|
+
`the submission is ${bytes} bytes; the limit is ${GOV_LIMITS.submissionBytes}. ` +
|
|
111
|
+
'Shorten summaries, evidence and lists.',
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return { ok: true, value, notes: this.notes };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* A text field. Numbers and booleans are stringified (a model that sends `exit: 0` where
|
|
119
|
+
* text was expected means 0); objects and arrays are not text and are rejected.
|
|
120
|
+
*/
|
|
121
|
+
str(raw, label, max, required = false) {
|
|
122
|
+
let text = '';
|
|
123
|
+
let wrongType = false;
|
|
124
|
+
if (typeof raw === 'string')
|
|
125
|
+
text = raw.trim();
|
|
126
|
+
else if (typeof raw === 'number' || typeof raw === 'boolean')
|
|
127
|
+
text = String(raw);
|
|
128
|
+
else if (raw !== undefined && raw !== null)
|
|
129
|
+
wrongType = true;
|
|
130
|
+
if (wrongType)
|
|
131
|
+
this.fail(`${label} must be text.`);
|
|
132
|
+
else if (text === '' && required) {
|
|
133
|
+
this.fail(`${label} ${raw === undefined || raw === null ? 'is required' : 'is empty'}.`);
|
|
134
|
+
}
|
|
135
|
+
const clipped = clipText(text, max);
|
|
136
|
+
if (clipped.clipped)
|
|
137
|
+
this.note(`${label} was truncated to ${max} characters.`);
|
|
138
|
+
return clipped.text;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* A work package id: letters, digits, `.`, `_`, `-`, at most `GOV_LIMITS.wpIdChars`.
|
|
142
|
+
* Never clipped (a clipped id names a different work package); an unusable one is an error.
|
|
143
|
+
*/
|
|
144
|
+
wpId(raw, label = 'wpId', required = true) {
|
|
145
|
+
const id = this.str(raw, label, 200, required);
|
|
146
|
+
if (id === '')
|
|
147
|
+
return id;
|
|
148
|
+
if (id.length > GOV_LIMITS.wpIdChars || !WP_ID_PATTERN.test(id)) {
|
|
149
|
+
this.fail(`${label} "${id.slice(0, 40)}" is not a work package id: use the id from your ` +
|
|
150
|
+
`assignment (letters, digits, . _ -; at most ${GOV_LIMITS.wpIdChars} characters).`);
|
|
151
|
+
}
|
|
152
|
+
return id;
|
|
153
|
+
}
|
|
154
|
+
/** A list of short strings. A single string is a list (newline-separated: several). */
|
|
155
|
+
list(raw, label, maxItems, maxChars) {
|
|
156
|
+
if (raw === undefined || raw === null)
|
|
157
|
+
return [];
|
|
158
|
+
let items;
|
|
159
|
+
if (Array.isArray(raw))
|
|
160
|
+
items = raw;
|
|
161
|
+
else if (typeof raw === 'string')
|
|
162
|
+
items = raw.split(/\r?\n/);
|
|
163
|
+
else {
|
|
164
|
+
this.fail(`${label} must be a list of strings.`);
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
const out = [];
|
|
168
|
+
for (const item of items) {
|
|
169
|
+
const text = typeof item === 'string' ? item.trim() : '';
|
|
170
|
+
if (text !== '' && !out.includes(text))
|
|
171
|
+
out.push(clipText(text, maxChars).text);
|
|
172
|
+
}
|
|
173
|
+
if (out.length > maxItems) {
|
|
174
|
+
this.note(`${label} keeps the first ${maxItems} of ${out.length} entries.`);
|
|
175
|
+
out.length = maxItems;
|
|
176
|
+
}
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
/** A list of records. A single object is a one-item list; anything else is an error. */
|
|
180
|
+
items(raw, label, maxItems) {
|
|
181
|
+
if (raw === undefined || raw === null)
|
|
182
|
+
return [];
|
|
183
|
+
let items;
|
|
184
|
+
if (Array.isArray(raw))
|
|
185
|
+
items = raw;
|
|
186
|
+
else if (isRecord(raw))
|
|
187
|
+
items = [raw];
|
|
188
|
+
else {
|
|
189
|
+
this.fail(`${label} must be a list.`);
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
if (items.length > maxItems) {
|
|
193
|
+
this.note(`${label} keeps the first ${maxItems} of ${items.length} entries.`);
|
|
194
|
+
return items.slice(0, maxItems);
|
|
195
|
+
}
|
|
196
|
+
return items;
|
|
197
|
+
}
|
|
198
|
+
/** One member of `allowed`, matched leniently (case, camelCase, spaces, hyphens, aliases). */
|
|
199
|
+
oneOf(raw, label, allowed, options = {}) {
|
|
200
|
+
if (raw === undefined || raw === null || raw === '') {
|
|
201
|
+
if (options.required)
|
|
202
|
+
this.fail(`${label} is required (${allowed.join(' | ')}).`);
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
const key = enumKey(String(raw));
|
|
206
|
+
const aliases = options.aliases;
|
|
207
|
+
const aliased = aliases && Object.prototype.hasOwnProperty.call(aliases, key) ? aliases[key] : undefined;
|
|
208
|
+
const found = allowed.find((a) => enumKey(a) === key) ?? aliased;
|
|
209
|
+
if (found === undefined) {
|
|
210
|
+
this.fail(`${label} must be one of ${allowed.join(', ')} (got "${String(raw)}").`);
|
|
211
|
+
}
|
|
212
|
+
return found;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/** `NOT ACCEPTED:` text for a failed normalization. Never an error result, never a penalty. */
|
|
216
|
+
export function formatNotAccepted(tool, errors) {
|
|
217
|
+
const shown = errors.slice(0, ERRORS_SHOWN);
|
|
218
|
+
const lines = [`NOT ACCEPTED: ${tool} was not recorded.`, ...shown.map((e) => `- ${e}`)];
|
|
219
|
+
if (errors.length > shown.length)
|
|
220
|
+
lines.push(`- ...and ${errors.length - shown.length} more.`);
|
|
221
|
+
lines.push(`Fix ${errors.length === 1 ? 'this' : 'these'} and call ${tool} again.`);
|
|
222
|
+
lines.push('Nothing was recorded and no GT was deducted.');
|
|
223
|
+
return lines.join('\n');
|
|
224
|
+
}
|
|
225
|
+
/** Append the repair notes (what was clipped, rescaled, renumbered) to an engine answer. */
|
|
226
|
+
export function withNotes(text, notes) {
|
|
227
|
+
if (notes.length === 0)
|
|
228
|
+
return text;
|
|
229
|
+
return `${text}\nNOTE: ${notes.slice(0, ERRORS_SHOWN).join(' ')}`;
|
|
230
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `gov_scan`: a deterministic scan for incomplete implementations (governance-mode 5.9).
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY (`governance/` is glyph-scanned).
|
|
5
|
+
*
|
|
6
|
+
* EVIDENCE, NEVER A VERDICT. The scan is a line-and-brace heuristic over several languages,
|
|
7
|
+
* not a parser. It returns `file:line:kind` hits plus a suspected-skeleton ratio, and
|
|
8
|
+
* NOTHING in the engine deducts GT from it: a heuristic that scores would turn every false
|
|
9
|
+
* positive (a deliberate empty `catch`, a trivial constant getter) into an unfair penalty.
|
|
10
|
+
* Reviewers, testers and the inspector cite it as evidence and still read the code.
|
|
11
|
+
*
|
|
12
|
+
* READ-ONLY AND CONFINED. Paths resolve against the working directory and are refused when
|
|
13
|
+
* the REAL path (after symlinks) leaves it; sizes, file counts and depth are bounded so a
|
|
14
|
+
* hostile or huge tree cannot stall a stage. Binary files are skipped, not decoded.
|
|
15
|
+
*
|
|
16
|
+
* The pure core, `scanText`, is exported separately so the heuristics are unit-tested on
|
|
17
|
+
* fixtures without touching a disk.
|
|
18
|
+
*/
|
|
19
|
+
import { type AgentTool } from '@aragon-agent/core';
|
|
20
|
+
export type ScanKind = 'todo' | 'not_implemented' | 'empty_catch' | 'bare_pass' | 'placeholder_return';
|
|
21
|
+
export interface ScanFinding {
|
|
22
|
+
line: number;
|
|
23
|
+
kind: ScanKind;
|
|
24
|
+
/** The offending line, trimmed and clipped. */
|
|
25
|
+
text: string;
|
|
26
|
+
}
|
|
27
|
+
export interface TextScan {
|
|
28
|
+
findings: ScanFinding[];
|
|
29
|
+
/** Function-like declarations seen. */
|
|
30
|
+
functions: number;
|
|
31
|
+
/** Of those, the ones whose whole body is a placeholder. */
|
|
32
|
+
skeletonFunctions: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Scan one file's text. Pure and deterministic: the same input always yields the same hits,
|
|
36
|
+
* in ascending line order, at most one per line and kind.
|
|
37
|
+
*/
|
|
38
|
+
export declare function scanText(text: string): TextScan;
|
|
39
|
+
export interface ScanOptions {
|
|
40
|
+
cwd: string;
|
|
41
|
+
paths: readonly string[];
|
|
42
|
+
/** Files larger than this are skipped, not truncated. */
|
|
43
|
+
maxFileBytes?: number;
|
|
44
|
+
/** Total files scanned across all paths. */
|
|
45
|
+
maxFiles?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface ScannedFinding extends ScanFinding {
|
|
48
|
+
file: string;
|
|
49
|
+
}
|
|
50
|
+
export interface ScanSkip {
|
|
51
|
+
path: string;
|
|
52
|
+
reason: string;
|
|
53
|
+
}
|
|
54
|
+
export interface ScanReport {
|
|
55
|
+
files: number;
|
|
56
|
+
findings: ScannedFinding[];
|
|
57
|
+
skipped: ScanSkip[];
|
|
58
|
+
functions: number;
|
|
59
|
+
skeletonFunctions: number;
|
|
60
|
+
/** `skeletonFunctions / functions`, 0 when no function was seen. */
|
|
61
|
+
skeletonRatio: number;
|
|
62
|
+
/** True when the file cap or the path cap cut the scan short. */
|
|
63
|
+
truncated: boolean;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Scan files and directories under `cwd`. Directories are walked (bounded depth, known code
|
|
67
|
+
* extensions, build and vendor trees skipped); explicit files are scanned whatever their
|
|
68
|
+
* extension. Only the first `GOV_LIMITS.scanPathsMax` paths are used; the rest are counted
|
|
69
|
+
* as truncation.
|
|
70
|
+
*/
|
|
71
|
+
export declare function scanPaths(options: ScanOptions): Promise<ScanReport>;
|
|
72
|
+
/** The text the model reads. The first line is `OK:` (7.1 rule 2). */
|
|
73
|
+
export declare function formatScanReport(report: ScanReport): string;
|
|
74
|
+
export interface ScanToolContext {
|
|
75
|
+
/** Read LIVE: the workspace the child works in. */
|
|
76
|
+
getCwd: () => string;
|
|
77
|
+
}
|
|
78
|
+
/** `gov_scan`: REV, TEST and INSP. Read-only. */
|
|
79
|
+
export declare function makeScanTool(ctx: ScanToolContext): AgentTool;
|