@adia-ai/adia-ui-forge 0.8.4 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +27 -2
- package/README.md +13 -4
- package/agents/a2ui-builder.corpus.json +35 -0
- package/agents/a2ui-builder.md +37 -0
- package/agents/component-author.corpus.json +40 -0
- package/agents/component-author.md +10 -1
- package/agents/framework-reviewer.corpus.json +35 -0
- package/agents/framework-reviewer.md +42 -0
- package/agents/release-builder.corpus.json +39 -0
- package/agents/release-builder.md +39 -0
- package/agents/routing-corpus.json +38 -38
- package/bin/release-pretag-docs-gate +226 -0
- package/commands/release.md +5 -3
- package/hooks/hooks.json +10 -0
- package/package.json +1 -1
- package/skills/adia-a2ui/SKILL.md +37 -8
- package/skills/adia-a2ui/evals/routing-corpus.json +36 -29
- package/skills/adia-a2ui/references/pipeline-overview.md +1 -1
- package/skills/adia-a2ui/references/zettel-calibration.md +5 -5
- package/skills/adia-author/SKILL.md +16 -8
- package/skills/adia-author/evals/routing-corpus.json +39 -218
- package/skills/adia-author/references/anti-patterns.md +7 -7
- package/skills/adia-author/references/api-contract.md +9 -9
- package/skills/adia-author/references/authoring-cycle.md +25 -6
- package/skills/adia-author/references/code-style.md +4 -5
- package/skills/adia-author/references/lifecycle-patterns.md +8 -8
- package/skills/adia-author/references/llm-bridge.md +7 -0
- package/skills/adia-author/references/token-contract.md +5 -5
- package/skills/adia-author/references/worked-example.md +34 -18
- package/skills/adia-author/references/yaml-contract.md +1 -1
- package/skills/adia-deploy/SKILL.md +71 -12
- package/skills/adia-deploy/evals/routing-corpus.json +37 -0
- package/skills/adia-deploy/references/deploy-playbooks.md +1 -1
- package/skills/adia-dogfood/SKILL.md +71 -11
- package/skills/adia-dogfood/evals/routing-corpus.json +34 -0
- package/skills/adia-dogfood/references/admin-shell-anatomy.md +1 -1
- package/skills/adia-gen-review/SKILL.md +73 -7
- package/skills/adia-gen-review/evals/routing-corpus.json +34 -0
- package/skills/adia-gen-review/scripts/gen-review-decompose.mjs +5 -0
- package/skills/adia-llm-internals/SKILL.md +10 -6
- package/skills/adia-llm-internals/evals/routing-corpus.json +37 -0
- package/skills/adia-llm-internals/references/bridge-facade.md +2 -2
- package/skills/adia-llm-internals/references/streaming-sse.md +1 -1
- package/skills/adia-release/SKILL.md +43 -13
- package/skills/adia-release/evals/evals.json +24 -0
- package/skills/adia-release/evals/routing-corpus.json +35 -0
- package/skills/adia-release/references/changelog-discipline.md +15 -9
- package/skills/adia-release/references/cut-procedure.md +78 -24
- package/skills/adia-release/references/gates-catalog.md +3 -3
- package/skills/adia-release/references/independent-package-release.md +1 -1
- package/skills/adia-release/references/recovery-paths.md +4 -2
- package/skills/adia-release/scripts/bump.mjs +32 -1
- package/skills/adia-release/scripts/dispatch-publish.mjs +49 -11
- package/skills/adia-release/scripts/gate-roster.mjs +71 -0
- package/skills/adia-release/scripts/insert-stub.mjs +36 -2
- package/skills/adia-release/scripts/promote-unreleased.mjs +35 -1
- package/skills/adia-release/scripts/release-pack.mjs +315 -56
- package/skills/adia-release/scripts/tag-lockstep.mjs +29 -7
- package/skills/adia-site-docs/SKILL.md +6 -1
- package/skills/adia-site-docs/evals/routing-corpus.json +35 -173
- package/skills/adia-ssr/SKILL.md +128 -0
- package/skills/adia-ssr/evals/audit-report.md +63 -0
- package/skills/adia-ssr/evals/routing-corpus.json +34 -0
- package/skills/adia-ssr/references/consumer-workarounds.md +81 -0
- package/skills/adia-ssr/references/failure-shapes.md +164 -0
- package/skills/adia-ssr/references/guard-patterns.md +183 -0
- package/skills/adia-ssr/references/status-ledger.md +33 -0
- package/skills/adia-ssr/references/test-without-linkedom.md +129 -0
- package/agents/a2ui-engineer.md +0 -26
- package/agents/framework-verifier.md +0 -26
- package/agents/release-engineer.md +0 -26
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""release-pretag-docs-gate — PreToolUse gate on irreversible release verbs.
|
|
3
|
+
|
|
4
|
+
Fires before Bash runs a command that makes a release irreversible — tag
|
|
5
|
+
creation (tag-lockstep without --delete), release-tag pushes, publish
|
|
6
|
+
dispatch, or release-pack's handoff mode — and requires the release docs
|
|
7
|
+
to already be in shape: every lockstep CHANGELOG carries the version's
|
|
8
|
+
section (no leftover [Unreleased] content), entry READMEs exist, and the
|
|
9
|
+
team release notes are generated at docs/releases/v<X>.md. The actual
|
|
10
|
+
checks live in the repo (scripts/release/check-release-docs.mjs) so the
|
|
11
|
+
git pre-push hook shares them; this wrapper only classifies the command,
|
|
12
|
+
derives the version, and converts the gate's findings into a deny with
|
|
13
|
+
the repair commands attached.
|
|
14
|
+
|
|
15
|
+
Born from the v0.8.4 near-miss (2026-07-17): the promotion step was
|
|
16
|
+
skipped, F-N1 fired only after tagging, and the publish shipped tarballs
|
|
17
|
+
with [Unreleased] CHANGELOG headers. This gate runs the same checks at
|
|
18
|
+
the last reversible moment instead.
|
|
19
|
+
|
|
20
|
+
Deliberately quiet: non-Bash events, non-release commands, --delete /
|
|
21
|
+
--dry runs, and repos without the gate script (consumer installs of this
|
|
22
|
+
plugin) all exit 0 with no output.
|
|
23
|
+
|
|
24
|
+
Usage:
|
|
25
|
+
release-pretag-docs-gate --hook # PreToolUse mode: event JSON on stdin
|
|
26
|
+
release-pretag-docs-gate selftest # prove the classifier on fixtures
|
|
27
|
+
"""
|
|
28
|
+
import json
|
|
29
|
+
import os
|
|
30
|
+
import re
|
|
31
|
+
import subprocess
|
|
32
|
+
import sys
|
|
33
|
+
|
|
34
|
+
GATE_REL = "scripts/release/check-release-docs.mjs"
|
|
35
|
+
|
|
36
|
+
# Irreversible release verbs -> how to find the version in the command.
|
|
37
|
+
VERSION_FLAG = re.compile(r"--version[= ]['\"]?(\d+\.\d+\.\d+)")
|
|
38
|
+
# A pushed TAG is a bare token like `v0.8.4`, `web-components-v0.8.4`, or
|
|
39
|
+
# the full-form `refs/tags/v0.8.4`; branch refs (`release/v0.8.4`,
|
|
40
|
+
# `refs/heads/…`) stay reversible and deliberately don't match, as do tag
|
|
41
|
+
# DELETIONS (`:refs/tags/v0.8.4` — leading colon).
|
|
42
|
+
TAG_TOKEN = re.compile(r"^(?:[a-z0-9-]+-)?v(\d+\.\d+\.\d+)$")
|
|
43
|
+
|
|
44
|
+
# Sentinel: the command is release-irreversible but no single version can
|
|
45
|
+
# be derived (e.g. `git push --tags`) — deny with the doctrine message
|
|
46
|
+
# rather than fail open.
|
|
47
|
+
AMBIGUOUS = "AMBIGUOUS"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def classify(command):
|
|
51
|
+
"""Return the target version string when `command` is an irreversible
|
|
52
|
+
release verb, AMBIGUOUS when it is irreversible but version-less
|
|
53
|
+
(fail closed), else None."""
|
|
54
|
+
if not command:
|
|
55
|
+
return None
|
|
56
|
+
if "tag-lockstep.mjs" in command or "dispatch-publish.mjs" in command \
|
|
57
|
+
or ("release-pack.mjs" in command and "--mode handoff" in command):
|
|
58
|
+
# Reversal / preview / selftest invocations only suppress for these
|
|
59
|
+
# script verbs (selftest earned its row on first live fire: a
|
|
60
|
+
# compound running `tag-lockstep.mjs selftest` next to an unrelated
|
|
61
|
+
# --version flag denied — 2026-07-17).
|
|
62
|
+
if "--delete" in command or "--dry" in command or "selftest" in command:
|
|
63
|
+
return None
|
|
64
|
+
m = VERSION_FLAG.search(command)
|
|
65
|
+
return m.group(1) if m else None
|
|
66
|
+
push_m = re.search(r"\bgit\b[^|;&\n]*\bpush\b", command)
|
|
67
|
+
if push_m:
|
|
68
|
+
# Scan ONLY the push command's own segment — a compound command's
|
|
69
|
+
# unrelated text (a PR body mentioning "v0.8.4", a commit message)
|
|
70
|
+
# must not classify a branch push as a tag push (live false
|
|
71
|
+
# positive 2026-07-17: `git push <branch> … && gh pr create
|
|
72
|
+
# --body "…v0.8.4…"` denied).
|
|
73
|
+
segment = re.split(r"[|;&\n]", command[push_m.start():])[0]
|
|
74
|
+
# `git push --dry-run` / `-n` never moves refs.
|
|
75
|
+
if "--dry-run" in segment or re.search(r"\s-n\b", segment):
|
|
76
|
+
return None
|
|
77
|
+
# Blanket tag pushes can carry release tags AND break the
|
|
78
|
+
# one-tag-per-push publish doctrine — fail closed.
|
|
79
|
+
if "--tags" in segment or "--follow-tags" in segment:
|
|
80
|
+
return AMBIGUOUS
|
|
81
|
+
for token in segment.split():
|
|
82
|
+
token = token.strip("\"'")
|
|
83
|
+
if token.startswith("refs/tags/"):
|
|
84
|
+
token = token[len("refs/tags/"):]
|
|
85
|
+
m = TAG_TOKEN.match(token)
|
|
86
|
+
if m:
|
|
87
|
+
return m.group(1)
|
|
88
|
+
return None
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def find_repo_gate(start):
|
|
93
|
+
"""Walk up from `start` to the git root looking for the gate script —
|
|
94
|
+
a release command launched from a subdirectory must not bypass the
|
|
95
|
+
gate just because cwd isn't the repo root."""
|
|
96
|
+
d = os.path.abspath(start or os.getcwd())
|
|
97
|
+
for _ in range(12):
|
|
98
|
+
gate = os.path.join(d, GATE_REL)
|
|
99
|
+
if os.path.isfile(gate):
|
|
100
|
+
return d, gate
|
|
101
|
+
if os.path.isdir(os.path.join(d, ".git")):
|
|
102
|
+
return None, None # repo root reached, no gate — consumer repo
|
|
103
|
+
parent = os.path.dirname(d)
|
|
104
|
+
if parent == d:
|
|
105
|
+
return None, None
|
|
106
|
+
d = parent
|
|
107
|
+
return None, None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def deny(reason):
|
|
111
|
+
print(json.dumps({
|
|
112
|
+
"hookSpecificOutput": {
|
|
113
|
+
"hookEventName": "PreToolUse",
|
|
114
|
+
"permissionDecision": "deny",
|
|
115
|
+
"permissionDecisionReason": reason,
|
|
116
|
+
}
|
|
117
|
+
}))
|
|
118
|
+
sys.exit(0)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def hook_mode():
|
|
122
|
+
try:
|
|
123
|
+
event = json.load(sys.stdin)
|
|
124
|
+
except Exception:
|
|
125
|
+
sys.exit(0)
|
|
126
|
+
if event.get("tool_name") != "Bash":
|
|
127
|
+
sys.exit(0)
|
|
128
|
+
version = classify((event.get("tool_input") or {}).get("command", ""))
|
|
129
|
+
if not version:
|
|
130
|
+
sys.exit(0)
|
|
131
|
+
root, gate = find_repo_gate(event.get("cwd"))
|
|
132
|
+
if not gate:
|
|
133
|
+
sys.exit(0) # consumer repo — nothing to enforce here
|
|
134
|
+
if version == AMBIGUOUS:
|
|
135
|
+
deny(
|
|
136
|
+
"release-pretag-docs-gate · blanket tag push in the release monorepo\n"
|
|
137
|
+
"`git push --tags`/`--follow-tags` can carry release tags unchecked AND\n"
|
|
138
|
+
"batched tag pushes drop the publish-on-tag triggers (invariant 4).\n"
|
|
139
|
+
"Push release tags ONE per `git push origin <tag>` instead."
|
|
140
|
+
)
|
|
141
|
+
try:
|
|
142
|
+
proc = subprocess.run(
|
|
143
|
+
["node", gate, "--version", version],
|
|
144
|
+
capture_output=True, text=True, cwd=root, timeout=60,
|
|
145
|
+
)
|
|
146
|
+
except (subprocess.TimeoutExpired, OSError) as e:
|
|
147
|
+
# Fail CLOSED on the release path — an erroring gate is not a pass.
|
|
148
|
+
deny(
|
|
149
|
+
"release-pretag-docs-gate · the docs gate itself errored (" + type(e).__name__ + ")\n"
|
|
150
|
+
"Run it directly to diagnose: node " + GATE_REL + " --version " + version
|
|
151
|
+
)
|
|
152
|
+
if proc.returncode == 0:
|
|
153
|
+
sys.exit(0)
|
|
154
|
+
findings = (proc.stderr or proc.stdout).strip()
|
|
155
|
+
deny(
|
|
156
|
+
"release-pretag-docs-gate · release docs not ready for v" + version + "\n"
|
|
157
|
+
+ findings + "\n"
|
|
158
|
+
"This command is irreversible (tags/publish) — land the doc fixes via PR first\n"
|
|
159
|
+
"(invariant 3), then re-run. If a finding seems wrong, report it against\n"
|
|
160
|
+
"scripts/release/check-release-docs.mjs; do not bypass the gate."
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def selftest():
|
|
165
|
+
cases = [
|
|
166
|
+
("node .claude/skills/adia-release/scripts/tag-lockstep.mjs --version 0.8.4", "0.8.4"),
|
|
167
|
+
("node .claude/skills/adia-release/scripts/tag-lockstep.mjs --version 0.8.4 --delete", None),
|
|
168
|
+
("node scripts/x/dispatch-publish.mjs --version 1.2.3 --verify-triggered", "1.2.3"),
|
|
169
|
+
("node release-pack.mjs --mode handoff --version 0.9.0 --yes", "0.9.0"),
|
|
170
|
+
("node release-pack.mjs --mode cut --version 0.9.0", None),
|
|
171
|
+
("node release-pack.mjs --mode handoff --version 0.9.0 --dry", None),
|
|
172
|
+
("git push origin web-components-v0.8.4", "0.8.4"),
|
|
173
|
+
("git push origin v0.8.4", "0.8.4"),
|
|
174
|
+
('git -C "$REPO" push origin v0.8.4', "0.8.4"),
|
|
175
|
+
("git push origin refs/tags/v0.8.4", "0.8.4"),
|
|
176
|
+
("git push origin :refs/tags/v0.8.4", None), # tag DELETION is a reversal
|
|
177
|
+
("git push origin v0.8.4 --dry-run", None),
|
|
178
|
+
("git push --tags origin", AMBIGUOUS),
|
|
179
|
+
("git push --follow-tags origin main", AMBIGUOUS),
|
|
180
|
+
("node tag-lockstep.mjs --version '0.8.4'", "0.8.4"),
|
|
181
|
+
("git push origin main", None),
|
|
182
|
+
("git push -u origin release/v0.8.4", None), # branch push is reversible
|
|
183
|
+
# Compound: branch push + unrelated version text in a later command
|
|
184
|
+
# (PR bodies) must NOT classify as a tag push.
|
|
185
|
+
('git push -u origin feat/x 2>&1 | tail -1\ngh pr create --body "the four v0.8.4 defects"', None),
|
|
186
|
+
('git push origin v0.8.4 && echo done', "0.8.4"),
|
|
187
|
+
("npm run build", None),
|
|
188
|
+
("", None),
|
|
189
|
+
]
|
|
190
|
+
for cmd, want in cases:
|
|
191
|
+
got = classify(cmd)
|
|
192
|
+
if got != want:
|
|
193
|
+
print(f"selftest FAIL: classify({cmd!r}) = {got!r}, want {want!r}", file=sys.stderr)
|
|
194
|
+
sys.exit(1)
|
|
195
|
+
|
|
196
|
+
# find_repo_gate walks up from a subdirectory (tempdir fixture — the
|
|
197
|
+
# v1 bug checked cwd only, so any subdir cwd bypassed the gate).
|
|
198
|
+
import tempfile
|
|
199
|
+
with tempfile.TemporaryDirectory() as td:
|
|
200
|
+
os.makedirs(os.path.join(td, ".git"))
|
|
201
|
+
os.makedirs(os.path.join(td, "scripts", "release"))
|
|
202
|
+
open(os.path.join(td, GATE_REL), "w").write("// fixture\n")
|
|
203
|
+
sub = os.path.join(td, "packages", "deep", "nested")
|
|
204
|
+
os.makedirs(sub)
|
|
205
|
+
root, gate = find_repo_gate(sub)
|
|
206
|
+
if root != td or not gate:
|
|
207
|
+
print(f"selftest FAIL: find_repo_gate walk-up ({root!r})", file=sys.stderr)
|
|
208
|
+
sys.exit(1)
|
|
209
|
+
with tempfile.TemporaryDirectory() as td:
|
|
210
|
+
os.makedirs(os.path.join(td, ".git"))
|
|
211
|
+
sub = os.path.join(td, "src")
|
|
212
|
+
os.makedirs(sub)
|
|
213
|
+
root, gate = find_repo_gate(sub)
|
|
214
|
+
if gate is not None:
|
|
215
|
+
print("selftest FAIL: consumer repo must yield no gate", file=sys.stderr)
|
|
216
|
+
sys.exit(1)
|
|
217
|
+
print("selftest OK")
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
if __name__ == "__main__":
|
|
221
|
+
if len(sys.argv) > 1 and sys.argv[1] == "selftest":
|
|
222
|
+
selftest()
|
|
223
|
+
elif len(sys.argv) > 1 and sys.argv[1] == "--hook":
|
|
224
|
+
hook_mode()
|
|
225
|
+
else:
|
|
226
|
+
print(__doc__)
|
package/commands/release.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Cut, tag, publish, and deploy an @adia-ai release (11-package lockstep — 9 npm libraries + 2 Claude Code plugins).
|
|
2
|
+
description: Cut, tag, publish, and deploy an @adia-ai release (11-package lockstep — 9 npm libraries + 2 Claude Code plugins). This invocation IS the authorization for the whole cycle; only gate failures stop it.
|
|
3
3
|
argument-hint: "[version or package]"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Run a release. **$ARGUMENTS**
|
|
7
7
|
|
|
8
8
|
Invoke **`adia-release`** and run its cut procedure end-to-end: pre-flight
|
|
9
|
-
gates → cut → tag → publish → deploy → notes
|
|
10
|
-
|
|
9
|
+
gates → cut → tag → publish → deploy → notes. This command's invocation is
|
|
10
|
+
the operator's single authorization for the entire cycle (§Authorization,
|
|
11
|
+
operator ruling 2026-07-17) — run inline, don't re-ask per step; stop only
|
|
12
|
+
on a gate failure. The skill owns the discipline; don't restate it here.
|
package/hooks/hooks.json
CHANGED
|
@@ -9,6 +9,16 @@
|
|
|
9
9
|
"command": "python3 \"${CLAUDE_PLUGIN_ROOT}/bin/sidecar-prewrite-guard\" --hook"
|
|
10
10
|
}
|
|
11
11
|
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"matcher": "Bash",
|
|
15
|
+
"hooks": [
|
|
16
|
+
{
|
|
17
|
+
"type": "command",
|
|
18
|
+
"command": "python3 \"${CLAUDE_PLUGIN_ROOT}/bin/release-pretag-docs-gate\" --hook",
|
|
19
|
+
"timeout": 90
|
|
20
|
+
}
|
|
21
|
+
]
|
|
12
22
|
}
|
|
13
23
|
],
|
|
14
24
|
"PostToolUse": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adia-ai/adia-ui-forge",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.6",
|
|
4
4
|
"description": "Maintain the adia-ui (@adia-ai) framework itself \u2014 author primitives and shells, run the A2UI generation pipeline and its corpus, review gen-UI quality, sweep QA, cut releases, deploy. The maintainer counterpart to adia-factory (the consumer/app-author plugin).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"adia-ui",
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: adia-a2ui
|
|
3
3
|
description: >-
|
|
4
|
-
Maintains the A2UI pipeline (packages/a2ui/): chunk corpus,
|
|
5
|
-
strategies,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
Maintains the A2UI pipeline (packages/a2ui/): the harvested chunk corpus,
|
|
5
|
+
compose strategies (zettel, chunk-zettel, free-form, monolithic), retrieval,
|
|
6
|
+
validator, calibration, evals, the a2ui MCP server. Use when asked to
|
|
7
|
+
author/harvest/fix chunks, tune STRONG_MATCH or zettel thresholds, validate
|
|
8
|
+
an A2UI document, diagnose an eval gap/regression or lift a semantic fail,
|
|
9
|
+
add or change MCP tools (generate_ui, compose_from_chunks,
|
|
10
|
+
check_anti_patterns, refine_composition), scan anti-patterns, run pipeline
|
|
11
|
+
ops, or when a component's A2UI contract can't express a content shape. NOT
|
|
12
|
+
for app screens (adia-compose), runtime gen-UI app features (adia-genui),
|
|
13
|
+
primitive authoring (adia-author), or gallery scoring (adia-gen-review).
|
|
9
14
|
disable-model-invocation: false
|
|
10
15
|
user-invocable: true
|
|
11
16
|
---
|
|
@@ -73,7 +78,7 @@ Unmatched work defaults to pipeline-overview and re-classifies from there.
|
|
|
73
78
|
Full structural gate after any pipeline change:
|
|
74
79
|
|
|
75
80
|
```bash
|
|
76
|
-
node scripts/build/components.mjs --verify # "clean — N files up-to-date" (
|
|
81
|
+
node scripts/build/components.mjs --verify # "clean — N files up-to-date" (the N is the gate; dir count drifts, don't pin it)
|
|
77
82
|
npm run verify:traits # 100% coverage
|
|
78
83
|
npm run smoke:engines
|
|
79
84
|
npm run smoke:register-engine # 11/11
|
|
@@ -81,10 +86,18 @@ npm run test:a2ui # 22/22 (+1 skipped OK)
|
|
|
81
86
|
npm run eval:diff -- --engine zettel # floors: cov≥87, avg≥85, MRR≥0.94
|
|
82
87
|
npm run check:zettel-eval-regression -- --latest --strict # mechanical floor gate
|
|
83
88
|
npm run check:free-form-eval-regression -- --latest # free-form twin
|
|
84
|
-
npm run eval:diff -- --engine free-form # floors: cov≥
|
|
89
|
+
npm run eval:diff -- --engine free-form # floors: cov≥88, avg≥85, F1≥52
|
|
85
90
|
```
|
|
86
91
|
|
|
87
|
-
|
|
92
|
+
The two `check:*-eval-regression` scripts (`scripts/release/check-zettel-eval-regression.mjs`,
|
|
93
|
+
`scripts/release/check-free-form-eval-regression.mjs`) own the floor numbers — read
|
|
94
|
+
their `ALERT_FLOOR` constants before quoting a number here; this file only mirrors
|
|
95
|
+
them and can drift (it once silently regressed to `cov≥40` before the mechanical
|
|
96
|
+
gate existed).
|
|
97
|
+
|
|
98
|
+
Monolithic floor: cov=100, avg≥95. Dogfood set: 20/20, avg≥95. No mechanical
|
|
99
|
+
regression gate exists for monolithic yet — this floor is convention-only, same
|
|
100
|
+
failure mode the zettel/free-form gates were built to close. A failing gate
|
|
88
101
|
is the artifact — fix at the source (chunk HTML, engine code, tool schema),
|
|
89
102
|
re-run the narrowest gate, then the full sequence. A threshold tweak that
|
|
90
103
|
papers over a failing gate is a regression, not a fix.
|
|
@@ -101,3 +114,19 @@ intent → retrieval (chunk / composition search)
|
|
|
101
114
|
Every change touches exactly one stage; identify which before patching. History
|
|
102
115
|
for any constant or decision lives in git and PR descriptions
|
|
103
116
|
(`git log -S STRONG_MATCH_THRESHOLD -- packages/a2ui`).
|
|
117
|
+
|
|
118
|
+
## Pipeline Change Record — the output contract
|
|
119
|
+
|
|
120
|
+
Every change reports:
|
|
121
|
+
|
|
122
|
+
| Field | Value |
|
|
123
|
+
| --- | --- |
|
|
124
|
+
| Stage touched | retrieval \| strategy engine (zettel/chunk-zettel/free-form/monolithic) \| composer \| validator \| MCP surface |
|
|
125
|
+
| Narrowest gate run | the specific check for the touched stage, + result |
|
|
126
|
+
| Full sequence | `npm run smoke:engines` + `npm run test:a2ui` result |
|
|
127
|
+
| Floors before → after | cov/avg/MRR (zettel) or cov/avg/F1 (free-form) or cov/avg (monolithic) |
|
|
128
|
+
| Re-baseline | no / yes — if yes, the PR that ratified the new floor |
|
|
129
|
+
|
|
130
|
+
Done when every row is filled and the cited gates are green. NOT done: a
|
|
131
|
+
floor number changed with no before/after comparison, or a threshold tweak
|
|
132
|
+
with no root-cause note for why the floor moved.
|
|
@@ -1,31 +1,38 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
2
|
+
"positives": [
|
|
3
|
+
"validate this A2UI document",
|
|
4
|
+
"harvest a chunk from this HTML demo",
|
|
5
|
+
"zettel coverage dropped after the last change",
|
|
6
|
+
"should this repeated subtree become its own chunk",
|
|
7
|
+
"tune the STRONG_MATCH threshold",
|
|
8
|
+
"run check_anti_patterns on this rendered HTML",
|
|
9
|
+
"refine composition with an OAuth row",
|
|
10
|
+
"MRR dropped in the chunk retrieval eval",
|
|
11
|
+
"add an MCP tool to the a2ui server",
|
|
12
|
+
"audit the training data corpus for drift",
|
|
13
|
+
"diagnose this eval gap and regression",
|
|
14
|
+
"run the MCP pipeline: generate_ui then validate_schema",
|
|
15
|
+
"lift a semantic fail under sixty",
|
|
16
|
+
"why did the composer emit composition-match instead of composition-synthesized"
|
|
17
|
+
],
|
|
18
|
+
"negatives": [
|
|
19
|
+
"build a settings page from existing primitives",
|
|
20
|
+
"add a slot to button-ui and update its yaml contract",
|
|
21
|
+
"cut a release and bump the version tag",
|
|
22
|
+
"score the generated UI quality in apps/genui",
|
|
23
|
+
"run a broad visual QA sweep across site and playgrounds",
|
|
24
|
+
"fix the @adia-ai/llm SSE adapter",
|
|
25
|
+
"explain how React state management works",
|
|
26
|
+
"compose a settings screen from catalog primitives",
|
|
27
|
+
"mount gen-root and wire data resolvers for end users at runtime",
|
|
28
|
+
"edit the getting-started docs page on the site"
|
|
29
|
+
],
|
|
30
|
+
"_measured": {
|
|
31
|
+
"as_of": "2026-07-18",
|
|
32
|
+
"scorer": "routing_eval.py (nonoun-plugins/forge)",
|
|
33
|
+
"f1": 0.783,
|
|
34
|
+
"precision": 1.0,
|
|
35
|
+
"recall": 0.643,
|
|
36
|
+
"exit_code": 0
|
|
37
|
+
}
|
|
31
38
|
}
|
|
@@ -14,7 +14,7 @@ lifecycle), `.claude/docs/specs/package-architecture.md` (package relations).
|
|
|
14
14
|
| File | Role |
|
|
15
15
|
| --- | --- |
|
|
16
16
|
| `packages/a2ui/compose/core/generator.js` | `generate_ui` orchestrator — instant / pro / thinking / stream modes; multi-turn via `executionId` |
|
|
17
|
-
| `packages/a2ui/compose/strategies/registry.js` | Engine registry — `registerEngine(name, factory)`. Reserved names: `monolithic`, `
|
|
17
|
+
| `packages/a2ui/compose/strategies/registry.js` | Engine registry — `registerEngine(name, factory)`. Reserved names: `monolithic`, `monolithic-instant`, `monolithic-pro`, `monolithic-thinking`, `zettel`, `chunk-zettel`, `free-form` |
|
|
18
18
|
| `packages/a2ui/compose/strategies/zettel/` | Zettel + chunk-zettel engines — see [strategy-engines](strategy-engines.md) for the per-file map |
|
|
19
19
|
| `packages/a2ui/compose/strategies/free-form-composer/` | Free-form engine (`index.js`, `system-prompt.js`, `transpile.js`) |
|
|
20
20
|
| `packages/a2ui/compose/strategies/_shared/chunk-loader.js` | Shared chunk loading for engines |
|
|
@@ -8,7 +8,7 @@ is calibrated against the held-out intent set or production telemetry.
|
|
|
8
8
|
|
|
9
9
|
## `STRONG_MATCH_THRESHOLD = 40`
|
|
10
10
|
|
|
11
|
-
- **File**: `packages/a2ui/compose/strategies/zettel/generator-adapter.js
|
|
11
|
+
- **File**: `packages/a2ui/compose/strategies/zettel/generator-adapter.js` (`grep -n STRONG_MATCH_THRESHOLD`)
|
|
12
12
|
- **Raised**: 22 → 40 post-incident.
|
|
13
13
|
- **Reason**: at 22, login-form / signup-form played verbatim too often →
|
|
14
14
|
repetitive output. At 40, only near-perfect retrievals (chart-dashboard=48,
|
|
@@ -29,7 +29,7 @@ is calibrated against the held-out intent set or production telemetry.
|
|
|
29
29
|
|
|
30
30
|
## `STRONG_RETRIEVAL_SCORE = 8`
|
|
31
31
|
|
|
32
|
-
- **File**: `chunk-synthesizer.js
|
|
32
|
+
- **File**: `chunk-synthesizer.js` (`grep -n STRONG_RETRIEVAL_SCORE`)
|
|
33
33
|
- **Scale**: corpus-size-**independent** absolute keyword score from
|
|
34
34
|
`chunk-library.js#keywordScore()`: first name-word matches a token **+10**,
|
|
35
35
|
full-query substring **+5**, whole-word token in name **+3**, substring /
|
|
@@ -46,7 +46,7 @@ is calibrated against the held-out intent set or production telemetry.
|
|
|
46
46
|
|
|
47
47
|
## `PRE_SEARCH_LIMIT = 30`
|
|
48
48
|
|
|
49
|
-
- **Files**: `chunk-synthesizer.js
|
|
49
|
+
- **Files**: `chunk-synthesizer.js` + `chunk-refiner.js` (`grep -n PRE_SEARCH_LIMIT`)
|
|
50
50
|
- **Reason**: token-budget mitigation — pre-filter the catalog before the LLM
|
|
51
51
|
sees it; the full catalog per prompt would burn tens of thousands of tokens.
|
|
52
52
|
- **Synthesizer**: kind-aware allocation —
|
|
@@ -61,7 +61,7 @@ is calibrated against the held-out intent set or production telemetry.
|
|
|
61
61
|
|
|
62
62
|
## `SCOPE_DRIFT_RATIO = 1.5` + `SCOPE_DRIFT_MIN_ACTUAL = 20`
|
|
63
63
|
|
|
64
|
-
- **File**: `chunk-synthesizer.js
|
|
64
|
+
- **File**: `chunk-synthesizer.js` (exported; `grep -n SCOPE_DRIFT`)
|
|
65
65
|
- Composed envelope's component count > 1.5× the sum of bound chunks' counts
|
|
66
66
|
auto-fires a `scope-drift` issue — catches LLM creative expansion that
|
|
67
67
|
hallucinates components beyond the bound chunks.
|
|
@@ -70,7 +70,7 @@ is calibrated against the held-out intent set or production telemetry.
|
|
|
70
70
|
|
|
71
71
|
## `DEFAULT_MAX_ATTEMPTS = 2`
|
|
72
72
|
|
|
73
|
-
- **Files**: `chunk-refiner.js
|
|
73
|
+
- **Files**: `chunk-refiner.js` + `chunk-synthesizer.js` (`grep -n DEFAULT_MAX_ATTEMPTS`)
|
|
74
74
|
- Validator-driven retry budget. After 2 failed validations the engine emits
|
|
75
75
|
`synthesis-failed`.
|
|
76
76
|
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: adia-author
|
|
3
3
|
description: >-
|
|
4
|
-
Author or modify
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
Author or modify AdiaUI framework source inside the monorepo — primitives
|
|
5
|
+
(packages/web-components), shells/composites (packages/web-modules), yaml
|
|
6
|
+
SoTs, demos. Use when asked to add a new primitive component, add or fix a prop/slot/
|
|
7
|
+
attribute/CSS variant, update a component's yaml, build or fix a shell
|
|
8
|
+
(chat-shell, admin-shell, editor-shell — sidebar/pane/bespoke-tier
|
|
9
|
+
composition), promote or extract repeated inline content into a shared
|
|
10
|
+
module, audit a component's four-axis contract/token usage/lifecycle
|
|
11
|
+
(connected/disconnected symmetry) for drift, or author/write a demo or
|
|
12
|
+
examples.html for a component or composite. NOT for composing app screens
|
|
13
|
+
from primitives (adia-compose), A2UI pipeline internals (adia-a2ui),
|
|
14
|
+
@adia-ai/llm adapter/streaming/bridge internals (adia-llm-internals), or
|
|
15
|
+
site/pages docs pages (adia-site-docs).
|
|
9
16
|
disable-model-invocation: false
|
|
10
17
|
user-invocable: true
|
|
11
18
|
---
|
|
@@ -14,7 +21,9 @@ user-invocable: true
|
|
|
14
21
|
|
|
15
22
|
Guard rails for code that lands INSIDE the adia-ui monorepo: primitives
|
|
16
23
|
(`packages/web-components/`), shells and composites (`packages/web-modules/`),
|
|
17
|
-
their `<name>.yaml` SoTs,
|
|
24
|
+
their `<name>.yaml` SoTs, and composite demos. `@adia-ai/llm` internals
|
|
25
|
+
(adapters, streaming, the bridge) are `adia-llm-internals`'s charter, not
|
|
26
|
+
this skill's — route there.
|
|
18
27
|
Light-DOM is load-bearing: `slot=` attributes are decorative metadata,
|
|
19
28
|
positioning is CSS by tag + ancestor + DOM order — never `::slotted()`,
|
|
20
29
|
`::part()`, or shadow DOM. `.claude/docs/specs/component-token-contract.md`
|
|
@@ -30,7 +39,6 @@ wins any tie with this skill. Monorepo source read while authoring (yaml, CSS,
|
|
|
30
39
|
| Shell / bespoke cluster child (`<admin-*>` `<chat-*>` `<editor-*>` `<simple-*>`) | [shell-patterns.md](references/shell-patterns.md) |
|
|
31
40
|
| Promote repeated inline UI → shared module | [module-promotion.md](references/module-promotion.md) |
|
|
32
41
|
| Contract / token / lifecycle drift audit on an existing component | [token-contract.md](references/token-contract.md) + [anti-patterns.md](references/anti-patterns.md) |
|
|
33
|
-
| Extend `@adia-ai/llm` (provider, streaming, proxy) | [llm-bridge.md](references/llm-bridge.md) |
|
|
34
42
|
| Demo for a composite/module — any `packages/web-modules/**/*.{examples,contents}.html` | [composite-demo-protocol.md](references/composite-demo-protocol.md) — NOT the primitive or promotion paths; they lack the canonical-survey discipline |
|
|
35
43
|
| Convention question ("is this idiomatic?") | [code-style.md](references/code-style.md) — cite the rule, don't expand it inline |
|
|
36
44
|
|
|
@@ -82,7 +90,6 @@ citation mechanically on every web-modules demo write; the
|
|
|
82
90
|
| Shell | render `packages/web-modules/<cluster>/<name>/<name>.html`; `node scripts/dev/audit-native-primitive-leak.mjs --include=<surface>`; `node scripts/dev/audit-shell-composition.mjs --include=<surface>` for `<admin-shell>` composites |
|
|
83
91
|
| Inline → module promotion | source page renders identically post-refactor (visual sweep + DOM diff) + native-primitive-leak audit on the touched surface |
|
|
84
92
|
| Drift audit | the findings report itself; gates run once a fix is applied |
|
|
85
|
-
| LLM-bridge extension | a real client round-trip parses + returns (`npm run check:lockstep` + `npm run smoke:engines`) |
|
|
86
93
|
| Composite demo | `npm run audit:demo-pattern-source:strict` + `npm run qa:design-coherence:strict` + (8b) `npm run qa:rendered-dom:emit -- --slug=<slug>` |
|
|
87
94
|
|
|
88
95
|
Full structural-gate sequence after any primitive / shell / promotion work:
|
|
@@ -93,6 +100,7 @@ npm run verify:traits # 100% coverage
|
|
|
93
100
|
npm run smoke:engines # green
|
|
94
101
|
node scripts/dev/audit-native-primitive-leak.mjs # 0 critical leaks
|
|
95
102
|
node scripts/dev/audit-shell-composition.mjs # 0 critical defects
|
|
103
|
+
node scripts/dev/audit-template-child-conflict.mjs # 0 critical (gh#284 shape — non-null template + slots.default)
|
|
96
104
|
```
|
|
97
105
|
|
|
98
106
|
A failed gate is the artifact: fix at the source, re-run the narrowest gate,
|