page-foundry 3.2.1 → 3.4.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/README.md +1 -1
- package/package.json +1 -1
- package/skills/page-foundry/SKILL.md +34 -10
- package/skills/page-foundry/TESTS.md +12 -0
- package/skills/page-foundry/references/design-direction.md +9 -6
- package/skills/page-foundry/references/ship-gates.md +11 -5
- package/skills/page-foundry/references/voice.md +6 -0
- package/skills/page-foundry/scripts/run_audit.py +455 -6
- package/skills/page-foundry/tests/build_fixture.sh +74 -1
- package/skills/page-foundry/tests/run_audit_test.sh +197 -0
|
@@ -21,6 +21,17 @@ assert_exit() { # name expected dir extra...
|
|
|
21
21
|
fi
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
assert_grep() { # name mode(has|no) pattern dir
|
|
25
|
+
local name="$1" mode="$2" pattern="$3" dir="$4"
|
|
26
|
+
local out
|
|
27
|
+
out="$(python3 "$AUDIT" --mode build --archetype oss-project "$dir" 2>&1)"
|
|
28
|
+
if echo "$out" | grep -q "$pattern"; then
|
|
29
|
+
if [ "$mode" = has ]; then echo " ok $name"; else echo " FAIL $name (unexpected match)"; fails=$((fails+1)); fi
|
|
30
|
+
else
|
|
31
|
+
if [ "$mode" = no ]; then echo " ok $name"; else echo " FAIL $name (pattern not found)"; fails=$((fails+1)); fi
|
|
32
|
+
fi
|
|
33
|
+
}
|
|
34
|
+
|
|
24
35
|
bash "$BUILD" "$TMP/clean" "$AUDIT" >/dev/null
|
|
25
36
|
assert_exit "A: complete valid run passes" 0 "$TMP/clean/.agents/foundry/demo"
|
|
26
37
|
|
|
@@ -87,6 +98,192 @@ open(p, "w").write(t)
|
|
|
87
98
|
PY
|
|
88
99
|
assert_exit "J: impeccable overridden -> DESIGN.md absence is advisory" 0 "$TMP/impoverride/.agents/foundry/demo"
|
|
89
100
|
|
|
101
|
+
# K: the clean fixture declares both taste inputs -> no taste WARN (#41).
|
|
102
|
+
assert_grep "K: complete taste inputs -> no taste warning" no "taste input missing" "$TMP/clean/.agents/foundry/demo"
|
|
103
|
+
|
|
104
|
+
# L: voice default + design-reference none, no override -> both taste WARNs, still exit 0 (honest PARTIAL).
|
|
105
|
+
cp -r "$TMP/clean" "$TMP/notaste"
|
|
106
|
+
D="$TMP/notaste/.agents/foundry/demo"
|
|
107
|
+
python3 - "$D/preflight.md" <<'PY'
|
|
108
|
+
import sys
|
|
109
|
+
p = sys.argv[1]; t = open(p).read()
|
|
110
|
+
t = t.replace("voice: configured", "voice: default")
|
|
111
|
+
t = t.replace("design-reference: supplied: a real terminal instrument panel the owner named",
|
|
112
|
+
"design-reference: none")
|
|
113
|
+
open(p, "w").write(t)
|
|
114
|
+
PY
|
|
115
|
+
assert_exit "L: missing taste inputs is PARTIAL, not a hard fail" 0 "$D"
|
|
116
|
+
assert_grep "L: missing voice input warns" has "voice taste input missing" "$D"
|
|
117
|
+
assert_grep "L: missing design reference warns" has "design reference missing" "$D"
|
|
118
|
+
|
|
119
|
+
# M: same absence but named on the override line -> waived, no taste WARN.
|
|
120
|
+
cp -r "$TMP/notaste" "$TMP/tasteoverride"
|
|
121
|
+
D="$TMP/tasteoverride/.agents/foundry/demo"
|
|
122
|
+
python3 - "$D/preflight.md" <<'PY'
|
|
123
|
+
import sys
|
|
124
|
+
p = sys.argv[1]; t = open(p).read()
|
|
125
|
+
open(p, "w").write(t.replace("override: none", "override: voice, design-reference"))
|
|
126
|
+
PY
|
|
127
|
+
assert_grep "M: overridden taste inputs -> no warning" no "taste input missing" "$D"
|
|
128
|
+
assert_grep "M: overridden design reference -> no warning" no "design reference missing" "$D"
|
|
129
|
+
|
|
130
|
+
# N: humanizer.md present but diff-less (backfill prose) -> FAIL (#42 tier 2).
|
|
131
|
+
cp -r "$TMP/clean" "$TMP/nodiff"
|
|
132
|
+
D="$TMP/nodiff/.agents/foundry/demo"
|
|
133
|
+
printf '# Humanizer pass\nThe humanizer pass ran over the approved draft in full.\nThe copy reads clean and human throughout the page.\nNothing stood out as an AI tell on this read.\nNo further notes; it looks good and is ready to ship.\n' > "$D/copy/humanizer.md"
|
|
134
|
+
assert_exit "N: diff-less humanizer.md fails (proves it ran)" 1 "$D"
|
|
135
|
+
assert_grep "N: diff-less humanizer names the reason" has "before/after diffs" "$D"
|
|
136
|
+
|
|
137
|
+
# O: red-team.md missing -> FAIL; present but no walks -> FAIL (#42 tier 2).
|
|
138
|
+
cp -r "$TMP/clean" "$TMP/nort"
|
|
139
|
+
rm "$TMP/nort/.agents/foundry/demo/red-team.md"
|
|
140
|
+
assert_exit "O: missing red-team.md fails" 1 "$TMP/nort/.agents/foundry/demo"
|
|
141
|
+
cp -r "$TMP/clean" "$TMP/thinrt"
|
|
142
|
+
D="$TMP/thinrt/.agents/foundry/demo"
|
|
143
|
+
printf '# Red team\nThe page was reviewed against a few readers and it holds up well overall here.\n' > "$D/red-team.md"
|
|
144
|
+
assert_exit "O: walk-less red-team.md fails" 1 "$D"
|
|
145
|
+
|
|
146
|
+
# P: detect-scan.json missing (impeccable core) -> FAIL; overridden -> advisory.
|
|
147
|
+
cp -r "$TMP/clean" "$TMP/noscan"
|
|
148
|
+
rm "$TMP/noscan/.impeccable/detect-scan.json"
|
|
149
|
+
assert_exit "P: missing detect-scan.json (impeccable core) fails" 1 "$TMP/noscan/.agents/foundry/demo"
|
|
150
|
+
assert_grep "P: missing detect scan names Gate 5 persistence" has "detect.mjs --json output" "$TMP/noscan/.agents/foundry/demo"
|
|
151
|
+
cp -r "$TMP/noscan" "$TMP/scanoverride"
|
|
152
|
+
python3 - "$TMP/scanoverride/.agents/foundry/demo/preflight.md" <<'PY'
|
|
153
|
+
import sys
|
|
154
|
+
p = sys.argv[1]; t = open(p).read()
|
|
155
|
+
t = t.replace("| impeccable | core | present | npx impeccable |",
|
|
156
|
+
"| impeccable | core | missing | — |")
|
|
157
|
+
t = t.replace("override: none", "override: impeccable")
|
|
158
|
+
open(p, "w").write(t)
|
|
159
|
+
PY
|
|
160
|
+
rm -f "$TMP/scanoverride/DESIGN.md"
|
|
161
|
+
assert_exit "P: impeccable overridden -> detect-scan absence advisory" 0 "$TMP/scanoverride/.agents/foundry/demo"
|
|
162
|
+
|
|
163
|
+
# Q: the un-fakeable teeth -- shipped copy carries a banned word -> fresh re-run FAILs (#42 tier 1).
|
|
164
|
+
cp -r "$TMP/clean" "$TMP/badvoice"
|
|
165
|
+
D="$TMP/badvoice/.agents/foundry/demo"
|
|
166
|
+
python3 - "$D/copy/copy-approved.md" <<'PY'
|
|
167
|
+
import sys
|
|
168
|
+
p = sys.argv[1]; t = open(p).read()
|
|
169
|
+
open(p, "w").write(t.replace("nobody can tell what it does.",
|
|
170
|
+
"nobody can tell what this robust platform does."))
|
|
171
|
+
PY
|
|
172
|
+
assert_exit "Q: copy that fails a fresh voice scan fails Gate 0" 1 "$D"
|
|
173
|
+
assert_grep "Q: voice re-run names the failure" has "fresh voice scan" "$D"
|
|
174
|
+
|
|
175
|
+
# R: detect-scan.json present but malformed JSON -> FAIL (#42 tier 1 shape check).
|
|
176
|
+
cp -r "$TMP/clean" "$TMP/badscan"
|
|
177
|
+
printf 'not json at all {[' > "$TMP/badscan/.impeccable/detect-scan.json"
|
|
178
|
+
assert_exit "R: malformed detect-scan.json fails" 1 "$TMP/badscan/.agents/foundry/demo"
|
|
179
|
+
|
|
180
|
+
# S: taste-judge.md missing -> FAIL (#44, required in build/explore).
|
|
181
|
+
cp -r "$TMP/clean" "$TMP/notaste_judge"
|
|
182
|
+
rm "$TMP/notaste_judge/.agents/foundry/demo/taste-judge.md"
|
|
183
|
+
assert_exit "S: missing taste-judge.md fails" 1 "$TMP/notaste_judge/.agents/foundry/demo"
|
|
184
|
+
|
|
185
|
+
# T: taste-judge.md with neither a verdict+tells nor the honest no-judge clause -> FAIL;
|
|
186
|
+
# the honest "no independent judge available" clause -> PASS (do not fake independence).
|
|
187
|
+
cp -r "$TMP/clean" "$TMP/emptyjudge"
|
|
188
|
+
D="$TMP/emptyjudge/.agents/foundry/demo"
|
|
189
|
+
printf '# Taste judge\nThe page was looked over and it seems fine and pretty good overall to me.\nNothing much else to add here on the design.\nLooks acceptable.\n' > "$D/taste-judge.md"
|
|
190
|
+
assert_exit "T: taste-judge without verdict+tells fails" 1 "$D"
|
|
191
|
+
cp -r "$TMP/clean" "$TMP/nojudge"
|
|
192
|
+
D="$TMP/nojudge/.agents/foundry/demo"
|
|
193
|
+
printf '# Taste judge\nNo cross-family judge was available this run and no human review either.\nIndependence was not possible; recorded honestly rather than faked.\nRe-run with a codex/GPT judge or a human when one is reachable.\n' > "$D/taste-judge.md"
|
|
194
|
+
assert_exit "T: honest no-judge clause passes (independence not faked)" 0 "$D"
|
|
195
|
+
|
|
196
|
+
# U: mechanics-heavy copy -> soft WARN, still exit 0 (#45); mechanics-as-value:yes silences it.
|
|
197
|
+
cp -r "$TMP/clean" "$TMP/mech"
|
|
198
|
+
D="$TMP/mech/.agents/foundry/demo"
|
|
199
|
+
python3 - "$D/copy/copy-approved.md" <<'PY'
|
|
200
|
+
import sys
|
|
201
|
+
p = sys.argv[1]; t = open(p).read()
|
|
202
|
+
t += "\nEvery page clears the gates before it leaves the shop.\n"
|
|
203
|
+
t += "The pipeline runs every phase and the spec comes first.\n"
|
|
204
|
+
open(p, "w").write(t)
|
|
205
|
+
PY
|
|
206
|
+
assert_exit "U: mechanics-heavy copy is a soft WARN, not a fail" 0 "$D"
|
|
207
|
+
assert_grep "U: mechanics framing warns" has "process/mechanics references" "$D"
|
|
208
|
+
cp -r "$TMP/mech" "$TMP/mechvalue"
|
|
209
|
+
D="$TMP/mechvalue/.agents/foundry/demo"
|
|
210
|
+
printf '# Design strategy\nmechanics-as-value: yes\nThis product IS its mechanics (a gated pipeline); selling the gates is correct here.\n' > "$D/design-strategy.md"
|
|
211
|
+
assert_grep "U: mechanics-as-value:yes silences the warn" no "process/mechanics references" "$D"
|
|
212
|
+
|
|
213
|
+
# V: design-strategy.md missing -> FAIL (#46, default-on); declined on the override line -> advisory.
|
|
214
|
+
cp -r "$TMP/clean" "$TMP/nostrat"
|
|
215
|
+
rm "$TMP/nostrat/.agents/foundry/demo/design-strategy.md"
|
|
216
|
+
assert_exit "V: missing design-strategy.md fails" 1 "$TMP/nostrat/.agents/foundry/demo"
|
|
217
|
+
cp -r "$TMP/nostrat" "$TMP/stratoverride"
|
|
218
|
+
python3 - "$TMP/stratoverride/.agents/foundry/demo/preflight.md" <<'PY'
|
|
219
|
+
import sys
|
|
220
|
+
p = sys.argv[1]; t = open(p).read()
|
|
221
|
+
open(p, "w").write(t.replace("override: none", "override: design-strategy"))
|
|
222
|
+
PY
|
|
223
|
+
assert_exit "V: design-strategy declined at preflight -> advisory" 0 "$TMP/stratoverride/.agents/foundry/demo"
|
|
224
|
+
|
|
225
|
+
# W: design-strategy.md that skips the mechanics decision / grounding / kill-list -> FAIL (#46).
|
|
226
|
+
cp -r "$TMP/clean" "$TMP/thinstrat"
|
|
227
|
+
D="$TMP/thinstrat/.agents/foundry/demo"
|
|
228
|
+
printf '# Design strategy\nWe will make it look modern and clean and nice.\nSome ideas about fonts and colors go here.\nWe think it will be good and appropriate for the product.\nIt should feel premium and trustworthy to visitors overall.\n' > "$D/design-strategy.md"
|
|
229
|
+
assert_exit "W: design-strategy without decision/grounding/kill-list fails" 1 "$D"
|
|
230
|
+
assert_grep "W: names the missing mechanics decision" has "mechanics-as-value" "$D"
|
|
231
|
+
|
|
232
|
+
# X: taste judge verdict FLAT on an axis, no accept -> blocking FAIL (#48).
|
|
233
|
+
cp -r "$TMP/clean" "$TMP/flatjudge"
|
|
234
|
+
D="$TMP/flatjudge/.agents/foundry/demo"
|
|
235
|
+
python3 - "$D/taste-judge.md" <<'PY'
|
|
236
|
+
import sys
|
|
237
|
+
p=sys.argv[1]; t=open(p).read()
|
|
238
|
+
open(p,"w").write(t.replace("visual-engagement: engaged","visual-engagement: flat"))
|
|
239
|
+
PY
|
|
240
|
+
assert_exit "X: taste judge flat verdict is a blocking fail" 1 "$D"
|
|
241
|
+
assert_grep "X: names the blocking axis" has "blocking Gate 5 failure" "$D"
|
|
242
|
+
# X2: same flat verdict, operator accepted in chat -> waived, passes.
|
|
243
|
+
cp -r "$TMP/flatjudge" "$TMP/flataccepted"
|
|
244
|
+
D="$TMP/flataccepted/.agents/foundry/demo"
|
|
245
|
+
printf '\naccepted: operator shipping the flat draft knowingly for a first cut\n' >> "$D/taste-judge.md"
|
|
246
|
+
assert_exit "X2: accepted flat verdict passes" 0 "$D"
|
|
247
|
+
# CC: copy-craft.md missing -> FAIL; present but weak (only 1 dimension) -> FAIL (#51).
|
|
248
|
+
cp -r "$TMP/clean" "$TMP/nocc"
|
|
249
|
+
rm "$TMP/nocc/.agents/foundry/demo/copy-craft.md"
|
|
250
|
+
assert_exit "CC: missing copy-craft.md fails" 1 "$TMP/nocc/.agents/foundry/demo"
|
|
251
|
+
cp -r "$TMP/clean" "$TMP/weakcc"
|
|
252
|
+
D="$TMP/weakcc/.agents/foundry/demo"
|
|
253
|
+
printf '# Copy craft\nThe copy is clean and reads fine and is fairly good overall on the page.\nNothing much else to note here about it.\nIt seems acceptable to ship.\n' > "$D/copy-craft.md"
|
|
254
|
+
assert_exit "CC: copy-craft with <3 dimensions fails" 1 "$D"
|
|
255
|
+
assert_grep "CC: names voice-compelling gap" has "voice-compelling" "$D"
|
|
256
|
+
|
|
257
|
+
# Z: conversion audit with no reading-experience assessment -> FAIL (#50).
|
|
258
|
+
cp -r "$TMP/clean" "$TMP/noread"
|
|
259
|
+
D="$TMP/noread/.agents/foundry/demo"
|
|
260
|
+
python3 - "$D/conversion-audit.md" <<'PY'
|
|
261
|
+
import sys
|
|
262
|
+
p=sys.argv[1]; t=open(p).read()
|
|
263
|
+
# strip the reading-experience paragraph
|
|
264
|
+
t="\n".join(l for l in t.splitlines() if "Reading experience" not in l and "Scannability" not in l and "scans the rendered" not in l and "reading friction" not in l)
|
|
265
|
+
open(p,"w").write(t)
|
|
266
|
+
PY
|
|
267
|
+
assert_exit "Z: conversion audit without reading-experience fails" 1 "$D"
|
|
268
|
+
assert_grep "Z: names the reading-experience gap" has "reading-experience assessment" "$D"
|
|
269
|
+
|
|
270
|
+
# Y: imagery-plan declares image-light with no engagement mechanism -> FAIL (#49 loophole).
|
|
271
|
+
cp -r "$TMP/clean" "$TMP/flatimg"
|
|
272
|
+
D="$TMP/flatimg/.agents/foundry/demo"
|
|
273
|
+
printf '# Imagery plan\nConsistency: cool grade, 16:9.\n- hero: real product screenshot.\n- everything else: no image by choice, text-only throughout the page.\nNo product screenshot is a mockup.\n' > "$D/imagery-plan.md"
|
|
274
|
+
assert_exit "Y: image-light with no engagement mechanism fails" 1 "$D"
|
|
275
|
+
assert_grep "Y: names the loophole" has "not a free pass" "$D"
|
|
276
|
+
|
|
277
|
+
# X3: missing an axis verdict line -> FAIL (not judged on that axis).
|
|
278
|
+
cp -r "$TMP/clean" "$TMP/noaxis"
|
|
279
|
+
D="$TMP/noaxis/.agents/foundry/demo"
|
|
280
|
+
python3 - "$D/taste-judge.md" <<'PY'
|
|
281
|
+
import sys
|
|
282
|
+
p=sys.argv[1]; t=open(p).read()
|
|
283
|
+
open(p,"w").write(t.replace("property-specificity: specific","(property axis omitted)"))
|
|
284
|
+
PY
|
|
285
|
+
assert_exit "X3: missing property-specificity axis fails" 1 "$D"
|
|
286
|
+
|
|
90
287
|
echo ""
|
|
91
288
|
if [ "$fails" -eq 0 ]; then echo "run_audit calibration: all fixtures pass"; else echo "run_audit calibration: $fails FAILED"; fi
|
|
92
289
|
exit $fails
|