@adia-ai/adia-ui-forge 0.8.8 → 0.8.10
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 +22 -0
- package/package.json +1 -1
- package/skills/adia-a2ui/evals/evals.json +23 -0
- package/skills/adia-a2ui/evals/routing-corpus.json +130 -29
- package/skills/adia-author/evals/evals.json +22 -0
- package/skills/adia-author/evals/routing-corpus.json +155 -34
- package/skills/adia-deploy/evals/evals.json +22 -0
- package/skills/adia-deploy/evals/routing-corpus.json +125 -28
- package/skills/adia-dogfood/evals/evals.json +20 -0
- package/skills/adia-dogfood/evals/routing-corpus.json +110 -25
- package/skills/adia-gen-review/evals/evals.json +20 -0
- package/skills/adia-gen-review/evals/routing-corpus.json +110 -25
- package/skills/adia-llm-internals/evals/evals.json +22 -0
- package/skills/adia-llm-internals/evals/routing-corpus.json +125 -28
- package/skills/adia-release/evals/routing-corpus.json +115 -26
- package/skills/adia-release/scripts/pr-bridge.mjs +41 -4
- package/skills/adia-release/scripts/release-pack.mjs +68 -4
- package/skills/adia-site-docs/evals/evals.json +20 -0
- package/skills/adia-site-docs/evals/routing-corpus.json +130 -29
- package/skills/adia-ssr/evals/evals.json +20 -0
- package/skills/adia-ssr/evals/routing-corpus.json +110 -25
|
@@ -1,37 +1,134 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"name": "adia-llm-internals routing accuracy corpus",
|
|
4
|
+
"version": "2.1.0",
|
|
5
|
+
"purpose": "Routing-eval corpus for adia-llm-internals. Each phrase declares the skill (expected), a forbidden skill (expected_not, for phrases the source data only ever asserted as \"not this skill\"), or neither. Scored by scripts/skills/run-skill-evals.mjs (TF-IDF token overlap over per-skill description+triggers).",
|
|
6
|
+
"scoring_notes": "Heuristic signal, not ground truth. Treat misroutes as a prompt to tighten the skill description, never as a reason to keyword-stuff it. Real harness routing is LLM-driven.",
|
|
7
|
+
"scope": "adia-llm-internals routing \u2014 does this phrase activate adia-llm-internals?",
|
|
8
|
+
"phrases": [
|
|
9
|
+
{
|
|
10
|
+
"id": "adia-llm-internals-pos-01",
|
|
11
|
+
"phrase": "the gemini adapter is dropping usage tokens on streamed responses",
|
|
12
|
+
"expected": "adia-llm-internals"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "adia-llm-internals-pos-02",
|
|
16
|
+
"phrase": "stopReason from openai never reaches the client as MAX_TOKENS",
|
|
17
|
+
"expected": "adia-llm-internals"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "adia-llm-internals-pos-03",
|
|
21
|
+
"phrase": "the SSE parser chokes when a chunk splits mid-line",
|
|
22
|
+
"expected": "adia-llm-internals"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "adia-llm-internals-pos-04",
|
|
26
|
+
"phrase": "the passthrough proxy is returning 401 for anthropic requests in production",
|
|
27
|
+
"expected": "adia-llm-internals"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "adia-llm-internals-pos-05",
|
|
31
|
+
"phrase": "add a new model to the MODELS registry and set it as DEFAULT_MODEL",
|
|
32
|
+
"expected": "adia-llm-internals"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "adia-llm-internals-pos-06",
|
|
36
|
+
"phrase": "createAdapter() is leaking a real API key to the browser on a production host",
|
|
37
|
+
"expected": "adia-llm-internals"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": "adia-llm-internals-pos-07",
|
|
41
|
+
"phrase": "add support for a new OpenAI-compatible gateway provider adapter",
|
|
42
|
+
"expected": "adia-llm-internals"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "adia-llm-internals-pos-08",
|
|
46
|
+
"phrase": "streamChat() never emits a terminal done chunk",
|
|
47
|
+
"expected": "adia-llm-internals"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"id": "adia-llm-internals-pos-09",
|
|
51
|
+
"phrase": "detectProvider isn't resolving the new model id to the right adapter",
|
|
52
|
+
"expected": "adia-llm-internals"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id": "adia-llm-internals-pos-10",
|
|
56
|
+
"phrase": "the anthropic adapter's buildRequest() diverges from the passthrough proxy path",
|
|
57
|
+
"expected": "adia-llm-internals"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "adia-llm-internals-pos-11",
|
|
61
|
+
"phrase": "the StreamChunk union is missing a variant for tool_use deltas",
|
|
62
|
+
"expected": "adia-llm-internals"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"id": "adia-llm-internals-pos-12",
|
|
66
|
+
"phrase": "the stub LLM adapter isn't returning parseable A2UI JSON in dev mode",
|
|
67
|
+
"expected": "adia-llm-internals"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"id": "adia-llm-internals-neg-01",
|
|
71
|
+
"phrase": "wire the chat client into our admin app's chat-shell",
|
|
72
|
+
"expected_not": "adia-llm-internals"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"id": "adia-llm-internals-neg-02",
|
|
76
|
+
"phrase": "why did the composer emit composition-synthesized instead of composition-match",
|
|
77
|
+
"expected_not": "adia-llm-internals"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"id": "adia-llm-internals-neg-03",
|
|
81
|
+
"phrase": "cut a release and bump @adia-ai/llm's version with the rest of the lockstep packages",
|
|
82
|
+
"expected_not": "adia-llm-internals"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"id": "adia-llm-internals-neg-04",
|
|
86
|
+
"phrase": "add a new prop to the chat-input-ui primitive",
|
|
87
|
+
"expected_not": "adia-llm-internals"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"id": "adia-llm-internals-neg-05",
|
|
91
|
+
"phrase": "explain how Server-Sent Events work in general",
|
|
92
|
+
"expected_not": "adia-llm-internals"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"id": "adia-llm-internals-neg-06",
|
|
96
|
+
"phrase": "add a chat box to the admin app",
|
|
97
|
+
"expected_not": "adia-llm-internals"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"id": "adia-llm-internals-neg-07",
|
|
101
|
+
"phrase": "the chat-shell message list isn't rendering streamed replies",
|
|
102
|
+
"expected_not": "adia-llm-internals"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"id": "adia-llm-internals-neg-08",
|
|
106
|
+
"phrase": "tune STRONG_MATCH and lift the eval fails in the compose strategies",
|
|
107
|
+
"expected_not": "adia-llm-internals"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"id": "adia-llm-internals-neg-09",
|
|
111
|
+
"phrase": "deploy the ui-kit dist to exe.dev and restart the service",
|
|
112
|
+
"expected_not": "adia-llm-internals"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"id": "adia-llm-internals-neg-10",
|
|
116
|
+
"phrase": "run a dogfood sweep to find broken demos",
|
|
117
|
+
"expected_not": "adia-llm-internals"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"id": "adia-llm-internals-neg-11",
|
|
121
|
+
"phrase": "score the gen-ui gallery outputs against the exit gate",
|
|
122
|
+
"expected_not": "adia-llm-internals"
|
|
123
|
+
}
|
|
15
124
|
],
|
|
16
|
-
"
|
|
17
|
-
"wire the chat client into our admin app's chat-shell",
|
|
18
|
-
"why did the composer emit composition-synthesized instead of composition-match",
|
|
19
|
-
"cut a release and bump @adia-ai/llm's version with the rest of the lockstep packages",
|
|
20
|
-
"add a new prop to the chat-input-ui primitive",
|
|
21
|
-
"explain how Server-Sent Events work in general",
|
|
22
|
-
"add a chat box to the admin app",
|
|
23
|
-
"the chat-shell message list isn't rendering streamed replies",
|
|
24
|
-
"tune STRONG_MATCH and lift the eval fails in the compose strategies",
|
|
25
|
-
"deploy the ui-kit dist to exe.dev and restart the service",
|
|
26
|
-
"run a dogfood sweep to find broken demos",
|
|
27
|
-
"score the gen-ui gallery outputs against the exit gate"
|
|
28
|
-
],
|
|
29
|
-
"_measured": {
|
|
125
|
+
"_measured_historical": {
|
|
30
126
|
"as_of": "2026-07-18",
|
|
31
127
|
"scorer": "routing_eval.py (nonoun-plugins/forge)",
|
|
32
128
|
"f1": 0.857,
|
|
33
129
|
"precision": 1.0,
|
|
34
130
|
"recall": 0.75,
|
|
35
|
-
"exit_code": 0
|
|
131
|
+
"exit_code": 0,
|
|
132
|
+
"note": "measured by the external routing_eval.py (nonoun-plugins/forge) against the pre-conversion positives/negatives shape; historical record only, not regenerated by run-skill-evals.mjs."
|
|
36
133
|
}
|
|
37
134
|
}
|
|
@@ -1,35 +1,124 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"name": "adia-release routing accuracy corpus",
|
|
4
|
+
"version": "2.1.0",
|
|
5
|
+
"purpose": "Routing-eval corpus for adia-release. Each phrase declares the skill (expected), a forbidden skill (expected_not, for phrases the source data only ever asserted as \"not this skill\"), or neither. Scored by scripts/skills/run-skill-evals.mjs (TF-IDF token overlap over per-skill description+triggers).",
|
|
6
|
+
"scoring_notes": "Heuristic signal, not ground truth. Treat misroutes as a prompt to tighten the skill description, never as a reason to keyword-stuff it. Real harness routing is LLM-driven.",
|
|
7
|
+
"scope": "adia-release routing \u2014 does this phrase activate adia-release?",
|
|
8
|
+
"phrases": [
|
|
9
|
+
{
|
|
10
|
+
"id": "adia-release-pos-01",
|
|
11
|
+
"phrase": "Cut and ship v0.8.5 of the AdiaUI packages",
|
|
12
|
+
"expected": "adia-release"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "adia-release-pos-02",
|
|
16
|
+
"phrase": "The [Unreleased] CHANGELOG entries are piling up, let's cut a release",
|
|
17
|
+
"expected": "adia-release"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "adia-release-pos-03",
|
|
21
|
+
"phrase": "Tag the lockstep packages and push them to origin",
|
|
22
|
+
"expected": "adia-release"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "adia-release-pos-04",
|
|
26
|
+
"phrase": "check:lockstep is failing \u2014 one package didn't bump",
|
|
27
|
+
"expected": "adia-release"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "adia-release-pos-05",
|
|
31
|
+
"phrase": "Dispatch the publish workflows for v0.8.5 and verify npm",
|
|
32
|
+
"expected": "adia-release"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "adia-release-pos-06",
|
|
36
|
+
"phrase": "We have three unpushed release commits piled up, batch-push them",
|
|
37
|
+
"expected": "adia-release"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": "adia-release-pos-07",
|
|
41
|
+
"phrase": "F-N1 is warning about a CHANGELOG entry not covering a touched directory",
|
|
42
|
+
"expected": "adia-release"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "adia-release-pos-08",
|
|
46
|
+
"phrase": "The release cut landed on the wrong branch, help me recover",
|
|
47
|
+
"expected": "adia-release"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"id": "adia-release-pos-09",
|
|
51
|
+
"phrase": "Author the release notes and Slack post for the last two versions",
|
|
52
|
+
"expected": "adia-release"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id": "adia-release-pos-10",
|
|
56
|
+
"phrase": "This cut removes the variant prop from button-ui, write the MIGRATION GUIDE section",
|
|
57
|
+
"expected": "adia-release"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "adia-release-pos-11",
|
|
61
|
+
"phrase": "Publish the adia-ui-forge plugin independently of the lockstep set",
|
|
62
|
+
"expected": "adia-release"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"id": "adia-release-pos-12",
|
|
66
|
+
"phrase": "Just verify the release gates are green without cutting anything",
|
|
67
|
+
"expected": "adia-release"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"id": "adia-release-pos-13",
|
|
71
|
+
"phrase": "Tags are pushed but zero publish workflows fired, what happened",
|
|
72
|
+
"expected": "adia-release"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"id": "adia-release-neg-01",
|
|
76
|
+
"phrase": "Restart the exe.dev VM, it's returning 502s",
|
|
77
|
+
"expected_not": "adia-release"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"id": "adia-release-neg-02",
|
|
81
|
+
"phrase": "Rsync the built dist folder to ui-kit.exe.xyz",
|
|
82
|
+
"expected_not": "adia-release"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"id": "adia-release-neg-03",
|
|
86
|
+
"phrase": "Sweep our consumer app for the renamed variant prop after the last breaking release",
|
|
87
|
+
"expected_not": "adia-release"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"id": "adia-release-neg-04",
|
|
91
|
+
"phrase": "Add a new size variant to the button-ui primitive",
|
|
92
|
+
"expected_not": "adia-release"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"id": "adia-release-neg-05",
|
|
96
|
+
"phrase": "Compose a settings screen for our app from the AdiaUI catalog",
|
|
97
|
+
"expected_not": "adia-release"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"id": "adia-release-neg-06",
|
|
101
|
+
"phrase": "zettel coverage dropped on the nightly eval, find out why",
|
|
102
|
+
"expected_not": "adia-release"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"id": "adia-release-neg-07",
|
|
106
|
+
"phrase": "sweep the component demos for native-primitive leaks before we cut",
|
|
107
|
+
"expected_not": "adia-release"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"id": "adia-release-neg-08",
|
|
111
|
+
"phrase": "the gemini adapter is dropping usage tokens on streamed responses",
|
|
112
|
+
"expected": "adia-llm-internals"
|
|
113
|
+
}
|
|
16
114
|
],
|
|
17
|
-
"
|
|
18
|
-
"Restart the exe.dev VM, it's returning 502s",
|
|
19
|
-
"Rsync the built dist folder to ui-kit.exe.xyz",
|
|
20
|
-
"Sweep our consumer app for the renamed variant prop after the last breaking release",
|
|
21
|
-
"Add a new size variant to the button-ui primitive",
|
|
22
|
-
"Compose a settings screen for our app from the AdiaUI catalog",
|
|
23
|
-
"zettel coverage dropped on the nightly eval, find out why",
|
|
24
|
-
"sweep the component demos for native-primitive leaks before we cut",
|
|
25
|
-
"the gemini adapter is dropping usage tokens on streamed responses"
|
|
26
|
-
],
|
|
27
|
-
"_measured": {
|
|
115
|
+
"_measured_historical": {
|
|
28
116
|
"as_of": "2026-07-18",
|
|
29
117
|
"scorer": "routing_eval.py (nonoun-plugins/forge)",
|
|
30
118
|
"f1": 1.0,
|
|
31
119
|
"precision": 1.0,
|
|
32
120
|
"recall": 1.0,
|
|
33
|
-
"exit_code": 0
|
|
121
|
+
"exit_code": 0,
|
|
122
|
+
"note": "measured by the external routing_eval.py (nonoun-plugins/forge) against the pre-conversion positives/negatives shape; historical record only, not regenerated by run-skill-evals.mjs."
|
|
34
123
|
}
|
|
35
124
|
}
|
|
@@ -124,6 +124,20 @@ export function bridgeDecision(state) {
|
|
|
124
124
|
if (!PASS.has(concl)) reasons.push(`check '${c.name}' landed ${concl}`);
|
|
125
125
|
else gatingLanded++;
|
|
126
126
|
}
|
|
127
|
+
// gh#(pr-bridge draft guard): a draft PR is the AUTHOR's own "not ready"
|
|
128
|
+
// signal — GitHub's API refuses to merge one anyway (GraphQL: "Pull
|
|
129
|
+
// Request is still a draft"), and the old code had no isDraft check, so
|
|
130
|
+
// bridgeDecision said MERGE, the gh pr merge call threw, and the whole
|
|
131
|
+
// script crashed uncaught. A caller piping through `grep -E "pr-bridge" |
|
|
132
|
+
// tail -N` (this script's own documented usage pattern) sees only the
|
|
133
|
+
// last N lines BEFORE the crash and no error — reads as quiet success.
|
|
134
|
+
// Unconditional stop (review nit: placed after the per-check loop above,
|
|
135
|
+
// not literally first — but every reasons.push() here is unconditional and
|
|
136
|
+
// the "stop" decision below fires on ANY non-empty reasons list, so a
|
|
137
|
+
// draft overrides an otherwise-perfect merge regardless of ordering):
|
|
138
|
+
// never attempt the merge call on a draft, no matter how green everything
|
|
139
|
+
// else is.
|
|
140
|
+
if (state.isDraft) reasons.push('PR is a DRAFT — the author has not marked it ready for review; never auto-merge a draft');
|
|
127
141
|
if (state.reviewDecision === 'CHANGES_REQUESTED') reasons.push('a review requests changes');
|
|
128
142
|
if (state.unresolvedThreads > 0) reasons.push(`${state.unresolvedThreads} unresolved review thread(s) — admin merge cannot bypass these`);
|
|
129
143
|
if (state.mergeable === 'CONFLICTING') reasons.push('PR is CONFLICTING with the base branch');
|
|
@@ -144,7 +158,7 @@ export function bridgeDecision(state) {
|
|
|
144
158
|
// ── GitHub reads ──────────────────────────────────────────────────
|
|
145
159
|
function readPrState(prNumber) {
|
|
146
160
|
const view = JSON.parse(sh(
|
|
147
|
-
`gh pr view ${prNumber} --json state,mergeable,reviewDecision,statusCheckRollup,headRefName,baseRefName`,
|
|
161
|
+
`gh pr view ${prNumber} --json state,isDraft,mergeable,reviewDecision,statusCheckRollup,headRefName,baseRefName`,
|
|
148
162
|
));
|
|
149
163
|
const checks = (view.statusCheckRollup || []).map((c) => ({
|
|
150
164
|
name: c.name || c.context || '?',
|
|
@@ -156,6 +170,7 @@ function readPrState(prNumber) {
|
|
|
156
170
|
)).data.repository.pullRequest.reviewThreads.nodes;
|
|
157
171
|
return {
|
|
158
172
|
state: view.state,
|
|
173
|
+
isDraft: view.isDraft,
|
|
159
174
|
headRefName: view.headRefName,
|
|
160
175
|
baseRefName: view.baseRefName,
|
|
161
176
|
checks,
|
|
@@ -240,7 +255,19 @@ async function main() {
|
|
|
240
255
|
}
|
|
241
256
|
if (d.action === 'merge') {
|
|
242
257
|
console.log(`\n[pr-bridge] all gates green — merging PR #${prNumber} (admin merge per house flow §5.7)`);
|
|
243
|
-
|
|
258
|
+
// Defense in depth: the isDraft check above should catch this, but
|
|
259
|
+
// GitHub's own merge call is the actual authority — never let its
|
|
260
|
+
// rejection crash uncaught (a caller filtering output for the
|
|
261
|
+
// "pr-bridge" prefix, this script's own documented usage, would see
|
|
262
|
+
// the run go quiet mid-merge with no clear failure, easy to misread
|
|
263
|
+
// as success). Report loudly and exit 1 instead of throwing.
|
|
264
|
+
try {
|
|
265
|
+
sh(`gh pr merge ${prNumber} --admin --merge --delete-branch`);
|
|
266
|
+
} catch (e) {
|
|
267
|
+
console.error(`\n[pr-bridge] STOP — gh pr merge rejected PR #${prNumber}: ${e.message || e}`);
|
|
268
|
+
console.error(' Nothing else mutated by this run; investigate and merge manually if appropriate.');
|
|
269
|
+
process.exit(1);
|
|
270
|
+
}
|
|
244
271
|
break;
|
|
245
272
|
}
|
|
246
273
|
if (Date.now() > deadline) {
|
|
@@ -269,10 +296,12 @@ async function main() {
|
|
|
269
296
|
// ── Selftest — the predicate is the load-bearing part ─────────────
|
|
270
297
|
function selftest() {
|
|
271
298
|
const ck = (name, status, conclusion) => ({ name, status, conclusion });
|
|
272
|
-
const base = { unresolvedThreads: 0, reviewDecision: 'REVIEW_REQUIRED', mergeable: 'MERGEABLE' };
|
|
299
|
+
const base = { unresolvedThreads: 0, reviewDecision: 'REVIEW_REQUIRED', mergeable: 'MERGEABLE', isDraft: false };
|
|
273
300
|
const cases = [
|
|
274
301
|
['all green → merge',
|
|
275
302
|
{ ...base, checks: [ck('Static checks + MCP smoke', 'COMPLETED', 'SUCCESS'), ck('CodeRabbit', '', 'SUCCESS')] }, 'merge'],
|
|
303
|
+
['DRAFT PR with all-green checks → stop, never attempt gh pr merge (real incident: PR #362)',
|
|
304
|
+
{ ...base, isDraft: true, checks: [ck('Static checks + MCP smoke', 'COMPLETED', 'SUCCESS'), ck('CodeRabbit', '', 'SUCCESS')] }, 'stop'],
|
|
276
305
|
['fail-soft failure is advisory → merge',
|
|
277
306
|
{ ...base, checks: [ck('Static checks + MCP smoke', 'COMPLETED', 'SUCCESS'), ck('Audit health (fail-soft)', 'COMPLETED', 'FAILURE')] }, 'merge'],
|
|
278
307
|
['EMPTY check set → wait, never blind-merge (reviewer finding #1)',
|
|
@@ -313,4 +342,12 @@ function selftest() {
|
|
|
313
342
|
|
|
314
343
|
const argv = process.argv.slice(2);
|
|
315
344
|
if (argv[0] === 'selftest') selftest();
|
|
316
|
-
else main().catch((e) => {
|
|
345
|
+
else main().catch((e) => {
|
|
346
|
+
// Reviewer finding (PR #364): every OTHER error path (readPrState()'s gh
|
|
347
|
+
// pr view/graphql calls on every poll, git push/gh pr create, the
|
|
348
|
+
// re-baseline sequence) throws up to this top-level handler, which had NO
|
|
349
|
+
// "[pr-bridge]" prefix — invisible under the exact `grep -E "pr-bridge" |
|
|
350
|
+
// tail -N` filter this PR exists to fix. Same class, different call site.
|
|
351
|
+
console.error(`[pr-bridge] STOP — ${e.message || e}`);
|
|
352
|
+
process.exit(1);
|
|
353
|
+
});
|
|
@@ -229,6 +229,21 @@ function shCapture(cmd, args) {
|
|
|
229
229
|
// ── Step 1 — Re-baseline ──────────────────────────────────────────
|
|
230
230
|
function step1ReBaseline(args) {
|
|
231
231
|
console.log('\n=== Step 1 — Re-baseline ===');
|
|
232
|
+
// cut-procedure.md §Step 1 requires `git branch --show-current` == main
|
|
233
|
+
// before a cut starts — cutting on any other branch pushes a stale main ref
|
|
234
|
+
// while tags point at the wrong tip (recovery-paths.md §Scenario 8). The doc
|
|
235
|
+
// asserted this; nothing here ever checked it (gh#358). The one legitimate
|
|
236
|
+
// exception is resuming this SAME cut after step5Commit already created and
|
|
237
|
+
// checked out release/vX.Y.Z — allow only that exact branch name back in.
|
|
238
|
+
if ((args.mode === 'cut' || args.mode === 'from-scratch') && !args.dry) {
|
|
239
|
+
const branch = shQuiet('git branch --show-current', args).trim();
|
|
240
|
+
const releaseBranch = `release/v${args.version}`;
|
|
241
|
+
if (branch !== 'main' && branch !== releaseBranch) {
|
|
242
|
+
console.error(`ERROR: on branch '${branch}' — cut modes start from 'main' (or resume on '${releaseBranch}'). Switch branches before proceeding.`);
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
console.log(` branch: ${branch} (ok)`);
|
|
246
|
+
}
|
|
232
247
|
console.log('git status --short:');
|
|
233
248
|
sh('git status --short', args);
|
|
234
249
|
console.log('\norigin/main..HEAD (unpushed):');
|
|
@@ -362,6 +377,30 @@ function step5Commit(args) {
|
|
|
362
377
|
return;
|
|
363
378
|
}
|
|
364
379
|
console.log('\n=== Step 5 — Stage + Commit ===');
|
|
380
|
+
// cut-procedure.md §Step 5: "Branch FIRST, then stage" — this step never
|
|
381
|
+
// actually created release/vX.Y.Z, so the commit landed on whatever branch
|
|
382
|
+
// was checked out (main, on the v0.8.8 cut — gh#358). Idempotent: if the
|
|
383
|
+
// branch already exists (a resumed run), check it out rather than failing;
|
|
384
|
+
// step1ReBaseline's branch assertion is what stops a genuinely wrong branch
|
|
385
|
+
// from reaching this point.
|
|
386
|
+
const releaseBranch = `release/v${args.version}`;
|
|
387
|
+
const currentBranch = args.dry ? null : shQuiet('git branch --show-current', args).trim();
|
|
388
|
+
if (currentBranch !== releaseBranch) {
|
|
389
|
+
const exists = !args.dry && shQuiet(`git branch --list ${releaseBranch}`, args).trim().length > 0;
|
|
390
|
+
// Reviewer finding (PR #359): checking out an EXISTING release branch (a
|
|
391
|
+
// resumed run) on a dirty tree fails with a raw, opaque git error — Step 4
|
|
392
|
+
// (promote/bump/lockfile) runs immediately before this and dirties exactly
|
|
393
|
+
// the files a release branch differs from main in. A fresh `-b` create
|
|
394
|
+
// from clean main can't hit this, so the check only guards the resume path.
|
|
395
|
+
if (exists && !args.dry) {
|
|
396
|
+
const dirty = shQuiet('git status --porcelain', args).trim();
|
|
397
|
+
if (dirty) {
|
|
398
|
+
console.error(`ERROR: '${releaseBranch}' already exists, but the working tree is dirty — checking out would fail or silently carry these changes onto the resumed branch:\n${dirty}\n Commit, stash, or discard first, then re-run.`);
|
|
399
|
+
process.exit(1);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
sh(`git checkout ${exists ? '' : '-b '}${releaseBranch}`, args);
|
|
403
|
+
}
|
|
365
404
|
sh('git reset HEAD >/dev/null 2>&1 || true', args);
|
|
366
405
|
const releaseFiles = [
|
|
367
406
|
'package-lock.json',
|
|
@@ -512,11 +551,34 @@ async function step9Publish(args) {
|
|
|
512
551
|
// Give GH a moment to index the runs created by the per-tag push in Step 8.
|
|
513
552
|
if (!args.dry) sh('sleep 6', args);
|
|
514
553
|
sh(`node ${SCRIPT_DIR}/dispatch-publish.mjs --version ${args.version} --scope ${args.scope} --verify-triggered`, args);
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
554
|
+
// gh#354: this used to poll ONE workflow (publish-a2ui-validator.yml) as a
|
|
555
|
+
// sample-of-one proxy for all 11 publishes settling — a straggler on any
|
|
556
|
+
// OTHER package's workflow made the fail-closed check below abort on a pure
|
|
557
|
+
// timing false alarm (hit live on the v0.8.8 cut: a2ui-retrieval published
|
|
558
|
+
// successfully ~1 minute after validator did, after the old proxy had
|
|
559
|
+
// already declared "settled" and the registry read caught it mid-flight).
|
|
560
|
+
// Poll the REGISTRY itself instead — the skill's own doctrine (SKILL.md:
|
|
561
|
+
// "the registry is the verify target, never a workflow's green check")
|
|
562
|
+
// applies here as much as it does to the final check.
|
|
563
|
+
console.log(`\n Waiting for the registry to confirm all ${PACKAGES.length} packages at ${args.version} (polling every 15s, up to 10min)...`);
|
|
564
|
+
sh(
|
|
565
|
+
`for i in $(seq 1 40); do ` +
|
|
566
|
+
`fail=0; ` +
|
|
567
|
+
`for pkg in ${PACKAGES.join(' ')}; do ` +
|
|
568
|
+
`v="$(npm view "${args.scope}/$pkg" version 2>/dev/null || true)"; ` +
|
|
569
|
+
`[ "$v" = "${args.version}" ] || fail=1; ` +
|
|
570
|
+
`done; ` +
|
|
571
|
+
`latest="$(npm view ${args.scope}/web-components dist-tags.latest 2>/dev/null || true)"; ` +
|
|
572
|
+
`[ "$latest" = "${args.version}" ] || fail=1; ` +
|
|
573
|
+
`[ "$fail" = 0 ] && { echo " ✓ registry confirms all ${PACKAGES.length} + latest, after $i check(s)"; exit 0; }; ` +
|
|
574
|
+
`sleep 15; ` +
|
|
575
|
+
`done; ` +
|
|
576
|
+
`echo " registry did not converge after 10min — the fail-closed check below will report exactly which package(s) are still stale"`,
|
|
577
|
+
args,
|
|
578
|
+
);
|
|
518
579
|
// Fail-closed: the registry is the verify target (SKILL.md) — a missing or
|
|
519
|
-
// stale package aborts the cut here, before GH releases + site deploy.
|
|
580
|
+
// stale package aborts the cut here, before GH releases + site deploy. This
|
|
581
|
+
// is now a final confirmation of the poll above, not the first real read.
|
|
520
582
|
console.log(`\n Post-publish registry verification, all ${PACKAGES.length} at ${args.version} + npm latest (fail-closed):`);
|
|
521
583
|
sh(
|
|
522
584
|
`fail=0; ` +
|
|
@@ -648,6 +710,8 @@ function selftest() {
|
|
|
648
710
|
`${GATE_ROSTER.length}-gate roster`,
|
|
649
711
|
`✓ all ${GATE_ROSTER.length}/${GATE_ROSTER.length} gates passed`,
|
|
650
712
|
'STOPPING per invariant 3',
|
|
713
|
+
// gh#358 — the release branch must actually be created, not just documented.
|
|
714
|
+
'git checkout -b release/v9.9.9',
|
|
651
715
|
];
|
|
652
716
|
for (const marker of need) {
|
|
653
717
|
if (!out.includes(marker)) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "adia-site-docs",
|
|
3
|
+
"note": "Trigger-routing suite (gh#369). expect=trigger: the skill should fire on the prompt in a fresh session. expect=no-trigger: a near-miss owned elsewhere (owner named per case) or generic knowledge with no AdiaUI skill involved. Cases derived from evals/routing-corpus.json's phrases array (gh#355 vetted corpus) plus the SKILL.md description's own trigger/NOT-fence language.",
|
|
4
|
+
"cases": [
|
|
5
|
+
{ "id": "t01", "prompt": "Review this site docs page for consistency with the others", "expect": "trigger" },
|
|
6
|
+
{ "id": "t02", "prompt": "This callout on the docs page reads as plain text, not a styled box — why?", "expect": "trigger" },
|
|
7
|
+
{ "id": "t03", "prompt": "Add a new pattern page under site/pages/patterns for the kanban-board pattern", "expect": "trigger" },
|
|
8
|
+
{ "id": "t04", "prompt": "Author a new getting-started guide page for theming dark mode", "expect": "trigger" },
|
|
9
|
+
{ "id": "t05", "prompt": "Write a new architecture explainer page for the docs site", "expect": "trigger" },
|
|
10
|
+
{ "id": "t06", "prompt": "Create a reference page documenting the MCP tools", "expect": "trigger" },
|
|
11
|
+
{ "id": "t07", "prompt": "Audit site/pages/guides for the same callout bug we found elsewhere", "expect": "trigger" },
|
|
12
|
+
{ "id": "t08", "prompt": "What's the shared skeleton every site docs page should follow?", "expect": "trigger" },
|
|
13
|
+
{ "id": "n01", "prompt": "Review this component's own demo gallery in its .examples.html file", "expect": "no-trigger", "owner": "adia-author" },
|
|
14
|
+
{ "id": "n02", "prompt": "Fix this gen-ui training exemplar page under site/pages/examples", "expect": "no-trigger", "owner": "adia-a2ui" },
|
|
15
|
+
{ "id": "n03", "prompt": "Tune the zettel retrieval threshold for the chunk corpus", "expect": "no-trigger", "owner": "adia-a2ui" },
|
|
16
|
+
{ "id": "n04", "prompt": "Add a new prop to the button-ui primitive", "expect": "no-trigger", "owner": "adia-author" },
|
|
17
|
+
{ "id": "n05", "prompt": "Cut the next npm release for the 9 @adia-ai packages", "expect": "no-trigger", "owner": "adia-release" },
|
|
18
|
+
{ "id": "n06", "prompt": "Deploy the docs site to ui-kit.exe.xyz", "expect": "no-trigger", "owner": "adia-deploy" }
|
|
19
|
+
]
|
|
20
|
+
}
|