omniconductor 0.5.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +68 -0
- package/README.md +59 -19
- package/VISION.md +2 -2
- package/adapters/README.md +8 -8
- package/adapters/claude/README.md +3 -6
- package/adapters/claude/SUPPORTED-FEATURES.md +4 -4
- package/adapters/claude/metadata.json +39 -0
- package/adapters/claude/transform-spec.md +1 -1
- package/adapters/claude/transform.sh +208 -38
- package/adapters/codex/README.md +23 -27
- package/adapters/codex/metadata.json +35 -0
- package/adapters/codex/transform-spec.md +5 -6
- package/adapters/codex/transform.sh +284 -37
- package/adapters/copilot/README.md +26 -29
- package/adapters/copilot/metadata.json +36 -0
- package/adapters/copilot/transform.sh +126 -35
- package/adapters/cursor/README.md +31 -30
- package/adapters/cursor/metadata.json +35 -0
- package/adapters/cursor/transform.sh +117 -28
- package/adapters/gemini/README.md +14 -15
- package/adapters/gemini/metadata.json +36 -0
- package/adapters/gemini/transform.sh +312 -36
- package/adapters/windsurf/README.md +20 -19
- package/adapters/windsurf/metadata.json +36 -0
- package/adapters/windsurf/transform.sh +139 -55
- package/bin/doctor.js +257 -0
- package/bin/omniconductor.js +15 -2
- package/core/anti-patterns/frequent-rule-file-edit.md +1 -1
- package/core/anti-patterns/single-monolithic-rule-file.md +1 -1
- package/core/hooks/README.md +1 -0
- package/core/hooks/pretool-loop-guard.sh.template +177 -0
- package/core/recipes/README.md +6 -4
- package/core/recipes/loop-engineering.md +73 -0
- package/core/universal-rules/README.md +4 -4
- package/core/universal-rules/meta-discipline.md +4 -4
- package/core/universal-rules/spec-as-you-go.md +1 -1
- package/docs/ADAPTER-LIVE-VERIFICATION.md +73 -0
- package/docs/COMPARISON.md +133 -0
- package/docs/COMPATIBILITY-MATRIX.md +126 -0
- package/docs/DESIGN-DECISIONS.md +1268 -0
- package/docs/MANUAL-INSTALL.md +15 -15
- package/docs/PUBLICATION-POLICY.md +46 -0
- package/docs/PUBLISH-GUIDE.md +141 -0
- package/package.json +7 -1
- package/tools/check-adapter-metadata.sh +211 -0
- package/tools/check-stale-tokens.sh +130 -0
- package/tools/generate-adapter-docs.js +123 -0
- package/tools/live-verify.sh +195 -0
- package/tools/manifest-safety.sh +118 -0
- package/tools/stale-tokens.txt +32 -0
- package/tools/test-install-modes.sh +277 -0
- package/tools/validate-adapter-output.sh +1 -1
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# CONDUCTOR --mode preset test harness (ADR-044).
|
|
3
|
+
# Usage: bash tools/test-install-modes.sh <tool> [base-tmp-dir]
|
|
4
|
+
# Exit 0 = all mode behaviors verified for the tool, 1 = a check failed, 2 = usage.
|
|
5
|
+
#
|
|
6
|
+
# Verifies, per tool:
|
|
7
|
+
# full — installs + validator passes + manifest stamps "mode": "full"
|
|
8
|
+
# minimal — rule text + docs present; NO reflector runtime (.conductor/) even
|
|
9
|
+
# with --recipes=self-improvement; tool-specific extras absent
|
|
10
|
+
# strict — fresh target installs; pre-seeded baseline ABORTS (exit 3, no writes)
|
|
11
|
+
# recipes-only — ONLY recipe content lands (file or marked block); --recipes required
|
|
12
|
+
# (error without); uninstall removes it losslessly
|
|
13
|
+
# reflector-only — self-improvement text + Reflector runtime, nothing else; uninstall clean
|
|
14
|
+
# block modes — (gemini/codex) pre-existing baseline content survives append + strip;
|
|
15
|
+
# customized block is left in place on uninstall
|
|
16
|
+
|
|
17
|
+
set -u
|
|
18
|
+
|
|
19
|
+
TOOL="${1:-}"
|
|
20
|
+
case "$TOOL" in
|
|
21
|
+
claude|cursor|copilot|gemini|codex|windsurf) : ;;
|
|
22
|
+
*) echo "Usage: $0 <claude|cursor|copilot|gemini|codex|windsurf> [base-tmp-dir]" >&2; exit 2 ;;
|
|
23
|
+
esac
|
|
24
|
+
|
|
25
|
+
cd "$(dirname "$0")/.." || exit 2
|
|
26
|
+
BASE="${2:-$(mktemp -d "${TMPDIR:-/tmp}/conductor-modes-$TOOL.XXXXXX")}"
|
|
27
|
+
T="adapters/$TOOL/transform.sh"
|
|
28
|
+
|
|
29
|
+
FAIL=0
|
|
30
|
+
ok() { echo "OK [$TOOL] $1"; }
|
|
31
|
+
bad() { echo "FAIL [$TOOL] $1"; FAIL=1; }
|
|
32
|
+
have() { [ -e "$1" ]; }
|
|
33
|
+
|
|
34
|
+
# Per-tool file map
|
|
35
|
+
case "$TOOL" in
|
|
36
|
+
claude) BASELINE="CLAUDE.md"; RULE="" ;;
|
|
37
|
+
cursor) BASELINE=".cursor/rules/workflow.mdc"; RULE=".cursor/rules/workflow.mdc" ;;
|
|
38
|
+
copilot) BASELINE=".github/copilot-instructions.md"; RULE=".github/copilot-instructions.md" ;;
|
|
39
|
+
gemini) BASELINE="GEMINI.md"; RULE="GEMINI.md" ;;
|
|
40
|
+
codex) BASELINE="AGENTS.md"; RULE="AGENTS.md" ;;
|
|
41
|
+
windsurf) BASELINE=".windsurfrules"; RULE=".devin/rules/workflow.md" ;;
|
|
42
|
+
esac
|
|
43
|
+
# claude special-case: universal rule location
|
|
44
|
+
[ "$TOOL" = "claude" ] && RULE=".claude/rules/workflow.md"
|
|
45
|
+
|
|
46
|
+
recipe_artifact() { # path proving the tdd recipe landed, per tool + mode-kind (file|block)
|
|
47
|
+
local d="$1"
|
|
48
|
+
case "$TOOL" in
|
|
49
|
+
claude) echo "$d/.claude/rules/tdd.md" ;;
|
|
50
|
+
cursor) echo "$d/.cursor/rules/tdd.mdc" ;;
|
|
51
|
+
copilot) echo "$d/.github/instructions/tdd.instructions.md" ;;
|
|
52
|
+
windsurf) echo "$d/.devin/rules/tdd.md" ;;
|
|
53
|
+
gemini) echo "$d/GEMINI.md" ;; # block host
|
|
54
|
+
codex) echo "$d/AGENTS.md" ;; # block host
|
|
55
|
+
esac
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# ---- full ------------------------------------------------------------------
|
|
59
|
+
d="$BASE/full"; mkdir -p "$d"
|
|
60
|
+
if bash "$T" "$d" --no-prompt --recipes=tdd,self-improvement >/dev/null 2>&1 \
|
|
61
|
+
&& bash tools/validate-adapter-output.sh "$d" "$TOOL" >/dev/null 2>&1 \
|
|
62
|
+
&& grep -q '"mode": "full"' "$d/.conductor-manifest.json"; then
|
|
63
|
+
ok "full: install + validator + manifest mode stamp"
|
|
64
|
+
else bad "full mode"; fi
|
|
65
|
+
|
|
66
|
+
# ---- manifest safety: repeat install + user edits -------------------------
|
|
67
|
+
# Re-running a full install must retain the original pre-CONDUCTOR baseline
|
|
68
|
+
# for uninstall, and uninstall must never delete a post-install user edit.
|
|
69
|
+
d="$BASE/full-reinstall"; mkdir -p "$d/$(dirname "$BASELINE")"
|
|
70
|
+
printf 'ORIGINAL-USER-BASELINE-%s\n' "$TOOL" > "$d/$BASELINE"
|
|
71
|
+
before="$(/usr/bin/cksum < "$d/$BASELINE")"
|
|
72
|
+
bash "$T" "$d" --no-prompt >/dev/null 2>&1 \
|
|
73
|
+
&& bash "$T" "$d" --no-prompt >/dev/null 2>&1 \
|
|
74
|
+
&& bash "$T" "$d" --uninstall >/dev/null 2>&1
|
|
75
|
+
after="MISSING"; [ -f "$d/$BASELINE" ] && after="$(/usr/bin/cksum < "$d/$BASELINE")"
|
|
76
|
+
[ "$before" = "$after" ] && ok "full: re-install + uninstall restores original baseline" || bad "full re-install lost original baseline"
|
|
77
|
+
|
|
78
|
+
d="$BASE/full-customized"; mkdir -p "$d"
|
|
79
|
+
bash "$T" "$d" --no-prompt >/dev/null 2>&1
|
|
80
|
+
printf '\nUSER-CUSTOMIZATION-MUST-SURVIVE\n' >> "$d/$BASELINE"
|
|
81
|
+
bash "$T" "$d" --uninstall >/dev/null 2>&1
|
|
82
|
+
grep -q 'USER-CUSTOMIZATION-MUST-SURVIVE' "$d/$BASELINE" 2>/dev/null \
|
|
83
|
+
&& ok "full: uninstall preserves user-modified emitted file" \
|
|
84
|
+
|| bad "full uninstall deleted user customization"
|
|
85
|
+
|
|
86
|
+
d="$BASE/full-edit-then-update"; mkdir -p "$d"
|
|
87
|
+
bash "$T" "$d" --no-prompt >/dev/null 2>&1
|
|
88
|
+
printf '\nUSER-EDIT-BEFORE-UPDATE\n' >> "$d/$BASELINE"
|
|
89
|
+
bash "$T" "$d" --no-prompt >/dev/null 2>&1
|
|
90
|
+
bash "$T" "$d" --uninstall >/dev/null 2>&1
|
|
91
|
+
grep -q 'USER-EDIT-BEFORE-UPDATE' "$d/$BASELINE" 2>/dev/null \
|
|
92
|
+
&& ok "full: update snapshots a user edit before replacement" \
|
|
93
|
+
|| bad "full update lost user edit"
|
|
94
|
+
|
|
95
|
+
# ---- minimal ---------------------------------------------------------------
|
|
96
|
+
d="$BASE/minimal"; mkdir -p "$d"
|
|
97
|
+
bash "$T" "$d" --mode=minimal --recipes=tdd,self-improvement >/dev/null 2>&1
|
|
98
|
+
if have "$d/$RULE" && have "$d/docs/CURRENT_WORK.md" && [ ! -d "$d/.conductor" ]; then
|
|
99
|
+
ok "minimal: rule text + docs, no Reflector runtime"
|
|
100
|
+
else bad "minimal mode"; fi
|
|
101
|
+
if [ "$TOOL" = "claude" ]; then
|
|
102
|
+
{ [ ! -d "$d/.claude/agents" ] && [ ! -d "$d/.claude/hooks" ]; } && ok "minimal: no agents/hooks (claude)" || bad "minimal leaked agents/hooks (claude)"
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
# ---- strict ----------------------------------------------------------------
|
|
106
|
+
d="$BASE/strict-fresh"; mkdir -p "$d"
|
|
107
|
+
bash "$T" "$d" --mode=strict --no-prompt >/dev/null 2>&1 && have "$d/$RULE" \
|
|
108
|
+
&& ok "strict: fresh target installs" || bad "strict fresh install"
|
|
109
|
+
d="$BASE/strict-seeded"; mkdir -p "$d"
|
|
110
|
+
case "$TOOL" in
|
|
111
|
+
cursor) mkdir -p "$d/.cursor/rules"; echo x > "$d/.cursor/rules/mine.mdc" ;;
|
|
112
|
+
copilot) mkdir -p "$d/.github"; echo MINE > "$d/.github/copilot-instructions.md" ;;
|
|
113
|
+
*) mkdir -p "$d/$(dirname "$BASELINE")" 2>/dev/null; echo MINE > "$d/$BASELINE" ;;
|
|
114
|
+
esac
|
|
115
|
+
bash "$T" "$d" --mode=strict --no-prompt >/dev/null 2>&1
|
|
116
|
+
rc=$?
|
|
117
|
+
if [ "$rc" -eq 3 ] && [ ! -f "$d/.conductor-manifest.json" ]; then
|
|
118
|
+
ok "strict: seeded baseline aborts (exit 3, no manifest)"
|
|
119
|
+
else bad "strict seeded abort (rc=$rc)"; fi
|
|
120
|
+
|
|
121
|
+
# Multi-surface tools: seeding ONLY the secondary rules surface must also abort.
|
|
122
|
+
case "$TOOL" in
|
|
123
|
+
claude|copilot|gemini|windsurf)
|
|
124
|
+
d="$BASE/strict-seeded2"; mkdir -p "$d"
|
|
125
|
+
case "$TOOL" in
|
|
126
|
+
claude) mkdir -p "$d/.claude/rules"; echo x > "$d/.claude/rules/mine.md" ;;
|
|
127
|
+
copilot) mkdir -p "$d/.github/instructions"; echo x > "$d/.github/instructions/mine.instructions.md" ;;
|
|
128
|
+
gemini) mkdir -p "$d/.gemini"; echo x > "$d/.gemini/styleguide.md" ;;
|
|
129
|
+
windsurf) mkdir -p "$d/.devin/rules"; echo x > "$d/.devin/rules/mine.md" ;;
|
|
130
|
+
esac
|
|
131
|
+
bash "$T" "$d" --mode=strict --no-prompt >/dev/null 2>&1
|
|
132
|
+
rc=$?
|
|
133
|
+
if [ "$rc" -eq 3 ] && [ ! -f "$d/.conductor-manifest.json" ]; then
|
|
134
|
+
ok "strict: seeded secondary rules surface also aborts"
|
|
135
|
+
else bad "strict secondary-surface abort (rc=$rc)"; fi
|
|
136
|
+
;;
|
|
137
|
+
esac
|
|
138
|
+
|
|
139
|
+
# ---- recipes-only ----------------------------------------------------------
|
|
140
|
+
d="$BASE/ro-noargs"; mkdir -p "$d"
|
|
141
|
+
bash "$T" "$d" --mode=recipes-only >/dev/null 2>&1
|
|
142
|
+
[ $? -eq 1 ] && ok "recipes-only: requires --recipes (errors without)" || bad "recipes-only missing-recipes guard"
|
|
143
|
+
|
|
144
|
+
d="$BASE/ro"; mkdir -p "$d"
|
|
145
|
+
PRE=""
|
|
146
|
+
if [ "$TOOL" = "gemini" ] || [ "$TOOL" = "codex" ]; then
|
|
147
|
+
PRE="MY EXISTING RULES"
|
|
148
|
+
echo "$PRE" > "$d/$BASELINE"
|
|
149
|
+
fi
|
|
150
|
+
bash "$T" "$d" --mode=recipes-only --recipes=tdd >/dev/null 2>&1
|
|
151
|
+
art="$(recipe_artifact "$d")"
|
|
152
|
+
okay=true
|
|
153
|
+
have "$art" || okay=false
|
|
154
|
+
[ -d "$d/docs" ] && okay=false
|
|
155
|
+
if [ "$TOOL" = "gemini" ] || [ "$TOOL" = "codex" ]; then
|
|
156
|
+
grep -q 'conductor:block recipes' "$art" || okay=false
|
|
157
|
+
grep -q "$PRE" "$art" || okay=false
|
|
158
|
+
else
|
|
159
|
+
# per-file tools must NOT have emitted universal rules
|
|
160
|
+
have "$d/$RULE" && okay=false
|
|
161
|
+
fi
|
|
162
|
+
$okay && ok "recipes-only: only recipe content landed" || bad "recipes-only emission"
|
|
163
|
+
|
|
164
|
+
bash "$T" "$d" --uninstall >/dev/null 2>&1
|
|
165
|
+
okay=true
|
|
166
|
+
if [ "$TOOL" = "gemini" ] || [ "$TOOL" = "codex" ]; then
|
|
167
|
+
grep -q 'conductor:block' "$art" 2>/dev/null && okay=false
|
|
168
|
+
grep -q "$PRE" "$art" 2>/dev/null || okay=false
|
|
169
|
+
else
|
|
170
|
+
have "$art" && okay=false
|
|
171
|
+
fi
|
|
172
|
+
$okay && ok "recipes-only: uninstall lossless" || bad "recipes-only uninstall"
|
|
173
|
+
|
|
174
|
+
# ---- reflector-only --------------------------------------------------------
|
|
175
|
+
d="$BASE/reflonly"; mkdir -p "$d"
|
|
176
|
+
bash "$T" "$d" --mode=reflector-only >/dev/null 2>&1
|
|
177
|
+
okay=true
|
|
178
|
+
if [ "$TOOL" = "claude" ]; then
|
|
179
|
+
# Claude's trajectory logger is the Stop hook, not the portable stdin logger.
|
|
180
|
+
[ -x "$d/.claude/hooks/stop-trajectory-log.sh" ] || okay=false
|
|
181
|
+
[ -d "$d/.conductor/reflect" ] || okay=false
|
|
182
|
+
else
|
|
183
|
+
[ -x "$d/.conductor/reflect/trajectory-log.sh" ] || okay=false
|
|
184
|
+
fi
|
|
185
|
+
[ -d "$d/docs" ] && okay=false
|
|
186
|
+
case "$TOOL" in
|
|
187
|
+
claude) { have "$d/.claude/rules/self-improvement.md" && have "$d/.claude/agents/reflector.md" && have "$d/.claude/hooks/stop-trajectory-log.sh" && have "$d/.claude/settings.json" && [ ! -f "$d/CLAUDE.md" ]; } || okay=false ;;
|
|
188
|
+
cursor) { have "$d/.cursor/rules/self-improvement.mdc" && have "$d/.cursor/hooks.json" && [ ! -f "$d/.cursor/rules/workflow.mdc" ]; } || okay=false ;;
|
|
189
|
+
copilot) { have "$d/.github/instructions/self-improvement.instructions.md" && have "$d/.github/hooks/conductor-reflect.json" && [ ! -f "$d/.github/copilot-instructions.md" ]; } || okay=false ;;
|
|
190
|
+
gemini) { grep -q 'conductor:block reflector' "$d/GEMINI.md" && have "$d/.gemini/settings.json"; } || okay=false ;;
|
|
191
|
+
codex) { grep -q 'conductor:block reflector' "$d/AGENTS.md" && have "$d/.codex/hooks.json"; } || okay=false ;;
|
|
192
|
+
windsurf) { have "$d/.devin/rules/self-improvement.md" && have "$d/.windsurf/hooks.json" && [ ! -f "$d/.windsurfrules" ]; } || okay=false ;;
|
|
193
|
+
esac
|
|
194
|
+
$okay && ok "reflector-only: loop artifacts only" || bad "reflector-only emission"
|
|
195
|
+
bash "$T" "$d" --uninstall >/dev/null 2>&1
|
|
196
|
+
[ ! -d "$d/.conductor" ] && ok "reflector-only: uninstall clean" || bad "reflector-only uninstall"
|
|
197
|
+
|
|
198
|
+
# ---- zero valid recipes must fail (all tools) -------------------------------
|
|
199
|
+
d="$BASE/ro-badname"; mkdir -p "$d"
|
|
200
|
+
bash "$T" "$d" --mode=recipes-only --recipes=notarealrecipe >/dev/null 2>&1
|
|
201
|
+
if [ $? -eq 1 ] && [ ! -f "$d/.conductor-manifest.json" ] && [ ! -f "$d/.conductor-manifest.json.staging" ]; then
|
|
202
|
+
ok "recipes-only: zero valid recipes → exit 1, no manifest/staging litter"
|
|
203
|
+
else bad "recipes-only zero-valid-recipes guard"; fi
|
|
204
|
+
|
|
205
|
+
# ---- byte-lossless block round-trip + cross-mode (single-file tools) ---------
|
|
206
|
+
if [ "$TOOL" = "gemini" ] || [ "$TOOL" = "codex" ]; then
|
|
207
|
+
# A user-owned marker (including a malformed one) must never be treated as
|
|
208
|
+
# an existing CONDUCTOR block. Refuse without touching the host file.
|
|
209
|
+
d="$BASE/marker-collision"; mkdir -p "$d"
|
|
210
|
+
printf 'USER PREFIX\n<!-- conductor:block recipes -->\nUSER CONTENT AFTER MARKER\n' > "$d/$BASELINE"
|
|
211
|
+
before="$(/usr/bin/cksum < "$d/$BASELINE")"
|
|
212
|
+
bash "$T" "$d" --mode=recipes-only --recipes=tdd >/dev/null 2>&1
|
|
213
|
+
rc=$?
|
|
214
|
+
after="$(/usr/bin/cksum < "$d/$BASELINE")"
|
|
215
|
+
if [ "$rc" -eq 1 ] && [ "$before" = "$after" ] && [ ! -f "$d/.conductor-manifest.json" ] && [ ! -f "$d/.conductor-manifest.json.staging" ]; then
|
|
216
|
+
ok "block: foreign/unpaired marker aborts without data loss"
|
|
217
|
+
else bad "block: foreign marker collision (rc=$rc)"; fi
|
|
218
|
+
|
|
219
|
+
d="$BASE/marker-foreign-paired"; mkdir -p "$d"
|
|
220
|
+
printf 'USER PREFIX\n<!-- conductor:block recipes -->\nUSER BLOCK\n<!-- /conductor:block recipes -->\nUSER SUFFIX\n' > "$d/$BASELINE"
|
|
221
|
+
before="$(/usr/bin/cksum < "$d/$BASELINE")"
|
|
222
|
+
bash "$T" "$d" --mode=recipes-only --recipes=tdd >/dev/null 2>&1
|
|
223
|
+
rc=$?
|
|
224
|
+
after="$(/usr/bin/cksum < "$d/$BASELINE")"
|
|
225
|
+
if [ "$rc" -eq 1 ] && [ "$before" = "$after" ] && [ ! -f "$d/.conductor-manifest.json" ] && [ ! -f "$d/.conductor-manifest.json.staging" ]; then
|
|
226
|
+
ok "block: foreign paired marker aborts without data loss"
|
|
227
|
+
else bad "block: foreign paired-marker collision (rc=$rc)"; fi
|
|
228
|
+
|
|
229
|
+
d="$BASE/marker-custom-reinstall"; mkdir -p "$d"
|
|
230
|
+
bash "$T" "$d" --mode=recipes-only --recipes=tdd >/dev/null 2>&1
|
|
231
|
+
/usr/bin/sed -i.bak 's/## Recipe — tdd/## Recipe — tdd (CUSTOMIZED)/' "$d/$BASELINE" && rm -f "$d/$BASELINE.bak"
|
|
232
|
+
before="$(/usr/bin/cksum < "$d/$BASELINE")"
|
|
233
|
+
bash "$T" "$d" --mode=recipes-only --recipes=tdd >/dev/null 2>&1
|
|
234
|
+
rc=$?
|
|
235
|
+
after="$(/usr/bin/cksum < "$d/$BASELINE")"
|
|
236
|
+
if [ "$rc" -eq 1 ] && [ "$before" = "$after" ] && [ ! -f "$d/.conductor-manifest.json.staging" ]; then
|
|
237
|
+
ok "block: customized managed block is not overwritten on re-install"
|
|
238
|
+
else bad "block: customized managed block overwritten (rc=$rc)"; fi
|
|
239
|
+
|
|
240
|
+
d="$BASE/lossless"; mkdir -p "$d"
|
|
241
|
+
printf 'MY EXISTING RULES\n' > "$d/$BASELINE"
|
|
242
|
+
before="$(/usr/bin/cksum < "$d/$BASELINE")"
|
|
243
|
+
for _i in 1 2 3; do bash "$T" "$d" --mode=recipes-only --recipes=tdd >/dev/null 2>&1; done
|
|
244
|
+
bash "$T" "$d" --uninstall >/dev/null 2>&1
|
|
245
|
+
after="$(/usr/bin/cksum < "$d/$BASELINE")"
|
|
246
|
+
[ "$before" = "$after" ] && ok "block: 3x reinstall + uninstall is byte-lossless" || bad "block: reinstall/uninstall not byte-lossless"
|
|
247
|
+
|
|
248
|
+
d="$BASE/crossmode"; mkdir -p "$d"
|
|
249
|
+
printf 'BASE\n' > "$d/$BASELINE"
|
|
250
|
+
bash "$T" "$d" --mode=recipes-only --recipes=tdd >/dev/null 2>&1
|
|
251
|
+
bash "$T" "$d" --mode=reflector-only >/dev/null 2>&1
|
|
252
|
+
bash "$T" "$d" --uninstall >/dev/null 2>&1
|
|
253
|
+
if [ "$(grep -c 'conductor:block' "$d/$BASELINE" 2>/dev/null)" -eq 0 ] && grep -q 'BASE' "$d/$BASELINE"; then
|
|
254
|
+
ok "block: cross-mode (recipes-only → reflector-only) uninstall strips BOTH blocks"
|
|
255
|
+
else bad "block: cross-mode uninstall left an orphaned block"; fi
|
|
256
|
+
fi
|
|
257
|
+
|
|
258
|
+
# ---- customized block survives uninstall (single-file tools) ----------------
|
|
259
|
+
if [ "$TOOL" = "gemini" ] || [ "$TOOL" = "codex" ]; then
|
|
260
|
+
d="$BASE/custom"; mkdir -p "$d"
|
|
261
|
+
echo "BASE" > "$d/$BASELINE"
|
|
262
|
+
bash "$T" "$d" --mode=recipes-only --recipes=tdd >/dev/null 2>&1
|
|
263
|
+
# mutate INSIDE the block
|
|
264
|
+
/usr/bin/sed -i.bak 's/## Recipe — tdd/## Recipe — tdd (CUSTOMIZED)/' "$d/$BASELINE" && rm -f "$d/$BASELINE.bak"
|
|
265
|
+
bash "$T" "$d" --uninstall >/dev/null 2>&1
|
|
266
|
+
if grep -q 'CUSTOMIZED' "$d/$BASELINE"; then
|
|
267
|
+
ok "block: customized block left in place on uninstall"
|
|
268
|
+
else bad "block: customized block was destroyed"; fi
|
|
269
|
+
fi
|
|
270
|
+
|
|
271
|
+
echo
|
|
272
|
+
if [ "$FAIL" -eq 0 ]; then
|
|
273
|
+
echo "OK — all --mode behaviors verified for $TOOL ($BASE)"
|
|
274
|
+
exit 0
|
|
275
|
+
fi
|
|
276
|
+
echo "FAIL — mode behavior broken for $TOOL (workdirs kept at $BASE)"
|
|
277
|
+
exit 1
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
#
|
|
55
55
|
# windsurf:
|
|
56
56
|
# - .windsurfrules exists, non-empty
|
|
57
|
-
# - .windsurf/rules/ contains the 5 universal rule files
|
|
57
|
+
# - .devin/rules/ (or legacy .windsurf/rules/) contains the 5 universal rule files
|
|
58
58
|
# (meta-discipline/operations/quality-gates/spec-as-you-go/workflow)
|
|
59
59
|
# - no unsubstituted ${...} placeholders (outside code fences)
|
|
60
60
|
# - no reference-product leakage
|