@event4u/agent-config 9.10.0 → 9.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +59 -0
- package/README.md +12 -15
- package/agents/templates/.ai-council.yml.example +481 -0
- package/agents/templates/.ai-video.xml.example +247 -0
- package/dist/agent-src/rules/design-fidelity.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +15 -2
- package/dist/agent-src/skills/fe-design/SKILL.md +10 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +30 -30
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/design.ts +88 -33
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +20 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +20 -1
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +179 -0
- package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +155 -4
- package/dist/agent-src/templates/scripts/work_engine/state.ts +21 -4
- package/dist/cli/initRouting.js +74 -11
- package/dist/cli/initRouting.js.map +1 -1
- package/dist/cli/main.js +14 -3
- package/dist/cli/main.js.map +1 -1
- package/dist/cli-delegate/chunk-4SV3XGFA.js +6910 -0
- package/dist/cli-delegate/chunk-736JCWLM.js +1104 -0
- package/dist/cli-delegate/chunk-7DSAMZFU.js +356 -0
- package/dist/cli-delegate/chunk-7HQ52DHU.js +74 -0
- package/dist/cli-delegate/chunk-BJGXZNPM.js +266 -0
- package/dist/cli-delegate/chunk-FTAISGDQ.js +117 -0
- package/dist/cli-delegate/chunk-GME3Q44L.js +82 -0
- package/dist/cli-delegate/chunk-GRRPEPRN.js +336 -0
- package/dist/cli-delegate/chunk-HJ6DAJHR.js +502 -0
- package/dist/cli-delegate/chunk-IR7P7JWA.js +872 -0
- package/dist/cli-delegate/chunk-J55WI3RR.js +47 -0
- package/dist/cli-delegate/chunk-MCJYCQPF.js +35 -0
- package/dist/cli-delegate/chunk-NDJLNC6Q.js +169 -0
- package/dist/cli-delegate/chunk-OMIVVCG2.js +26 -0
- package/dist/cli-delegate/chunk-OY53J6II.js +7336 -0
- package/dist/cli-delegate/chunk-RDGRHAAB.js +259 -0
- package/dist/cli-delegate/chunk-TFG57JJM.js +1969 -0
- package/dist/cli-delegate/chunk-XX6TOPD7.js +715 -0
- package/dist/cli-delegate/chunk-YMFO6FDN.js +10603 -0
- package/dist/cli-delegate/cmd_analyze_session.js +294 -0
- package/dist/cli-delegate/cmd_conformance.js +646 -0
- package/dist/cli-delegate/cmd_converge.js +326 -0
- package/dist/cli-delegate/cmd_doctor.js +144 -0
- package/dist/cli-delegate/cmd_explain.js +2508 -0
- package/dist/cli-delegate/cmd_export.js +296 -0
- package/dist/cli-delegate/cmd_fleet.js +332 -0
- package/dist/cli-delegate/cmd_memory_get.js +3878 -0
- package/dist/cli-delegate/cmd_migrate.js +11 -0
- package/dist/cli-delegate/cmd_preflight.js +270 -0
- package/dist/cli-delegate/cmd_prune.js +444 -0
- package/dist/cli-delegate/cmd_refresh.js +28 -0
- package/dist/cli-delegate/cmd_settings_check.js +574 -0
- package/dist/cli-delegate/cmd_settings_migrate.js +223 -0
- package/dist/cli-delegate/cmd_sync.js +264 -0
- package/dist/cli-delegate/cmd_uninstall.js +656 -0
- package/dist/cli-delegate/cmd_update.js +425 -0
- package/dist/cli-delegate/cmd_upgrade.js +444 -0
- package/dist/cli-delegate/cmd_validate.js +305 -0
- package/dist/cli-delegate/cmd_versions.js +370 -0
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +14 -7
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +2 -2
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +12 -7
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +4 -3
- package/dist/install/emit_host_rules_cli.js +2 -1
- package/dist/install/emit_host_rules_cli.js.map +1 -1
- package/dist/install/install.mjs +410 -378
- package/dist/install/rule_scope.js +5 -1
- package/dist/install/rule_scope.js.map +1 -1
- package/dist/install/toolDetection.js +40 -12
- package/dist/install/toolDetection.js.map +1 -1
- package/dist/install/wizard-plan.js +2 -2
- package/dist/install/wizard-plan.js.map +1 -1
- package/dist/mcp/registry-manifest.json +1 -1
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1614 -765
- package/dist/router.json +1 -1
- package/package.json +4 -2
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +30 -30
- package/src/agent-src/templates/scripts/work_engine/directives/ui/design.ts +88 -33
- package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +20 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +20 -1
- package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +179 -0
- package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +155 -4
- package/src/agent-src/templates/scripts/work_engine/state.ts +21 -4
- package/src/config/discovery/packs.yml +7 -0
- package/src/config/evaluator-budgets.json +1 -1
- package/src/config/pack-size-budget.json +27 -0
- package/src/install/emit_host_rules_cli.ts +2 -1
- package/src/install/rule_scope.ts +5 -1
- package/src/install/toolDetection.ts +39 -8
- package/src/install/wizard-plan.ts +2 -2
- package/src/scripts/_cli/cmd_conformance.ts +2 -1
- package/src/scripts/_cli/cmd_converge.ts +2 -1
- package/src/scripts/_cli/cmd_doctor.ts +214 -13
- package/src/scripts/_cli/cmd_export.ts +2 -1
- package/src/scripts/_cli/cmd_fleet.ts +2 -1
- package/src/scripts/_cli/cmd_migrate.ts +2 -1
- package/src/scripts/_cli/cmd_preflight.ts +2 -1
- package/src/scripts/_cli/cmd_refresh.ts +2 -1
- package/src/scripts/_cli/cmd_update.ts +2 -1
- package/src/scripts/_cli/cmd_upgrade.ts +2 -1
- package/src/scripts/_cli/cmd_versions.ts +2 -1
- package/src/scripts/_cli/detection_report.ts +480 -0
- package/src/scripts/_dispatch.bash +52 -14
- package/src/scripts/_lib/changelog_eras.ts +103 -9
- package/src/scripts/_lib/environment_detector.ts +441 -0
- package/src/scripts/_lib/package_root.ts +72 -0
- package/src/scripts/ai_council/config.ts +15 -7
- package/src/scripts/ai_council/modes.ts +68 -2
- package/src/scripts/ai_council/transport_resolver.ts +331 -0
- package/src/scripts/check_bundle_path_leakage.ts +10 -4
- package/src/scripts/check_dependency_floors.ts +153 -0
- package/src/scripts/check_pack_size.ts +188 -0
- package/src/scripts/council_cli.ts +6 -2
- package/src/scripts/evaluator_umbrella.sh +34 -1
- package/src/scripts/lint_eval_fixture_citations.ts +113 -0
- package/src/scripts/lint_ui_stack_bundles.ts +170 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!--
|
|
3
|
+
agents/.ai-video.xml.example
|
|
4
|
+
============================
|
|
5
|
+
|
|
6
|
+
Operator-facing provider configuration for the /video:* command cluster.
|
|
7
|
+
|
|
8
|
+
Copy this file to `agents/.ai-video.xml` and fill in the API keys for the
|
|
9
|
+
providers you actually use. The real file is gitignored and MUST NEVER be
|
|
10
|
+
committed.
|
|
11
|
+
|
|
12
|
+
Loader: scripts/media/lib/load-config.sh
|
|
13
|
+
Redaction: scripts/media/lib/redact.sh
|
|
14
|
+
Docs: agents/reference/ai-video/README.md
|
|
15
|
+
-->
|
|
16
|
+
<ai-video version="1">
|
|
17
|
+
|
|
18
|
+
<!--
|
|
19
|
+
Defaults: pick which provider a /video:* run uses when the command
|
|
20
|
+
does not pass the image-provider / video-provider flags. Must match
|
|
21
|
+
a provider id="..." declared below.
|
|
22
|
+
|
|
23
|
+
Each provider carries a <lifecycle> tag — experimental | stable |
|
|
24
|
+
deprecated | community — per docs/contracts/provider-lifecycle.md.
|
|
25
|
+
Day-one shipped tiers are conservative: every adapter is
|
|
26
|
+
`experimental` until a maintainer captures a real-API smoke trace.
|
|
27
|
+
The agent refuses to *default* to a non-stable provider silently;
|
|
28
|
+
it surfaces the tier and asks before running.
|
|
29
|
+
-->
|
|
30
|
+
<default-image-provider>openai-images</default-image-provider>
|
|
31
|
+
<default-video-provider>gemini-veo</default-video-provider>
|
|
32
|
+
|
|
33
|
+
<!-- ============================================================ -->
|
|
34
|
+
<!-- Image providers -->
|
|
35
|
+
<!-- ============================================================ -->
|
|
36
|
+
|
|
37
|
+
<provider id="openai-images" kind="image">
|
|
38
|
+
<lifecycle>stable</lifecycle>
|
|
39
|
+
<api-key>sk-REPLACE-ME</api-key>
|
|
40
|
+
<endpoint>https://api.openai.com/v1</endpoint>
|
|
41
|
+
<default-model>gpt-image-1</default-model>
|
|
42
|
+
<dry-run>true</dry-run>
|
|
43
|
+
<tuning>
|
|
44
|
+
<aspect>16:9</aspect>
|
|
45
|
+
<quality>high</quality>
|
|
46
|
+
<best-of-n>4</best-of-n>
|
|
47
|
+
</tuning>
|
|
48
|
+
</provider>
|
|
49
|
+
|
|
50
|
+
<!-- ============================================================ -->
|
|
51
|
+
<!-- Video providers -->
|
|
52
|
+
<!-- ============================================================ -->
|
|
53
|
+
|
|
54
|
+
<provider id="higgsfield" kind="image+video">
|
|
55
|
+
<lifecycle>stable</lifecycle>
|
|
56
|
+
<api-key>hf-REPLACE-ME</api-key>
|
|
57
|
+
<endpoint>https://api.higgsfield.ai/v1</endpoint>
|
|
58
|
+
<default-model>higgsfield-soul</default-model>
|
|
59
|
+
<dry-run>true</dry-run>
|
|
60
|
+
<tuning>
|
|
61
|
+
<aspect>16:9</aspect>
|
|
62
|
+
<preset>cinematic</preset>
|
|
63
|
+
<max-duration>5</max-duration>
|
|
64
|
+
<best-of-n>2</best-of-n>
|
|
65
|
+
</tuning>
|
|
66
|
+
</provider>
|
|
67
|
+
|
|
68
|
+
<provider id="gemini-veo" kind="video">
|
|
69
|
+
<lifecycle>stable</lifecycle>
|
|
70
|
+
<api-key>AIza-REPLACE-ME</api-key>
|
|
71
|
+
<endpoint>https://generativelanguage.googleapis.com/v1beta</endpoint>
|
|
72
|
+
<default-model>veo-3.0-generate-001</default-model>
|
|
73
|
+
<dry-run>true</dry-run>
|
|
74
|
+
<tuning>
|
|
75
|
+
<aspect>16:9</aspect>
|
|
76
|
+
<fps>24</fps>
|
|
77
|
+
<max-duration>8</max-duration>
|
|
78
|
+
<audio-native>true</audio-native>
|
|
79
|
+
</tuning>
|
|
80
|
+
</provider>
|
|
81
|
+
|
|
82
|
+
<provider id="kling" kind="video">
|
|
83
|
+
<lifecycle>stable</lifecycle>
|
|
84
|
+
<access-key>kling-access-REPLACE-ME</access-key>
|
|
85
|
+
<secret-key>kling-secret-REPLACE-ME</secret-key>
|
|
86
|
+
<endpoint>https://api.klingai.com/v1</endpoint>
|
|
87
|
+
<default-model>kling-v2-master</default-model>
|
|
88
|
+
<dry-run>true</dry-run>
|
|
89
|
+
<tuning>
|
|
90
|
+
<aspect>16:9</aspect>
|
|
91
|
+
<fps>30</fps>
|
|
92
|
+
<max-duration>10</max-duration>
|
|
93
|
+
<audio-native>false</audio-native>
|
|
94
|
+
</tuning>
|
|
95
|
+
</provider>
|
|
96
|
+
|
|
97
|
+
<provider id="sora" kind="video">
|
|
98
|
+
<lifecycle>stable</lifecycle>
|
|
99
|
+
<api-key>sora-REPLACE-ME</api-key>
|
|
100
|
+
<endpoint>https://api.openai.com/v1</endpoint>
|
|
101
|
+
<default-model>sora-2</default-model>
|
|
102
|
+
<dry-run>true</dry-run>
|
|
103
|
+
<tuning>
|
|
104
|
+
<aspect>16:9</aspect>
|
|
105
|
+
<fps>30</fps>
|
|
106
|
+
<max-duration>20</max-duration>
|
|
107
|
+
<audio-native>true</audio-native>
|
|
108
|
+
</tuning>
|
|
109
|
+
</provider>
|
|
110
|
+
|
|
111
|
+
<!-- ============================================================ -->
|
|
112
|
+
<!-- Multiplexer providers — one adapter, many models -->
|
|
113
|
+
<!-- Model selection: top-level `model_id` stdin key overrides -->
|
|
114
|
+
<!-- <default-model>. Per-model capabilities: -->
|
|
115
|
+
<!-- scripts/ai-video/lib/model-capabilities/<id>.json -->
|
|
116
|
+
<!-- Kill-switch: <enabled>false</enabled> refuses every network -->
|
|
117
|
+
<!-- subcommand without editing procedures that name the provider.-->
|
|
118
|
+
<!-- ============================================================ -->
|
|
119
|
+
|
|
120
|
+
<provider id="fal" kind="video">
|
|
121
|
+
<lifecycle>stable</lifecycle>
|
|
122
|
+
<enabled>true</enabled>
|
|
123
|
+
<api-key>fal-REPLACE-ME</api-key>
|
|
124
|
+
<endpoint>https://queue.fal.run</endpoint>
|
|
125
|
+
<default-model>fal-ai/ltx-2/text-to-video</default-model>
|
|
126
|
+
<dry-run>true</dry-run>
|
|
127
|
+
<tuning>
|
|
128
|
+
<aspect>16:9</aspect>
|
|
129
|
+
<max-duration>10</max-duration>
|
|
130
|
+
<audio-native>false</audio-native>
|
|
131
|
+
</tuning>
|
|
132
|
+
</provider>
|
|
133
|
+
|
|
134
|
+
<provider id="replicate" kind="video">
|
|
135
|
+
<lifecycle>stable</lifecycle>
|
|
136
|
+
<enabled>true</enabled>
|
|
137
|
+
<api-key>r8_REPLACE-ME</api-key>
|
|
138
|
+
<endpoint>https://api.replicate.com/v1</endpoint>
|
|
139
|
+
<default-model>kwaivgi/kling-v2.1-master</default-model>
|
|
140
|
+
<dry-run>true</dry-run>
|
|
141
|
+
<tuning>
|
|
142
|
+
<aspect>16:9</aspect>
|
|
143
|
+
<max-duration>10</max-duration>
|
|
144
|
+
<audio-native>false</audio-native>
|
|
145
|
+
</tuning>
|
|
146
|
+
</provider>
|
|
147
|
+
|
|
148
|
+
<!--
|
|
149
|
+
Local ComfyUI (ADR-060 — container-primary sandbox). No api-key:
|
|
150
|
+
the adapter talks to YOUR ComfyUI. A loopback endpoint is assumed
|
|
151
|
+
to be the shipped container posture (read-only image, cap-drop ALL,
|
|
152
|
+
network-none at generation time — run line in the adapter header).
|
|
153
|
+
A NON-loopback endpoint is the remote escape hatch: isolation is
|
|
154
|
+
the operator's responsibility and the block must carry
|
|
155
|
+
<unsandboxed>accepted</unsandboxed> or the adapter refuses (exit 6).
|
|
156
|
+
Models: wan22-ti2v-5b (default, Apache-2.0) | ltx-2 (audio-sync).
|
|
157
|
+
Shipped workflow templates only — operator workflow JSONs are
|
|
158
|
+
rejected by design. macOS: no CUDA — container path is
|
|
159
|
+
reference-only; use the remote escape hatch.
|
|
160
|
+
-->
|
|
161
|
+
<provider id="comfyui" kind="video">
|
|
162
|
+
<lifecycle>experimental</lifecycle>
|
|
163
|
+
<enabled>true</enabled>
|
|
164
|
+
<endpoint>http://127.0.0.1:8188</endpoint>
|
|
165
|
+
<default-model>wan22-ti2v-5b</default-model>
|
|
166
|
+
<dry-run>true</dry-run>
|
|
167
|
+
<tuning>
|
|
168
|
+
<aspect>16:9</aspect>
|
|
169
|
+
<max-duration>5</max-duration>
|
|
170
|
+
<audio-native>false</audio-native>
|
|
171
|
+
</tuning>
|
|
172
|
+
</provider>
|
|
173
|
+
|
|
174
|
+
<!-- ============================================================ -->
|
|
175
|
+
<!-- Audio adapters (music-video orchestration) -->
|
|
176
|
+
<!-- Contract: scripts/ai-video/lib/audio-adapter-contract.md -->
|
|
177
|
+
<!-- local-cli adapters wrap an OPERATOR-installed tool (pip) — -->
|
|
178
|
+
<!-- no api-key element needed; <enabled>false</enabled> opts a -->
|
|
179
|
+
<!-- configured-but-unwanted adapter out (otherwise a missing CLI -->
|
|
180
|
+
<!-- is a loud config failure, never a silent probe fallback). -->
|
|
181
|
+
<!-- ============================================================ -->
|
|
182
|
+
|
|
183
|
+
<provider id="allin1" kind="audio-analysis">
|
|
184
|
+
<lifecycle>experimental</lifecycle>
|
|
185
|
+
<enabled>true</enabled>
|
|
186
|
+
<dry-run>true</dry-run>
|
|
187
|
+
</provider>
|
|
188
|
+
|
|
189
|
+
<provider id="whisperx" kind="lyrics">
|
|
190
|
+
<lifecycle>experimental</lifecycle>
|
|
191
|
+
<enabled>true</enabled>
|
|
192
|
+
<dry-run>true</dry-run>
|
|
193
|
+
</provider>
|
|
194
|
+
|
|
195
|
+
<!-- ============================================================ -->
|
|
196
|
+
<!-- Lip-sync post-process (v2 contract, kind="lipsync") -->
|
|
197
|
+
<!-- Sparse budget: lib/model-capabilities/syncso.json -->
|
|
198
|
+
<!-- ============================================================ -->
|
|
199
|
+
|
|
200
|
+
<!--
|
|
201
|
+
Local (MIT) lip-sync — offline alternative to syncso. Wraps an
|
|
202
|
+
OPERATOR-installed MuseTalk (github.com/TMElyralab/MuseTalk); the
|
|
203
|
+
wrapped entry point is configurable via the MUSETALK_CMD env var.
|
|
204
|
+
No api-key. Same sparse lipsync_budget discipline as syncso
|
|
205
|
+
(lib/model-capabilities/musetalk.json) — local renders are free but
|
|
206
|
+
the quality argument for sparseness is unchanged.
|
|
207
|
+
-->
|
|
208
|
+
<provider id="musetalk" kind="lipsync">
|
|
209
|
+
<lifecycle>experimental</lifecycle>
|
|
210
|
+
<enabled>true</enabled>
|
|
211
|
+
<dry-run>true</dry-run>
|
|
212
|
+
</provider>
|
|
213
|
+
|
|
214
|
+
<provider id="syncso" kind="lipsync">
|
|
215
|
+
<lifecycle>experimental</lifecycle>
|
|
216
|
+
<enabled>true</enabled>
|
|
217
|
+
<api-key>sk-REPLACE-ME</api-key>
|
|
218
|
+
<endpoint>https://api.sync.so</endpoint>
|
|
219
|
+
<default-model>lipsync-2</default-model>
|
|
220
|
+
<dry-run>true</dry-run>
|
|
221
|
+
</provider>
|
|
222
|
+
|
|
223
|
+
<!-- ============================================================ -->
|
|
224
|
+
<!-- Extra slot — future providers without schema bump -->
|
|
225
|
+
<!-- Use kind="image" | "video" | "image+video" | "audio-analysis"-->
|
|
226
|
+
<!-- | "lyrics" | "lipsync". -->
|
|
227
|
+
<!-- ============================================================ -->
|
|
228
|
+
|
|
229
|
+
<extra>
|
|
230
|
+
<!--
|
|
231
|
+
<provider id="my-future-backend" kind="video">
|
|
232
|
+
<lifecycle>experimental</lifecycle>
|
|
233
|
+
<api-key>REPLACE-ME</api-key>
|
|
234
|
+
<endpoint>https://example.invalid/v1</endpoint>
|
|
235
|
+
<default-model>example-1</default-model>
|
|
236
|
+
<dry-run>true</dry-run>
|
|
237
|
+
<tuning>
|
|
238
|
+
<aspect>16:9</aspect>
|
|
239
|
+
<fps>24</fps>
|
|
240
|
+
<max-duration>6</max-duration>
|
|
241
|
+
<audio-native>false</audio-native>
|
|
242
|
+
</tuning>
|
|
243
|
+
</provider>
|
|
244
|
+
-->
|
|
245
|
+
</extra>
|
|
246
|
+
|
|
247
|
+
</ai-video>
|
|
@@ -31,7 +31,7 @@ Use this skill when:
|
|
|
31
31
|
Do NOT use when:
|
|
32
32
|
- `directive_set="ui-trivial"` — the trivial path bypasses audit by precondition (≤1 file, ≤5 lines, no new component, no new state, no new dep)
|
|
33
33
|
- `directive_set="backend"` — no UI surface to inventory
|
|
34
|
-
- The audit findings already exist in `state.ui_audit` for this state-file
|
|
34
|
+
- The audit findings already exist in `state.ui_audit` for this state-file — the gate round-trips through SUCCESS once `audit_path` is set, and does **not** re-run for the life of that state-file (see Gotchas for what that does and does not cover)
|
|
35
35
|
|
|
36
36
|
## Resource-first context gate (design fidelity)
|
|
37
37
|
|
|
@@ -243,7 +243,20 @@ tool produces, so the import path is uniform.
|
|
|
243
243
|
- Don't assume a Radix-only `package.json` means shadcn — shadcn requires `components.json` at repo root.
|
|
244
244
|
- `state.ui_audit.shadcn_inventory.version` is often missing; the shadcn CLI does not always pin itself in `package.json`. Record `null` rather than guessing.
|
|
245
245
|
- Greenfield is detected, not assumed — a project with one Blade layout and no components is still greenfield only if tokens AND design system markers AND components are all empty.
|
|
246
|
-
-
|
|
246
|
+
- **There is no mtime cache.** An earlier revision told you to cache by
|
|
247
|
+
`(composer.json mtime, package.json mtime)` and re-audit when either changed.
|
|
248
|
+
No code implemented it, and the key was the wrong one anyway: `apply` adds
|
|
249
|
+
components without touching a manifest, so a manifest-keyed cache never
|
|
250
|
+
invalidates on the change that matters. What the engine does is simpler and
|
|
251
|
+
stricter — `audit.ts` returns SUCCESS unconditionally once `audit_path` is
|
|
252
|
+
`high_confidence` or `ambiguous`, so the inventory is computed **once per
|
|
253
|
+
state-file** and never refreshed.
|
|
254
|
+
- **What that means in practice.** A single request is one pass, so the
|
|
255
|
+
inventory is fresh where it matters. The bounded gap is a run that reuses one
|
|
256
|
+
state-file across several component additions: components written earlier in
|
|
257
|
+
that run are absent from the inventory the later ones are checked against.
|
|
258
|
+
Adding a second component in the same run → re-read the component directories
|
|
259
|
+
directly rather than trusting `state.ui_audit.components_found`.
|
|
247
260
|
|
|
248
261
|
## Anti-slop cross-reference
|
|
249
262
|
|
|
@@ -63,6 +63,16 @@ where the corpus reports an evidence gap or the audit already pins a
|
|
|
63
63
|
project pattern. Stack-specific choices come from the dispatched
|
|
64
64
|
implementation skill, not from here.
|
|
65
65
|
|
|
66
|
+
> **When the corpus is not installed.** `design-intelligence` ships in the
|
|
67
|
+
> `frontend-design` pack; this skill ships in `engineering-base`. A consumer
|
|
68
|
+
> who installed only `laravel` or only `react` therefore has this skill and
|
|
69
|
+
> **not** the corpus. Fall back to the heuristics here and say so in the
|
|
70
|
+
> result — "selected from heuristics; `frontend-design` not installed, so no
|
|
71
|
+
> corpus grounding". The evidence-gap fallback above is about a corpus that
|
|
72
|
+
> answered "nothing here"; this is about a corpus that is absent. Never
|
|
73
|
+
> present an ungrounded pick as grounded, and never record a missing pack as
|
|
74
|
+
> an evidence gap.
|
|
75
|
+
|
|
66
76
|
## Component Architecture
|
|
67
77
|
|
|
68
78
|
### Page structure (universal shape)
|
|
@@ -39,7 +39,7 @@ schema_version: 1
|
|
|
39
39
|
# CI guard: a release bump of `package.json` must update this value
|
|
40
40
|
# in lockstep — see scripts/check_template_pin_drift.ts (road-to-
|
|
41
41
|
# portable-runtime-and-update-check P3.3).
|
|
42
|
-
agent_config_version: "9.
|
|
42
|
+
agent_config_version: "9.11.0"
|
|
43
43
|
|
|
44
44
|
# --- Project identity ---
|
|
45
45
|
project:
|
|
@@ -17,14 +17,25 @@ import {
|
|
|
17
17
|
StepResult,
|
|
18
18
|
agent_directive,
|
|
19
19
|
} from '../../delivery_state.js';
|
|
20
|
-
import {
|
|
20
|
+
import { placeholder_paths } from './design.js';
|
|
21
|
+
import {
|
|
22
|
+
UNSUPPORTED_LANE,
|
|
23
|
+
bundle_line,
|
|
24
|
+
unsupported_stack_questions,
|
|
25
|
+
} from './stack_bundles.js';
|
|
21
26
|
|
|
22
27
|
/** Map `state.stack.frontend` → agent-directive skill name. */
|
|
23
28
|
export const STACK_DIRECTIVES: Record<string, string> = {
|
|
24
29
|
'blade-livewire-flux': 'ui-apply-blade-livewire-flux',
|
|
30
|
+
'blade-livewire': 'ui-apply-blade-livewire',
|
|
31
|
+
filament: 'ui-apply-filament',
|
|
25
32
|
'react-shadcn': 'ui-apply-react-shadcn',
|
|
33
|
+
react: 'ui-apply-react',
|
|
26
34
|
vue: 'ui-apply-vue',
|
|
27
35
|
plain: 'ui-apply-plain',
|
|
36
|
+
// Present so the `keys == KNOWN_STACKS` invariant holds. Never emitted —
|
|
37
|
+
// the step intercepts this lane and refuses instead of dispatching.
|
|
38
|
+
unknown: 'ui-apply-unsupported',
|
|
28
39
|
};
|
|
29
40
|
|
|
30
41
|
/** Fallback directive when `state.stack` is missing or malformed. */
|
|
@@ -104,47 +115,36 @@ function _resolve_directive(state: DeliveryState): string {
|
|
|
104
115
|
return DEFAULT_DIRECTIVE;
|
|
105
116
|
}
|
|
106
117
|
|
|
107
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* Return paths into `envelope['rendered']` whose text matches a pattern.
|
|
120
|
+
*
|
|
121
|
+
* Delegates to the walker shared with `design`, so the producer-side and
|
|
122
|
+
* consumer-side gates cannot drift apart. They previously held
|
|
123
|
+
* byte-identical copies of the same array-blind recursion, which meant the
|
|
124
|
+
* contract's "defense-in-depth" framing described two layers that failed on
|
|
125
|
+
* exactly the same inputs.
|
|
126
|
+
*/
|
|
108
127
|
function _placeholder_violations_in_output(envelope: Record<string, Any>): string[] {
|
|
109
|
-
|
|
110
|
-
if (!_isDict(rendered)) {
|
|
111
|
-
return [];
|
|
112
|
-
}
|
|
113
|
-
const violations: string[] = [];
|
|
114
|
-
_walk_rendered(rendered, '', violations);
|
|
115
|
-
return violations;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function _walk_rendered(node: Record<string, Any>, prefix: string, violations: string[]): void {
|
|
119
|
-
for (const key of Object.keys(node)) {
|
|
120
|
-
const value = node[key];
|
|
121
|
-
const path = prefix ? `${prefix}.${key}` : String(key);
|
|
122
|
-
if (_isDict(value)) {
|
|
123
|
-
_walk_rendered(value, path, violations);
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
if (typeof value !== 'string') {
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
const lowered = value.toLowerCase();
|
|
130
|
-
for (const pattern of PLACEHOLDER_PATTERNS) {
|
|
131
|
-
if (lowered.includes(pattern)) {
|
|
132
|
-
violations.push(path);
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
128
|
+
return placeholder_paths(envelope['rendered']);
|
|
137
129
|
}
|
|
138
130
|
|
|
139
131
|
/** First-pass halt — emit the stack-specific apply directive. */
|
|
140
132
|
function _delegate_to_stack_skill(state: DeliveryState): StepResult {
|
|
141
133
|
const directive = _resolve_directive(state);
|
|
142
134
|
const stack_label = _stack_label(state);
|
|
135
|
+
if (stack_label === UNSUPPORTED_LANE) {
|
|
136
|
+
return new StepResult({
|
|
137
|
+
outcome: Outcome.BLOCKED,
|
|
138
|
+
questions: unsupported_stack_questions('apply'),
|
|
139
|
+
message: 'UI apply refused: frontend framework not modelled by any lane.',
|
|
140
|
+
});
|
|
141
|
+
}
|
|
143
142
|
return new StepResult({
|
|
144
143
|
outcome: Outcome.BLOCKED,
|
|
145
144
|
questions: [
|
|
146
145
|
agent_directive(directive),
|
|
147
146
|
`> Stack: \`${stack_label}\`. Implementing the locked design brief.`,
|
|
147
|
+
bundle_line(stack_label, 'build'),
|
|
148
148
|
'> Microcopy is locked — every button label, empty-state ' +
|
|
149
149
|
'message, and validation message must come verbatim from ' +
|
|
150
150
|
'`state.ui_design.microcopy`.',
|
|
@@ -146,7 +146,17 @@ function _missing_required_keys(design: Record<string, Any>): string[] {
|
|
|
146
146
|
missing.push(key);
|
|
147
147
|
continue;
|
|
148
148
|
}
|
|
149
|
-
if (key === 'states'
|
|
149
|
+
if (key === 'states') {
|
|
150
|
+
// A non-object `states` (a string, a list) used to skip the
|
|
151
|
+
// per-state loop entirely, so `states: "n/a"` satisfied the gate
|
|
152
|
+
// without covering a single state. Report every required key
|
|
153
|
+
// instead — the author gets the same actionable list either way.
|
|
154
|
+
if (!_isDict(value)) {
|
|
155
|
+
for (const state_key of REQUIRED_STATE_KEYS) {
|
|
156
|
+
missing.push(`states.${state_key}`);
|
|
157
|
+
}
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
150
160
|
for (const state_key of REQUIRED_STATE_KEYS) {
|
|
151
161
|
if (!_pyTruthy(value[state_key])) {
|
|
152
162
|
missing.push(`states.${state_key}`);
|
|
@@ -157,37 +167,74 @@ function _missing_required_keys(design: Record<string, Any>): string[] {
|
|
|
157
167
|
return missing;
|
|
158
168
|
}
|
|
159
169
|
|
|
160
|
-
/**
|
|
170
|
+
/**
|
|
171
|
+
* Return brief paths whose values match a placeholder pattern.
|
|
172
|
+
*
|
|
173
|
+
* Covers `microcopy` **and** `states`. The five required states were checked
|
|
174
|
+
* for truthiness only, so `states.error: "TBD"` satisfied the gate — the brief
|
|
175
|
+
* looked complete while covering nothing. Both slots carry strings the user
|
|
176
|
+
* signs off on, so both are held to the same bar.
|
|
177
|
+
*
|
|
178
|
+
* An explicit `"n/a"` stays legitimate: a static landing page genuinely has no
|
|
179
|
+
* error or disabled state, and *declaring* that is the opposite of inventing
|
|
180
|
+
* filler. That is why the gate demands all five keys rather than branching on
|
|
181
|
+
* page type — the author states the answer instead of the engine guessing which
|
|
182
|
+
* states a surface ought to have.
|
|
183
|
+
*/
|
|
161
184
|
function _placeholder_violations(design: Record<string, Any>): string[] {
|
|
162
|
-
|
|
163
|
-
|
|
185
|
+
return [
|
|
186
|
+
...placeholder_paths(design['microcopy'], 'microcopy'),
|
|
187
|
+
...placeholder_paths(design['states'], 'states'),
|
|
188
|
+
];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Return every path under `node` whose leaf string carries a placeholder.
|
|
193
|
+
*
|
|
194
|
+
* Traverses objects **and arrays**. The array arm is load-bearing, not
|
|
195
|
+
* defensive: a list is the natural shape for the most common microcopy
|
|
196
|
+
* (`nav_items`, `menu`, `steps`) and for a rendered file split into lines,
|
|
197
|
+
* and a walker that recursed into objects only let
|
|
198
|
+
* `{ nav_items: ["Home", "TODO: Link"] }` through both the brief lock and
|
|
199
|
+
* the rendered-output gate.
|
|
200
|
+
*
|
|
201
|
+
* Array elements are addressed `key[i]` so the halt names the exact element
|
|
202
|
+
* the author has to fix, not just the list.
|
|
203
|
+
*
|
|
204
|
+
* Shared by `design` (producer side, on `microcopy`) and `apply` (consumer
|
|
205
|
+
* side, on the rendered envelope). One implementation on purpose: the two
|
|
206
|
+
* call sites previously carried byte-identical copies of the same bug, so
|
|
207
|
+
* duplicating them bought no independence — see
|
|
208
|
+
* `docs/contracts/ui-track-flow.md` § implement → apply.
|
|
209
|
+
*/
|
|
210
|
+
export function placeholder_paths(node: Any, prefix = ''): string[] {
|
|
164
211
|
const violations: string[] = [];
|
|
165
|
-
|
|
212
|
+
_walk_placeholders(node, prefix, violations);
|
|
166
213
|
return violations;
|
|
167
214
|
}
|
|
168
215
|
|
|
169
|
-
function
|
|
170
|
-
node
|
|
171
|
-
|
|
172
|
-
violations: string[],
|
|
173
|
-
): void {
|
|
174
|
-
for (const key of Object.keys(node)) {
|
|
175
|
-
const value = node[key];
|
|
176
|
-
const path = prefix ? `${prefix}.${key}` : String(key);
|
|
177
|
-
if (_isDict(value)) {
|
|
178
|
-
_walk_microcopy(value, path, violations);
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
if (typeof value !== 'string') {
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
const lowered = value.toLowerCase();
|
|
216
|
+
function _walk_placeholders(node: Any, prefix: string, violations: string[]): void {
|
|
217
|
+
if (typeof node === 'string') {
|
|
218
|
+
const lowered = node.toLowerCase();
|
|
185
219
|
for (const pattern of PLACEHOLDER_PATTERNS) {
|
|
186
220
|
if (lowered.includes(pattern)) {
|
|
187
|
-
violations.push(
|
|
188
|
-
|
|
221
|
+
violations.push(prefix);
|
|
222
|
+
return;
|
|
189
223
|
}
|
|
190
224
|
}
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (Array.isArray(node)) {
|
|
228
|
+
node.forEach((value, index) => {
|
|
229
|
+
_walk_placeholders(value, `${prefix}[${index}]`, violations);
|
|
230
|
+
});
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (_isDict(node)) {
|
|
234
|
+
for (const key of Object.keys(node)) {
|
|
235
|
+
const path = prefix ? `${prefix}.${key}` : String(key);
|
|
236
|
+
_walk_placeholders(node[key], path, violations);
|
|
237
|
+
}
|
|
191
238
|
}
|
|
192
239
|
}
|
|
193
240
|
|
|
@@ -311,15 +358,23 @@ function _halt_unconfirmed(state: DeliveryState, design: Record<string, Any>): S
|
|
|
311
358
|
});
|
|
312
359
|
}
|
|
313
360
|
|
|
314
|
-
/**
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
361
|
+
/**
|
|
362
|
+
* Count leaf string entries in `microcopy` (recursive, arrays included).
|
|
363
|
+
*
|
|
364
|
+
* The count is shown in the sign-off the user confirms, so it has to agree
|
|
365
|
+
* with what the placeholder walker inspects — an object-only count reported
|
|
366
|
+
* fewer locked strings than were actually locked.
|
|
367
|
+
*/
|
|
368
|
+
function _count_microcopy(microcopy: Any): number {
|
|
369
|
+
if (typeof microcopy === 'string') return 1;
|
|
370
|
+
if (Array.isArray(microcopy)) {
|
|
371
|
+
return microcopy.reduce((total: number, value) => total + _count_microcopy(value), 0);
|
|
372
|
+
}
|
|
373
|
+
if (_isDict(microcopy)) {
|
|
374
|
+
return Object.values(microcopy).reduce(
|
|
375
|
+
(total: number, value) => total + _count_microcopy(value),
|
|
376
|
+
0,
|
|
377
|
+
);
|
|
323
378
|
}
|
|
324
|
-
return
|
|
379
|
+
return 0;
|
|
325
380
|
}
|
|
@@ -15,6 +15,11 @@ import {
|
|
|
15
15
|
StepResult,
|
|
16
16
|
agent_directive,
|
|
17
17
|
} from '../../delivery_state.js';
|
|
18
|
+
import {
|
|
19
|
+
UNSUPPORTED_LANE,
|
|
20
|
+
bundle_line,
|
|
21
|
+
unsupported_stack_questions,
|
|
22
|
+
} from './stack_bundles.js';
|
|
18
23
|
|
|
19
24
|
/** Maximum number of polish rounds per `/work` run. */
|
|
20
25
|
export const POLISH_CEILING = 2;
|
|
@@ -31,9 +36,15 @@ export const TOKEN_REPEAT_THRESHOLD = 2;
|
|
|
31
36
|
/** Map `state.stack.frontend` → agent-directive skill name. */
|
|
32
37
|
export const STACK_DIRECTIVES: Record<string, string> = {
|
|
33
38
|
'blade-livewire-flux': 'ui-polish-blade-livewire-flux',
|
|
39
|
+
'blade-livewire': 'ui-polish-blade-livewire',
|
|
40
|
+
filament: 'ui-polish-filament',
|
|
34
41
|
'react-shadcn': 'ui-polish-react-shadcn',
|
|
42
|
+
react: 'ui-polish-react',
|
|
35
43
|
vue: 'ui-polish-vue',
|
|
36
44
|
plain: 'ui-polish-plain',
|
|
45
|
+
// Present so the `keys == KNOWN_STACKS` invariant holds. Never emitted —
|
|
46
|
+
// the step intercepts this lane and refuses instead of dispatching.
|
|
47
|
+
unknown: 'ui-polish-unsupported',
|
|
37
48
|
};
|
|
38
49
|
|
|
39
50
|
/** Fallback directive when `state.stack` is missing or malformed. */
|
|
@@ -184,6 +195,14 @@ function _delegate_to_polish_skill(
|
|
|
184
195
|
ceiling: number,
|
|
185
196
|
): StepResult {
|
|
186
197
|
const directive = _resolve_directive(state);
|
|
198
|
+
const unsupported_label = _stack_label(state);
|
|
199
|
+
if (unsupported_label === UNSUPPORTED_LANE) {
|
|
200
|
+
return new StepResult({
|
|
201
|
+
outcome: Outcome.BLOCKED,
|
|
202
|
+
questions: unsupported_stack_questions('polish'),
|
|
203
|
+
message: 'UI polish refused: frontend framework not modelled by any lane.',
|
|
204
|
+
});
|
|
205
|
+
}
|
|
187
206
|
const stack_label = _stack_label(state);
|
|
188
207
|
const next_round = rounds + 1;
|
|
189
208
|
let findings_line =
|
|
@@ -203,6 +222,7 @@ function _delegate_to_polish_skill(
|
|
|
203
222
|
questions: [
|
|
204
223
|
agent_directive(directive),
|
|
205
224
|
`> Stack: \`${stack_label}\`. Polish round ${next_round} of ${ceiling}.`,
|
|
225
|
+
bundle_line(stack_label, 'build'),
|
|
206
226
|
findings_line,
|
|
207
227
|
'> Fix chart-type / contrast findings against the adopted ' +
|
|
208
228
|
"corpus rows (design-intelligence § 'Grounding the " +
|
|
@@ -16,13 +16,24 @@ import {
|
|
|
16
16
|
StepResult,
|
|
17
17
|
agent_directive,
|
|
18
18
|
} from '../../delivery_state.js';
|
|
19
|
+
import {
|
|
20
|
+
UNSUPPORTED_LANE,
|
|
21
|
+
bundle_line,
|
|
22
|
+
unsupported_stack_questions,
|
|
23
|
+
} from './stack_bundles.js';
|
|
19
24
|
|
|
20
|
-
/** Map `state.stack.frontend` → agent-directive
|
|
25
|
+
/** Map `state.stack.frontend` → agent-directive verb. */
|
|
21
26
|
export const STACK_DIRECTIVES: Record<string, string> = {
|
|
22
27
|
'blade-livewire-flux': 'ui-design-review-blade-livewire-flux',
|
|
28
|
+
'blade-livewire': 'ui-design-review-blade-livewire',
|
|
29
|
+
filament: 'ui-design-review-filament',
|
|
23
30
|
'react-shadcn': 'ui-design-review-react-shadcn',
|
|
31
|
+
react: 'ui-design-review-react',
|
|
24
32
|
vue: 'ui-design-review-vue',
|
|
25
33
|
plain: 'ui-design-review-plain',
|
|
34
|
+
// Present so the `keys == KNOWN_STACKS` invariant holds. Never emitted —
|
|
35
|
+
// the step intercepts this lane and refuses instead of dispatching.
|
|
36
|
+
unknown: 'ui-design-review-unsupported',
|
|
26
37
|
};
|
|
27
38
|
|
|
28
39
|
/** Fallback directive when `state.stack` is missing or malformed. */
|
|
@@ -181,12 +192,20 @@ function _stack_label(state: DeliveryState): string {
|
|
|
181
192
|
function _delegate_to_review_skill(state: DeliveryState): StepResult {
|
|
182
193
|
const directive = _resolve_directive(state);
|
|
183
194
|
const stack_label = _stack_label(state);
|
|
195
|
+
if (stack_label === UNSUPPORTED_LANE) {
|
|
196
|
+
return new StepResult({
|
|
197
|
+
outcome: Outcome.BLOCKED,
|
|
198
|
+
questions: unsupported_stack_questions('review'),
|
|
199
|
+
message: 'UI review refused: frontend framework not modelled by any lane.',
|
|
200
|
+
});
|
|
201
|
+
}
|
|
184
202
|
return new StepResult({
|
|
185
203
|
outcome: Outcome.BLOCKED,
|
|
186
204
|
questions: [
|
|
187
205
|
agent_directive(directive),
|
|
188
206
|
`> Stack: \`${stack_label}\`. Reviewing rendered components ` +
|
|
189
207
|
'against the locked design brief.',
|
|
208
|
+
bundle_line(stack_label, 'review'),
|
|
190
209
|
'> The review pass compares `state.ticket.ui_apply.rendered` ' +
|
|
191
210
|
'against `state.ui_design` (microcopy, states, a11y, layout) ' +
|
|
192
211
|
'and produces a structured `findings` list.',
|