@event4u/agent-config 8.4.0 → 8.4.1
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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +6 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +1 -1
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +1 -1
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +1 -1
- package/dist/mcp/registry-manifest.json +1 -1
- package/package.json +4 -1
- package/src/agent-src/README.md +64 -0
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +497 -0
- package/src/agent-src/scripts/scan-seeder-violations.php +145 -0
- package/src/agent-src/scripts/update_roadmap_progress.ts +1087 -0
- package/src/agent-src/templates/AGENTS.md +34 -0
- package/src/agent-src/templates/scripts/README.md +195 -0
- package/src/agent-src/templates/scripts/check_memory.ts +662 -0
- package/src/agent-src/templates/scripts/check_memory_proposal.ts +370 -0
- package/src/agent-src/templates/scripts/historical-bug-patterns.example.yml +84 -0
- package/src/agent-src/templates/scripts/implement_ticket/__main__.ts +27 -0
- package/src/agent-src/templates/scripts/memory_hash.ts +355 -0
- package/src/agent-src/templates/scripts/memory_lookup.ts +1089 -0
- package/src/agent-src/templates/scripts/memory_report.ts +868 -0
- package/src/agent-src/templates/scripts/memory_signal.ts +444 -0
- package/src/agent-src/templates/scripts/memory_status.ts +215 -0
- package/src/agent-src/templates/scripts/ownership-map.example.yml +87 -0
- package/src/agent-src/templates/scripts/pr-risk-config.example.yml +76 -0
- package/src/agent-src/templates/scripts/pr_review_routing.ts +544 -0
- package/src/agent-src/templates/scripts/pr_risk_review.ts +0 -0
- package/src/agent-src/templates/scripts/telemetry/aggregator.ts +0 -0
- package/src/agent-src/templates/scripts/telemetry/boundary.ts +164 -0
- package/src/agent-src/templates/scripts/telemetry/engagement.ts +479 -0
- package/src/agent-src/templates/scripts/telemetry/report_renderer.ts +394 -0
- package/src/agent-src/templates/scripts/telemetry/settings.ts +210 -0
- package/src/agent-src/templates/scripts/telemetry_record.ts +276 -0
- package/src/agent-src/templates/scripts/telemetry_report.ts +211 -0
- package/src/agent-src/templates/scripts/telemetry_status.ts +333 -0
- package/src/agent-src/templates/scripts/tier_usage_report.ts +619 -0
- package/src/agent-src/templates/scripts/work_engine/__main__.ts +14 -0
- package/src/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +1261 -0
- package/src/agent-src/templates/scripts/work_engine/_lib/user_global_paths.ts +329 -0
- package/src/agent-src/templates/scripts/work_engine/cli.ts +206 -0
- package/src/agent-src/templates/scripts/work_engine/cli_args.ts +249 -0
- package/src/agent-src/templates/scripts/work_engine/delivery_state.ts +225 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/analyze.ts +125 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/implement.ts +189 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/index.ts +94 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/memory.ts +193 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/plan.ts +267 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/refine.ts +518 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/report.ts +379 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +268 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +258 -0
- package/src/agent-src/templates/scripts/work_engine/directives/index.ts +32 -0
- package/src/agent-src/templates/scripts/work_engine/directives/mixed/contract.ts +243 -0
- package/src/agent-src/templates/scripts/work_engine/directives/mixed/index.ts +108 -0
- package/src/agent-src/templates/scripts/work_engine/directives/mixed/stitch.ts +259 -0
- package/src/agent-src/templates/scripts/work_engine/directives/mixed/ui.ts +216 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/_passthrough.ts +40 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +241 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +216 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +506 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/design.ts +325 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/index.ts +102 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +462 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +474 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +352 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui_trivial/_skipped.ts +33 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui_trivial/apply.ts +213 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui_trivial/index.ts +111 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui_trivial/refine.ts +126 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui_trivial/report.ts +112 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui_trivial/test.ts +164 -0
- package/src/agent-src/templates/scripts/work_engine/dispatcher.ts +515 -0
- package/src/agent-src/templates/scripts/work_engine/emitters.ts +119 -0
- package/src/agent-src/templates/scripts/work_engine/errors.ts +24 -0
- package/src/agent-src/templates/scripts/work_engine/hook_bootstrap.ts +104 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/_chat_history_base.ts +189 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_append.ts +41 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_halt_append.ts +89 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/decision_gate.ts +193 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +304 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/directive_set_guard.ts +110 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/halt_surface_audit.ts +118 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/index.ts +17 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +161 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/state_shape_validation.ts +45 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/trace.ts +134 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/context.ts +94 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/events.ts +58 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/exceptions.ts +85 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/index.ts +27 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/registry.ts +66 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/runner.ts +90 -0
- package/src/agent-src/templates/scripts/work_engine/hooks/settings.ts +260 -0
- package/src/agent-src/templates/scripts/work_engine/input_builders.ts +260 -0
- package/src/agent-src/templates/scripts/work_engine/intent/classify.ts +466 -0
- package/src/agent-src/templates/scripts/work_engine/migration/v0_to_v1.ts +531 -0
- package/src/agent-src/templates/scripts/work_engine/orchestration.ts +366 -0
- package/src/agent-src/templates/scripts/work_engine/persona_policy.ts +97 -0
- package/src/agent-src/templates/scripts/work_engine/resolvers/diff.ts +135 -0
- package/src/agent-src/templates/scripts/work_engine/resolvers/file.ts +175 -0
- package/src/agent-src/templates/scripts/work_engine/resolvers/prompt.ts +115 -0
- package/src/agent-src/templates/scripts/work_engine/scoring/confidence.ts +415 -0
- package/src/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +466 -0
- package/src/agent-src/templates/scripts/work_engine/scoring/decision_trace.ts +298 -0
- package/src/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +444 -0
- package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +252 -0
- package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +745 -0
- package/src/agent-src/templates/scripts/work_engine/state.ts +1151 -0
- package/src/agent-src/templates/scripts/work_engine/state_io.ts +413 -0
- package/src/install/atomic.ts +111 -0
- package/src/install/conflict.ts +226 -0
- package/src/install/detect.ts +280 -0
- package/src/install/paths.ts +90 -0
- package/src/install/plan.ts +205 -0
- package/src/install/preflight.ts +210 -0
- package/src/install/selectedTools.ts +52 -0
- package/src/install/toolDetection.ts +120 -0
- package/src/install/txlog.ts +161 -0
- package/src/install/types.ts +175 -0
- package/src/install/wizard-plan.ts +216 -0
- package/src/scripts/prepack-check.mjs +108 -5
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI-trivial directive set — single-file ≤5-line micro-edit path.
|
|
3
|
+
*
|
|
4
|
+
* TypeScript twin of `work_engine/directives/ui_trivial/__init__.py` (ADR-200
|
|
5
|
+
* py2ts Phase 1 — work_engine TOP/integration layer). The `__init__.py` →
|
|
6
|
+
* `index.ts` mapping mirrors the hooks subpackage convention. Public API names
|
|
7
|
+
* stay snake_case to mirror the Python module 1:1 (per ADR-200).
|
|
8
|
+
*
|
|
9
|
+
* The eight-step shape mirrors `work_engine.directives.backend` /
|
|
10
|
+
* `work_engine.directives.ui` — eight slots, fixed order, no branching:
|
|
11
|
+
*
|
|
12
|
+
* - `refine` → `refine` — confirm intent gate.
|
|
13
|
+
* - `memory` → `_skipped` — bypassed.
|
|
14
|
+
* - `analyze` → `_skipped` — bypassed.
|
|
15
|
+
* - `plan` → `_skipped` — bypassed.
|
|
16
|
+
* - `implement` → `apply` — hard preconditions; reclassify to `ui-improve`
|
|
17
|
+
* (full audit gate) when violated.
|
|
18
|
+
* - `test` → `test` — smoke-test delegate.
|
|
19
|
+
* - `verify` → `_skipped` — bypassed.
|
|
20
|
+
* - `report` → `report` — one-line delivery summary.
|
|
21
|
+
*
|
|
22
|
+
* The directory uses an underscore (`ui_trivial`) because Python packages
|
|
23
|
+
* cannot contain hyphens. The schema carries the external hyphenated name
|
|
24
|
+
* `"ui-trivial"`; the dispatcher's loader is the single place that translates
|
|
25
|
+
* between them.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import type { Step } from '../../delivery_state.js';
|
|
29
|
+
|
|
30
|
+
import * as _skipped from './_skipped.js';
|
|
31
|
+
import * as apply from './apply.js';
|
|
32
|
+
import * as refine from './refine.js';
|
|
33
|
+
import * as report from './report.js';
|
|
34
|
+
import * as test from './test.js';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* External name carried in `state.directive_set` for this set.
|
|
38
|
+
*
|
|
39
|
+
* Note the hyphen — this is the schema/wire form, not the module name. The
|
|
40
|
+
* module name (`ui_trivial`) is an implementation detail of the loader.
|
|
41
|
+
*/
|
|
42
|
+
export const DIRECTIVE_SET_NAME = 'ui-trivial';
|
|
43
|
+
|
|
44
|
+
/** Roadmap that defines this directive bundle (Phase 2 Step 6). */
|
|
45
|
+
export const ROADMAP = 'agents/roadmaps/road-to-product-ui-track.md';
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Input kinds this directive set knows how to handle.
|
|
49
|
+
*
|
|
50
|
+
* The intent classifier reaches `ui-trivial` from any of the four input
|
|
51
|
+
* kinds; the trivial set keeps the same tuple so input routing stays
|
|
52
|
+
* unchanged once the intent label has landed.
|
|
53
|
+
*/
|
|
54
|
+
export const SUPPORTED_KINDS: ReadonlyArray<string> = ['ticket', 'prompt', 'diff', 'file'];
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Wire the eight-step dispatcher slots for the trivial set.
|
|
58
|
+
*
|
|
59
|
+
* `refine` validates the intent gate; `implement`, `test`, and `report` carry
|
|
60
|
+
* the trivial-path behavior; the four bypassed slots share `_skipped` so the
|
|
61
|
+
* dispatcher's completeness check is satisfied without inventing per-slot
|
|
62
|
+
* stubs. The mapping is rebuilt per call (cheap; the dispatcher invokes
|
|
63
|
+
* `get_steps` once per run).
|
|
64
|
+
*/
|
|
65
|
+
function _build_step_map(): Map<string, Step> {
|
|
66
|
+
const skipped = _skipped.run;
|
|
67
|
+
return new Map<string, Step>([
|
|
68
|
+
['refine', refine.run],
|
|
69
|
+
['memory', skipped],
|
|
70
|
+
['analyze', skipped],
|
|
71
|
+
['plan', skipped],
|
|
72
|
+
['implement', apply.run],
|
|
73
|
+
['test', test.run],
|
|
74
|
+
['verify', skipped],
|
|
75
|
+
['report', report.run],
|
|
76
|
+
]);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Return the `{step_name: handler}` mapping the dispatcher walks.
|
|
81
|
+
*
|
|
82
|
+
* Mirrors `work_engine.directives.backend.get_steps`. `refine`, `implement`,
|
|
83
|
+
* `test`, and `report` carry trivial-path behavior; the four bypassed slots
|
|
84
|
+
* delegate to `_skipped`.
|
|
85
|
+
*/
|
|
86
|
+
export function get_steps(): Map<string, Step> {
|
|
87
|
+
return _build_step_map();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Per-step ambiguity declarations.
|
|
92
|
+
*
|
|
93
|
+
* Mirrors `work_engine.directives.backend.all_ambiguities`. The four bypassed
|
|
94
|
+
* slots re-export `_skipped.AMBIGUITIES` (empty) so doc generators see a
|
|
95
|
+
* uniform shape across all eight steps.
|
|
96
|
+
*/
|
|
97
|
+
export function all_ambiguities(): Record<string, ReadonlyArray<Record<string, string>>> {
|
|
98
|
+
const skipped = _skipped.AMBIGUITIES;
|
|
99
|
+
return {
|
|
100
|
+
refine: refine.AMBIGUITIES,
|
|
101
|
+
memory: skipped,
|
|
102
|
+
analyze: skipped,
|
|
103
|
+
plan: skipped,
|
|
104
|
+
implement: apply.AMBIGUITIES,
|
|
105
|
+
test: test.AMBIGUITIES,
|
|
106
|
+
verify: skipped,
|
|
107
|
+
report: report.AMBIGUITIES,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export { apply, refine, report, test };
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `refine` step — intent gate for the `ui-trivial` directive set.
|
|
3
|
+
*
|
|
4
|
+
* TypeScript twin of `directives/ui_trivial/refine.py` (ADR-200 py2ts).
|
|
5
|
+
* Public API names stay snake_case to mirror the Python module 1:1.
|
|
6
|
+
*
|
|
7
|
+
* `ui-trivial` is reachable only when the intent classifier (or an explicit
|
|
8
|
+
* user override) labelled the work as `ui-trivial`. Reaching this slot through
|
|
9
|
+
* any other route is a routing error, not a user-facing ambiguity.
|
|
10
|
+
*
|
|
11
|
+
* The handler is deterministic and tiny: confirm the ticket carries the
|
|
12
|
+
* expected intent label (or accept the default when `directive_set` is already
|
|
13
|
+
* pinned), and return `SUCCESS`.
|
|
14
|
+
*/
|
|
15
|
+
import {
|
|
16
|
+
type Any,
|
|
17
|
+
type DeliveryState,
|
|
18
|
+
Outcome,
|
|
19
|
+
StepResult,
|
|
20
|
+
} from '../../delivery_state.js';
|
|
21
|
+
|
|
22
|
+
/** Intent label that gates entry into this directive set. */
|
|
23
|
+
export const EXPECTED_INTENT = 'ui-trivial';
|
|
24
|
+
|
|
25
|
+
export const AMBIGUITIES: ReadonlyArray<Record<string, string>> = [
|
|
26
|
+
{
|
|
27
|
+
code: 'wrong_intent_for_trivial',
|
|
28
|
+
trigger:
|
|
29
|
+
"state.ticket['intent'] is set and not equal to 'ui-trivial' " +
|
|
30
|
+
'— routing landed on this set by mistake',
|
|
31
|
+
resolution:
|
|
32
|
+
'abort and re-run with the correct directive_set ' +
|
|
33
|
+
"(populate_routing should select 'ui' or 'backend' instead)",
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Python `repr()` for the scalar/JSON value kinds that reach the `{intent!r}`
|
|
39
|
+
* interpolation. Mirrors CPython: `None`, `True`/`False`, single-quoted
|
|
40
|
+
* strings (double-quoted only when the string contains a single quote and no
|
|
41
|
+
* double quote), numbers and everything else via `str`.
|
|
42
|
+
*/
|
|
43
|
+
function pyRepr(value: Any): string {
|
|
44
|
+
if (value === null || value === undefined) {
|
|
45
|
+
return 'None';
|
|
46
|
+
}
|
|
47
|
+
if (value === true) {
|
|
48
|
+
return 'True';
|
|
49
|
+
}
|
|
50
|
+
if (value === false) {
|
|
51
|
+
return 'False';
|
|
52
|
+
}
|
|
53
|
+
if (typeof value === 'string') {
|
|
54
|
+
return pyStrRepr(value);
|
|
55
|
+
}
|
|
56
|
+
return String(value);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Python `str(value)` for the scalar kinds reaching the `{intent}` slot. */
|
|
60
|
+
function pyStr(value: Any): string {
|
|
61
|
+
if (value === null || value === undefined) {
|
|
62
|
+
return 'None';
|
|
63
|
+
}
|
|
64
|
+
if (value === true) {
|
|
65
|
+
return 'True';
|
|
66
|
+
}
|
|
67
|
+
if (value === false) {
|
|
68
|
+
return 'False';
|
|
69
|
+
}
|
|
70
|
+
return String(value);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function pyStrRepr(s: string): string {
|
|
74
|
+
const hasSingle = s.includes("'");
|
|
75
|
+
const hasDouble = s.includes('"');
|
|
76
|
+
const quote = hasSingle && !hasDouble ? '"' : "'";
|
|
77
|
+
let body = '';
|
|
78
|
+
for (const ch of s) {
|
|
79
|
+
if (ch === '\\') {
|
|
80
|
+
body += '\\\\';
|
|
81
|
+
} else if (ch === quote) {
|
|
82
|
+
body += `\\${ch}`;
|
|
83
|
+
} else if (ch === '\n') {
|
|
84
|
+
body += '\\n';
|
|
85
|
+
} else if (ch === '\r') {
|
|
86
|
+
body += '\\r';
|
|
87
|
+
} else if (ch === '\t') {
|
|
88
|
+
body += '\\t';
|
|
89
|
+
} else {
|
|
90
|
+
body += ch;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return `${quote}${body}${quote}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Confirm the ticket's intent matches the trivial path.
|
|
98
|
+
*
|
|
99
|
+
* The ticket's `intent` is optional on v0 callers; absence is treated as a
|
|
100
|
+
* silent pass since the v0 path predates intent classification. v1 callers
|
|
101
|
+
* always carry an intent — a mismatch means the dispatcher routed incorrectly
|
|
102
|
+
* and we halt loudly so the wiring bug surfaces before any edit happens.
|
|
103
|
+
*/
|
|
104
|
+
export function run(state: DeliveryState): StepResult {
|
|
105
|
+
const ticket = state.ticket ?? {};
|
|
106
|
+
const intent = ticket['intent'];
|
|
107
|
+
if (intent === undefined || intent === null || intent === EXPECTED_INTENT) {
|
|
108
|
+
return new StepResult({ outcome: Outcome.SUCCESS });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return new StepResult({
|
|
112
|
+
outcome: Outcome.BLOCKED,
|
|
113
|
+
questions: [
|
|
114
|
+
'> Routing error — the ``ui-trivial`` directive set was ' +
|
|
115
|
+
"selected but the ticket's intent is `" +
|
|
116
|
+
pyStr(intent) +
|
|
117
|
+
'`.',
|
|
118
|
+
'> 1. Reclassify — set `state.directive_set` from the ' +
|
|
119
|
+
'intent label and re-invoke the engine',
|
|
120
|
+
'> 2. Abort — drop this run',
|
|
121
|
+
],
|
|
122
|
+
message:
|
|
123
|
+
`intent=${pyRepr(intent)} but directive_set='ui-trivial'; ` +
|
|
124
|
+
'routing must be reclassified before any edit',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `report` step — one-line delivery summary for the trivial path.
|
|
3
|
+
*
|
|
4
|
+
* TypeScript twin of `directives/ui_trivial/report.py` (ADR-200 py2ts).
|
|
5
|
+
* Public API names stay snake_case to mirror the Python module 1:1.
|
|
6
|
+
*
|
|
7
|
+
* The trivial summary captures what the operator needs: which file, how many
|
|
8
|
+
* lines, what the edit did, and the smoke verdict. The step is pure and
|
|
9
|
+
* deterministic: reads `DeliveryState`, writes `state.report`, always returns
|
|
10
|
+
* `SUCCESS`.
|
|
11
|
+
*/
|
|
12
|
+
import {
|
|
13
|
+
type Any,
|
|
14
|
+
type DeliveryState,
|
|
15
|
+
Outcome,
|
|
16
|
+
StepResult,
|
|
17
|
+
} from '../../delivery_state.js';
|
|
18
|
+
|
|
19
|
+
/** Pure render — no blocked paths. */
|
|
20
|
+
export const AMBIGUITIES: ReadonlyArray<Record<string, string>> = [];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Python truthiness for the values reached here: empty string / empty array /
|
|
24
|
+
* empty object / 0 / false / null / undefined are falsy.
|
|
25
|
+
*/
|
|
26
|
+
function _pyTruthy(value: Any): boolean {
|
|
27
|
+
if (value === null || value === undefined) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (typeof value === 'string') {
|
|
31
|
+
return value.length > 0;
|
|
32
|
+
}
|
|
33
|
+
if (typeof value === 'number') {
|
|
34
|
+
return value !== 0;
|
|
35
|
+
}
|
|
36
|
+
if (typeof value === 'boolean') {
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
if (Array.isArray(value)) {
|
|
40
|
+
return value.length > 0;
|
|
41
|
+
}
|
|
42
|
+
if (typeof value === 'object') {
|
|
43
|
+
return Object.keys(value).length > 0;
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Python `str(value)` for the `{lines}` interpolation. */
|
|
49
|
+
function pyStr(value: Any): string {
|
|
50
|
+
if (value === null || value === undefined) {
|
|
51
|
+
return 'None';
|
|
52
|
+
}
|
|
53
|
+
if (value === true) {
|
|
54
|
+
return 'True';
|
|
55
|
+
}
|
|
56
|
+
if (value === false) {
|
|
57
|
+
return 'False';
|
|
58
|
+
}
|
|
59
|
+
return String(value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function _isDict(value: Any): value is Record<string, Any> {
|
|
63
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Render the one-line trivial summary into `state.report`. */
|
|
67
|
+
export function run(state: DeliveryState): StepResult {
|
|
68
|
+
state.report = _render(state);
|
|
69
|
+
return new StepResult({ outcome: Outcome.SUCCESS });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function _render(state: DeliveryState): string {
|
|
73
|
+
const change = _last_trivial_change(state);
|
|
74
|
+
if (change === null) {
|
|
75
|
+
return '_(trivial UI edit — no change recorded)_';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const filesRaw = change['files'];
|
|
79
|
+
const files = _pyTruthy(filesRaw) ? filesRaw : [];
|
|
80
|
+
const lines = 'lines_changed' in change ? change['lines_changed'] : 0;
|
|
81
|
+
const summaryRaw = change['summary'];
|
|
82
|
+
const summary = (_pyTruthy(summaryRaw) ? String(summaryRaw) : 'trivial UI edit').trim();
|
|
83
|
+
const file_str =
|
|
84
|
+
Array.isArray(files) && files.length === 1
|
|
85
|
+
? pyStr((files as Any[])[0])
|
|
86
|
+
: `${Array.isArray(files) ? files.length : 0} files`;
|
|
87
|
+
const verdict = _smoke_verdict(state);
|
|
88
|
+
const verdict_str = verdict ? ` — smoke: **${verdict}**` : '';
|
|
89
|
+
return `**Trivial edit:** ${summary} (\`${file_str}\`, ${pyStr(lines)} lines)${verdict_str}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function _last_trivial_change(state: DeliveryState): Record<string, Any> | null {
|
|
93
|
+
const changes = _pyTruthy(state.changes) ? state.changes : [];
|
|
94
|
+
for (let i = changes.length - 1; i >= 0; i -= 1) {
|
|
95
|
+
const change = changes[i];
|
|
96
|
+
if (_isDict(change) && change['kind'] === 'ui-trivial') {
|
|
97
|
+
return change;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function _smoke_verdict(state: DeliveryState): string {
|
|
104
|
+
const tests = state.tests;
|
|
105
|
+
if (_isDict(tests)) {
|
|
106
|
+
const verdict = tests['verdict'];
|
|
107
|
+
if (typeof verdict === 'string') {
|
|
108
|
+
return verdict;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return '';
|
|
112
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `test` step — smoke-test delegate for the `ui-trivial` set.
|
|
3
|
+
*
|
|
4
|
+
* TypeScript twin of `directives/ui_trivial/test.py` (ADR-200 py2ts). Public
|
|
5
|
+
* API names stay snake_case to mirror the Python module 1:1.
|
|
6
|
+
*
|
|
7
|
+
* The trivial path runs "apply + smoke-test only". The handler is the smaller
|
|
8
|
+
* cousin of `backend.test`: same verdict contract on `state.tests`, narrower
|
|
9
|
+
* scope on the agent directive. `failed` / `mixed` verdicts halt with the
|
|
10
|
+
* verdict echoed back; `success` flows through.
|
|
11
|
+
*/
|
|
12
|
+
import {
|
|
13
|
+
type Any,
|
|
14
|
+
type DeliveryState,
|
|
15
|
+
Outcome,
|
|
16
|
+
StepResult,
|
|
17
|
+
agent_directive,
|
|
18
|
+
} from '../../delivery_state.js';
|
|
19
|
+
|
|
20
|
+
const _ALLOWED_VERDICTS: ReadonlyArray<string> = ['success', 'failed', 'mixed'];
|
|
21
|
+
|
|
22
|
+
export const AMBIGUITIES: ReadonlyArray<Record<string, string>> = [
|
|
23
|
+
{
|
|
24
|
+
code: 'empty_tests_delegate',
|
|
25
|
+
trigger: '`state.tests` empty — smoke runner not invoked yet',
|
|
26
|
+
resolution:
|
|
27
|
+
'agent directive `run-tests scope=smoke` → ' +
|
|
28
|
+
'`/tests-execute` (narrowest layer covering the touched file)',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
code: 'malformed_tests',
|
|
32
|
+
trigger:
|
|
33
|
+
'`state.tests` is not a dict or `verdict` is not one of ' +
|
|
34
|
+
'success / failed / mixed',
|
|
35
|
+
resolution: 're-run smoke and record a clean verdict',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
code: 'bad_test_verdict',
|
|
39
|
+
trigger: "`state.tests['verdict']` is `failed` or `mixed`",
|
|
40
|
+
resolution: 'fix the regression and re-run, or abort',
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
function _isDict(value: Any): value is Record<string, Any> {
|
|
45
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Python truthiness for `if not tests`. */
|
|
49
|
+
function _pyTruthy(value: Any): boolean {
|
|
50
|
+
if (value === null || value === undefined) return false;
|
|
51
|
+
if (typeof value === 'string') return value.length > 0;
|
|
52
|
+
if (typeof value === 'number') return value !== 0;
|
|
53
|
+
if (typeof value === 'boolean') return value;
|
|
54
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
55
|
+
if (typeof value === 'object') return Object.keys(value).length > 0;
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Python `type(x).__name__` for the dict/non-dict diagnostic. */
|
|
60
|
+
function _pyTypeName(value: Any): string {
|
|
61
|
+
if (value === null || value === undefined) return 'NoneType';
|
|
62
|
+
if (typeof value === 'boolean') return 'bool';
|
|
63
|
+
if (typeof value === 'number') {
|
|
64
|
+
return Number.isInteger(value) ? 'int' : 'float';
|
|
65
|
+
}
|
|
66
|
+
if (typeof value === 'string') return 'str';
|
|
67
|
+
if (Array.isArray(value)) return 'list';
|
|
68
|
+
return 'dict';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Python `repr()` for the verdict value (`{verdict!r}`). */
|
|
72
|
+
function pyRepr(value: Any): string {
|
|
73
|
+
if (value === null || value === undefined) return 'None';
|
|
74
|
+
if (value === true) return 'True';
|
|
75
|
+
if (value === false) return 'False';
|
|
76
|
+
if (typeof value === 'string') return pyStrRepr(value);
|
|
77
|
+
return String(value);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function pyStrRepr(s: string): string {
|
|
81
|
+
const hasSingle = s.includes("'");
|
|
82
|
+
const hasDouble = s.includes('"');
|
|
83
|
+
const quote = hasSingle && !hasDouble ? '"' : "'";
|
|
84
|
+
let body = '';
|
|
85
|
+
for (const ch of s) {
|
|
86
|
+
if (ch === '\\') body += '\\\\';
|
|
87
|
+
else if (ch === quote) body += `\\${ch}`;
|
|
88
|
+
else if (ch === '\n') body += '\\n';
|
|
89
|
+
else if (ch === '\r') body += '\\r';
|
|
90
|
+
else if (ch === '\t') body += '\\t';
|
|
91
|
+
else body += ch;
|
|
92
|
+
}
|
|
93
|
+
return `${quote}${body}${quote}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Python `repr()` of the `_ALLOWED_VERDICTS` tuple, for `{_ALLOWED_VERDICTS}`. */
|
|
97
|
+
function _allowedVerdictsRepr(): string {
|
|
98
|
+
return `(${_ALLOWED_VERDICTS.map((v) => pyStrRepr(v)).join(', ')})`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Gate the smoke verdict; delegate when `state.tests` is empty. */
|
|
102
|
+
export function run(state: DeliveryState): StepResult {
|
|
103
|
+
const tests = state.tests;
|
|
104
|
+
if (!_pyTruthy(tests)) {
|
|
105
|
+
return _delegate();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!_isDict(tests)) {
|
|
109
|
+
return _malformed(`state.tests is ${_pyTypeName(tests)}, expected dict`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const verdict = tests['verdict'];
|
|
113
|
+
if (typeof verdict !== 'string' || !_ALLOWED_VERDICTS.includes(verdict)) {
|
|
114
|
+
return _malformed(
|
|
115
|
+
`state.tests['verdict']=${pyRepr(verdict)} not in ${_allowedVerdictsRepr()}`,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (verdict === 'success') {
|
|
120
|
+
return new StepResult({ outcome: Outcome.SUCCESS });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return new StepResult({
|
|
124
|
+
outcome: Outcome.BLOCKED,
|
|
125
|
+
questions: [
|
|
126
|
+
`> Smoke test verdict: **${verdict}** — trivial edit may ` +
|
|
127
|
+
'have regressed the touched surface.',
|
|
128
|
+
'> 1. Investigate — read failures, fix, re-run smoke',
|
|
129
|
+
'> 2. Reclassify — promote to `ui-improve` if the regression ' +
|
|
130
|
+
'indicates the edit was less trivial than assumed',
|
|
131
|
+
'> 3. Abort — revert the edit',
|
|
132
|
+
],
|
|
133
|
+
message: `smoke verdict=${verdict} on trivial edit`,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function _delegate(): StepResult {
|
|
138
|
+
return new StepResult({
|
|
139
|
+
outcome: Outcome.BLOCKED,
|
|
140
|
+
questions: [
|
|
141
|
+
agent_directive('run-tests', { scope: 'smoke' }),
|
|
142
|
+
'> Trivial edit applied; run the smoke test layer covering ' +
|
|
143
|
+
'the touched file.',
|
|
144
|
+
'> 1. Continue — invoke `/tests-execute` with smoke scope, ' +
|
|
145
|
+
'then write the verdict back to `state.tests`',
|
|
146
|
+
'> 2. Skip — record `state.tests = {"verdict": "success", ' +
|
|
147
|
+
'"scope": "none"}` (logged as a deferred verification)',
|
|
148
|
+
'> 3. Abort — drop this run',
|
|
149
|
+
],
|
|
150
|
+
message: 'smoke run not yet invoked',
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function _malformed(detail: string): StepResult {
|
|
155
|
+
return new StepResult({
|
|
156
|
+
outcome: Outcome.BLOCKED,
|
|
157
|
+
questions: [
|
|
158
|
+
`> Malformed test envelope: ${detail}.`,
|
|
159
|
+
'> 1. Re-run smoke — produce a clean verdict',
|
|
160
|
+
'> 2. Abort — drop this run',
|
|
161
|
+
],
|
|
162
|
+
message: detail,
|
|
163
|
+
});
|
|
164
|
+
}
|