omniconductor 0.6.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +16 -12
  3. package/VISION.md +2 -2
  4. package/adapters/README.md +8 -8
  5. package/adapters/claude/README.md +2 -5
  6. package/adapters/claude/SUPPORTED-FEATURES.md +1 -1
  7. package/adapters/claude/metadata.json +39 -0
  8. package/adapters/claude/transform.sh +198 -34
  9. package/adapters/codex/README.md +23 -27
  10. package/adapters/codex/metadata.json +35 -0
  11. package/adapters/codex/transform-spec.md +5 -6
  12. package/adapters/codex/transform.sh +282 -35
  13. package/adapters/copilot/README.md +26 -29
  14. package/adapters/copilot/metadata.json +36 -0
  15. package/adapters/copilot/transform.sh +124 -33
  16. package/adapters/cursor/README.md +31 -30
  17. package/adapters/cursor/metadata.json +35 -0
  18. package/adapters/cursor/transform.sh +115 -26
  19. package/adapters/gemini/README.md +14 -15
  20. package/adapters/gemini/metadata.json +36 -0
  21. package/adapters/gemini/transform.sh +310 -34
  22. package/adapters/windsurf/README.md +20 -19
  23. package/adapters/windsurf/metadata.json +36 -0
  24. package/adapters/windsurf/transform.sh +137 -53
  25. package/bin/doctor.js +257 -0
  26. package/bin/omniconductor.js +15 -2
  27. package/core/anti-patterns/frequent-rule-file-edit.md +1 -1
  28. package/core/anti-patterns/single-monolithic-rule-file.md +1 -1
  29. package/core/recipes/README.md +2 -2
  30. package/core/universal-rules/README.md +4 -4
  31. package/core/universal-rules/meta-discipline.md +4 -4
  32. package/core/universal-rules/spec-as-you-go.md +1 -1
  33. package/docs/ADAPTER-LIVE-VERIFICATION.md +73 -0
  34. package/docs/COMPARISON.md +133 -0
  35. package/docs/COMPATIBILITY-MATRIX.md +126 -0
  36. package/docs/DESIGN-DECISIONS.md +1268 -0
  37. package/docs/MANUAL-INSTALL.md +15 -15
  38. package/docs/PUBLICATION-POLICY.md +46 -0
  39. package/docs/PUBLISH-GUIDE.md +141 -0
  40. package/package.json +7 -1
  41. package/tools/check-adapter-metadata.sh +211 -0
  42. package/tools/check-stale-tokens.sh +130 -0
  43. package/tools/generate-adapter-docs.js +123 -0
  44. package/tools/live-verify.sh +195 -0
  45. package/tools/manifest-safety.sh +118 -0
  46. package/tools/stale-tokens.txt +32 -0
  47. package/tools/test-install-modes.sh +277 -0
  48. package/tools/validate-adapter-output.sh +1 -1
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /*
5
+ * CONDUCTOR — doc generator from adapter metadata (ADR-042; ADR-040 slice 2).
6
+ *
7
+ * Reads adapters/<tool>/metadata.json (the single source for enumerable adapter
8
+ * facts) and rewrites MARKED REGIONS in the docs below. Hand-editing inside a
9
+ * marked region is futile — edit metadata.json and re-run this script.
10
+ *
11
+ * Regions:
12
+ * docs/ADAPTER-LIVE-VERIFICATION.md <!-- generated:live-verification-table -->
13
+ * docs/COMPATIBILITY-MATRIX.md <!-- generated:adapter-outputs-table -->
14
+ *
15
+ * Usage:
16
+ * node tools/generate-adapter-docs.js # rewrite regions in place
17
+ * node tools/generate-adapter-docs.js --check # exit 1 if any region is out of date (CI)
18
+ *
19
+ * Exit codes: 0 = up to date / written, 1 = --check found drift, 2 = error.
20
+ */
21
+
22
+ const fs = require('fs');
23
+ const path = require('path');
24
+
25
+ const ROOT = path.resolve(__dirname, '..');
26
+ const TOOLS = ['claude', 'cursor', 'copilot', 'gemini', 'codex', 'windsurf'];
27
+ const CHECK = process.argv.includes('--check');
28
+
29
+ function die(msg) { process.stderr.write(`generate-adapter-docs: ${msg}\n`); process.exit(2); }
30
+
31
+ function loadMetadata() {
32
+ return TOOLS.map((tool) => {
33
+ const p = path.join(ROOT, 'adapters', tool, 'metadata.json');
34
+ let m;
35
+ try { m = JSON.parse(fs.readFileSync(p, 'utf8')); }
36
+ catch (e) { die(`${p}: ${e.message}`); }
37
+ if (m.tool !== tool) die(`${p}: tool field '${m.tool}' != directory '${tool}'`);
38
+ return m;
39
+ });
40
+ }
41
+
42
+ // ---- renderers -------------------------------------------------------------
43
+
44
+ function liveCell(m) {
45
+ const lv = m.live_verification;
46
+ if (lv.status === 'verified') {
47
+ return `✅ **live-verified ${lv.date}** — ${lv.cli}${lv.note ? ` ${lv.note}` : ''}`;
48
+ }
49
+ return `🧪 ${lv.note || 'not yet run'}`;
50
+ }
51
+
52
+ function renderLiveVerificationTable(metas) {
53
+ const rows = metas.map((m) => `| ${m.display_name} | ✅ | ${liveCell(m)} |`);
54
+ return [
55
+ '| Adapter | File emission | Live rule-loading |',
56
+ '|---|---|---|',
57
+ ...rows,
58
+ ].join('\n');
59
+ }
60
+
61
+ function renderOutputsTable(metas) {
62
+ const rows = metas.map((m) => {
63
+ const outputs = m.outputs.map((o) => `\`${o.path}\``).join(' + ');
64
+ // "(legacy)" qualifier keeps these rows compliant with the stale-token
65
+ // allow_regex (ADR-039) — a bare legacy path here would read as a current claim.
66
+ const legacy = m.legacy_paths.length ? m.legacy_paths.map((l) => `\`${l}\` (legacy)`).join(', ') : '—';
67
+ const live = m.live_verification.status === 'verified'
68
+ ? `✅ ${m.live_verification.date}`
69
+ : '🧪 pending';
70
+ const headless = `\`${m.headless_cli.invocation}\``;
71
+ const alaCarte = m.install && m.install.ala_carte === 'block' ? 'marked block' : 'per-file';
72
+ return `| ${m.display_name} | ${m.tier} | ${outputs} | ${legacy} | ${live} | ${headless} | ${alaCarte} |`;
73
+ });
74
+ return [
75
+ '| Tool | Tier | Emitted outputs | Legacy paths (still read) | Live-verified | Headless CLI | À la carte (`--mode`) |',
76
+ '|---|---|---|---|---|---|---|',
77
+ ...rows,
78
+ ].join('\n');
79
+ }
80
+
81
+ // ---- region splicing -------------------------------------------------------
82
+
83
+ function spliceRegion(file, name, body) {
84
+ const p = path.join(ROOT, file);
85
+ const src = fs.readFileSync(p, 'utf8');
86
+ const open = `<!-- generated:${name} — edit adapters/*/metadata.json + run tools/generate-adapter-docs.js; do not hand-edit (ADR-042) -->`;
87
+ const close = `<!-- /generated:${name} -->`;
88
+ const start = src.indexOf(open);
89
+ const end = src.indexOf(close);
90
+ if (start === -1 || end === -1 || end < start) {
91
+ die(`${file}: marked region '${name}' not found (need both open + close markers)`);
92
+ }
93
+ const next = src.slice(0, start + open.length) + '\n' + body + '\n' + src.slice(end);
94
+ return { p, src, next, changed: next !== src };
95
+ }
96
+
97
+ function main() {
98
+ const metas = loadMetadata();
99
+ const jobs = [
100
+ spliceRegion('docs/ADAPTER-LIVE-VERIFICATION.md', 'live-verification-table', renderLiveVerificationTable(metas)),
101
+ spliceRegion('docs/COMPATIBILITY-MATRIX.md', 'adapter-outputs-table', renderOutputsTable(metas)),
102
+ ];
103
+
104
+ const drifted = jobs.filter((j) => j.changed);
105
+ if (CHECK) {
106
+ if (drifted.length) {
107
+ for (const j of drifted) {
108
+ process.stderr.write(`DRIFT: ${path.relative(ROOT, j.p)} generated region is out of date with adapters/*/metadata.json\n`);
109
+ }
110
+ process.stderr.write(`Run: node tools/generate-adapter-docs.js\n`);
111
+ process.exit(1);
112
+ }
113
+ process.stdout.write('OK — generated doc regions match adapter metadata.\n');
114
+ return;
115
+ }
116
+
117
+ for (const j of jobs) {
118
+ if (j.changed) { fs.writeFileSync(j.p, j.next); process.stdout.write(`wrote ${path.relative(ROOT, j.p)}\n`); }
119
+ else process.stdout.write(`up-to-date ${path.relative(ROOT, j.p)}\n`);
120
+ }
121
+ }
122
+
123
+ main();
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/env bash
2
+ # CONDUCTOR live-verify — automated live rule-loading verification (ADR-043).
3
+ # Exit 0 = every attempted tool passed (skips don't fail), 1 = a probe FAILED, 2 = error.
4
+ #
5
+ # For each adapter (or --tool=<t>):
6
+ # 1. read adapters/<tool>/metadata.json → headless CLI command/invocation
7
+ # 2. if the CLI is not on PATH → SKIP (honest — never fake a ✅)
8
+ # 3. install the adapter into a throwaway temp dir (base install, no recipes)
9
+ # 4. run the tool headlessly with the probe prompt from docs/ADAPTER-LIVE-VERIFICATION.md
10
+ # 5. grade DETERMINISTICALLY (no LLM judge): the answer must name ≥3 of the 5
11
+ # universal rules AND mention CURRENT_WORK
12
+ # 6. on PASS: write live_verification {status:verified, date, cli} into the tool's
13
+ # metadata.json and regenerate the doc tables (tools/generate-adapter-docs.js)
14
+ #
15
+ # Freshness: any 'verified' date older than 90 days prints a WARN (re-verify).
16
+ #
17
+ # Usage:
18
+ # bash tools/live-verify.sh # all six (installed CLIs only)
19
+ # bash tools/live-verify.sh --tool=codex # one tool
20
+ # bash tools/live-verify.sh --dry-run # show plan, run nothing, write nothing
21
+ #
22
+ # Env: CONDUCTOR_LIVE_TIMEOUT (seconds per probe, default 300)
23
+ #
24
+ # Local-first by design: CI cannot run six authenticated AI CLIs (see
25
+ # docs/ADAPTER-LIVE-VERIFICATION.md "Why this is separate from CI").
26
+
27
+ set -u
28
+
29
+ cd "$(dirname "$0")/.." || exit 2
30
+ ROOT="$(pwd)"
31
+
32
+ command -v node >/dev/null 2>&1 || { echo "ERROR: node is required" >&2; exit 2; }
33
+
34
+ ONLY_TOOL=""
35
+ DRY_RUN="false"
36
+ for a in "$@"; do
37
+ case "$a" in
38
+ --tool=*) ONLY_TOOL="${a#--tool=}" ;;
39
+ --dry-run) DRY_RUN="true" ;;
40
+ *) echo "ERROR: unknown arg '$a' (use --tool=<t> / --dry-run)" >&2; exit 2 ;;
41
+ esac
42
+ done
43
+
44
+ TOOLS="claude cursor copilot gemini codex windsurf"
45
+ TIMEOUT_S="${CONDUCTOR_LIVE_TIMEOUT:-300}"
46
+
47
+ if [ -n "$ONLY_TOOL" ]; then
48
+ case " $TOOLS " in
49
+ *" $ONLY_TOOL "*) : ;;
50
+ *) echo "ERROR: unknown --tool '$ONLY_TOOL' (one of: $TOOLS)" >&2; exit 2 ;;
51
+ esac
52
+ fi
53
+
54
+ PROBE="What workflow and rules are you operating under in this project? List the universal rules you can see, and tell me the first thing you must do before writing code."
55
+
56
+ FAILED=0
57
+ ATTEMPTED=0
58
+ PASSED=0
59
+
60
+ meta_field() { # meta_field <tool> <js-expr on m>
61
+ node -e "const m=require('$ROOT/adapters/$1/metadata.json');console.log($2 ?? '')"
62
+ }
63
+
64
+ grade() { # grade <output-file> → prints "rule_hits current_work(0/1)"
65
+ local f="$1" hits=0 cw=0 r
66
+ for r in workflow spec-as-you-go quality-gates operations meta-discipline; do
67
+ grep -qi -- "$r" "$f" && hits=$((hits + 1))
68
+ done
69
+ grep -qi 'CURRENT_WORK' "$f" && cw=1
70
+ echo "$hits $cw"
71
+ }
72
+
73
+ run_with_timeout() { # run_with_timeout <seconds> <cmd...> (portable: no coreutils timeout)
74
+ local secs="$1"; shift
75
+ "$@" &
76
+ local pid=$!
77
+ local waited=0
78
+ while kill -0 "$pid" 2>/dev/null; do
79
+ if [ "$waited" -ge "$secs" ]; then
80
+ kill "$pid" 2>/dev/null
81
+ wait "$pid" 2>/dev/null
82
+ return 124
83
+ fi
84
+ sleep 2; waited=$((waited + 2))
85
+ done
86
+ wait "$pid"
87
+ }
88
+
89
+ for tool in $TOOLS; do
90
+ [ -n "$ONLY_TOOL" ] && [ "$tool" != "$ONLY_TOOL" ] && continue
91
+
92
+ cmd="$(meta_field "$tool" 'm.headless_cli.command')"
93
+ status="$(meta_field "$tool" 'm.live_verification.status')"
94
+ date_v="$(meta_field "$tool" 'm.live_verification.date')"
95
+
96
+ if ! command -v "$cmd" >/dev/null 2>&1; then
97
+ echo "SKIP $tool — headless CLI '$cmd' not on PATH (honest skip; install it to verify)"
98
+ continue
99
+ fi
100
+
101
+ if [ "$DRY_RUN" = "true" ]; then
102
+ echo "PLAN $tool — would install to a temp dir and probe via '$cmd' (current: $status${date_v:+ $date_v})"
103
+ continue
104
+ fi
105
+
106
+ ATTEMPTED=$((ATTEMPTED + 1))
107
+ tmp="$(mktemp -d "${TMPDIR:-/tmp}/conductor-live-$tool.XXXXXX")" || { echo "ERROR: mktemp failed" >&2; exit 2; }
108
+ out="$tmp/.probe-output.txt"
109
+
110
+ echo "RUN $tool — installing into $tmp"
111
+ if ! bash "adapters/$tool/transform.sh" "$tmp" --no-prompt >/dev/null 2>&1; then
112
+ echo "FAIL $tool — adapter install failed (pre-probe)"; FAILED=1; rm -rf "$tmp"; continue
113
+ fi
114
+ ( cd "$tmp" && git init -q ) 2>/dev/null || true
115
+
116
+ echo "RUN $tool — probing '$cmd' (timeout ${TIMEOUT_S}s)"
117
+ (
118
+ cd "$tmp" || exit 2
119
+ # Read-only probe; flags mirror core/reflector/run-weekly.sh (verified 2026-07-05),
120
+ # minus write permissions — the probe only asks a question.
121
+ case "$cmd" in
122
+ claude) run_with_timeout "$TIMEOUT_S" claude -p "$PROBE" ;;
123
+ codex) run_with_timeout "$TIMEOUT_S" codex exec --sandbox read-only "$PROBE" ;;
124
+ gemini) run_with_timeout "$TIMEOUT_S" gemini -p "$PROBE" ;;
125
+ cursor-agent) run_with_timeout "$TIMEOUT_S" cursor-agent -p "$PROBE" ;;
126
+ copilot) run_with_timeout "$TIMEOUT_S" copilot -p "$PROBE" ;;
127
+ devin) run_with_timeout "$TIMEOUT_S" devin -p "$PROBE" ;;
128
+ *) echo "unknown CLI '$cmd'" >&2; exit 2 ;;
129
+ esac
130
+ ) > "$out" 2>&1
131
+ probe_rc=$?
132
+
133
+ if [ "$probe_rc" -eq 124 ]; then
134
+ echo "FAIL $tool — probe timed out after ${TIMEOUT_S}s"; FAILED=1; rm -rf "$tmp"; continue
135
+ fi
136
+ if [ "$probe_rc" -ne 0 ]; then
137
+ # A non-zero CLI exit (auth failure, transport error, crash) must never be
138
+ # recorded as verified — even if partial output happens to contain keywords.
139
+ echo "FAIL $tool — probe CLI exited $probe_rc (see $out)"; FAILED=1; continue
140
+ fi
141
+
142
+ read -r hits cw <<EOF_GRADE
143
+ $(grade "$out")
144
+ EOF_GRADE
145
+ echo " $tool — graded: $hits/5 rule names, CURRENT_WORK=$cw (probe exit $probe_rc)"
146
+
147
+ if [ "$hits" -ge 3 ] && [ "$cw" -eq 1 ]; then
148
+ PASSED=$((PASSED + 1))
149
+ today="$(date +%F)"
150
+ cliver="$("$cmd" --version 2>/dev/null | head -n 1 | tr -d '\n' | cut -c1-60)"
151
+ [ -n "$cliver" ] || cliver="$cmd"
152
+ echo "PASS $tool — live-verified $today ($cliver)"
153
+ node -e '
154
+ const fs = require("fs");
155
+ const p = process.argv[1], date = process.argv[2], cli = process.argv[3], hits = process.argv[4];
156
+ const raw = fs.readFileSync(p, "utf8");
157
+ const m = JSON.parse(raw);
158
+ m.live_verification = { status: "verified", date, cli,
159
+ note: `headless probe listed ${hits}/5 rules + read-CURRENT_WORK-first` };
160
+ // Preserve the file style: compact one-line live_verification object.
161
+ const updated = raw.replace(/"live_verification": \{[^}]*\}/,
162
+ `"live_verification": { "status": "verified", "date": ${JSON.stringify(date)}, "cli": ${JSON.stringify(cli)}, "note": ${JSON.stringify(m.live_verification.note)} }`);
163
+ fs.writeFileSync(p, updated);
164
+ ' "adapters/$tool/metadata.json" "$today" "$cliver" "$hits"
165
+ node tools/generate-adapter-docs.js >/dev/null
166
+ echo " $tool — metadata.json updated + doc tables regenerated"
167
+ else
168
+ echo "FAIL $tool — tool did not demonstrate loading the rules (see $out)"
169
+ echo " (an emission-verified adapter that fails live-loading usually means the tool's rules-file convention moved — see docs/ADAPTER-LIVE-VERIFICATION.md)"
170
+ FAILED=1
171
+ continue # keep $tmp for inspection on failure
172
+ fi
173
+ rm -rf "$tmp"
174
+ done
175
+
176
+ # Freshness guard — verified dates older than 90 days deserve a re-run.
177
+ node -e '
178
+ const fs = require("fs");
179
+ const tools = ["claude","cursor","copilot","gemini","codex","windsurf"];
180
+ const now = Date.now();
181
+ for (const t of tools) {
182
+ const m = JSON.parse(fs.readFileSync(`adapters/${t}/metadata.json`, "utf8"));
183
+ const lv = m.live_verification;
184
+ if (lv.status === "verified" && lv.date) {
185
+ const age = Math.floor((now - new Date(lv.date).getTime()) / 86400000);
186
+ if (age > 90) console.log(`WARN ${t} — live verification is ${age} days old (${lv.date}); re-run: bash tools/live-verify.sh --tool=${t}`);
187
+ }
188
+ }
189
+ '
190
+
191
+ echo
192
+ if [ "$DRY_RUN" = "true" ]; then echo "dry-run complete — nothing executed or written."; exit 0; fi
193
+ echo "live-verify: attempted=$ATTEMPTED passed=$PASSED failed=$((FAILED))"
194
+ [ "$FAILED" -eq 0 ] || exit 1
195
+ exit 0
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env bash
2
+ # Shared manifest-safety helpers for the six adapters.
3
+ #
4
+ # This file is sourced after an adapter defines TARGET_ABS, MANIFEST_PATH,
5
+ # DRY_RUN, and log(). It deliberately uses only bash + shasum/sha256sum so
6
+ # installers remain dependency-free.
7
+
8
+ conductor_sha256_file() {
9
+ local file="$1"
10
+ if command -v sha256sum >/dev/null 2>&1; then
11
+ sha256sum "$file" | /usr/bin/awk '{print $1}'
12
+ else
13
+ /usr/bin/shasum -a 256 "$file" | /usr/bin/awk '{print $1}'
14
+ fi
15
+ }
16
+
17
+ # Return the one-line normal-file manifest entry for a relative path.
18
+ conductor_manifest_entry_for_path() {
19
+ local wanted="$1" line found
20
+ [ -f "$MANIFEST_PATH" ] || return 1
21
+ while IFS= read -r line; do
22
+ case "$line" in
23
+ *'"path":'*'"source":'*)
24
+ found="$(printf '%s' "$line" | /usr/bin/sed -E 's/.*"path": *"([^"]*)".*/\1/')"
25
+ [ "$found" = "$wanted" ] && { printf '%s\n' "$line"; return 0; }
26
+ ;;
27
+ esac
28
+ done < "$MANIFEST_PATH"
29
+ return 1
30
+ }
31
+
32
+ # Return a simple string-valued JSON field from a manifest entry.
33
+ conductor_manifest_field() {
34
+ local line="$1" key="$2"
35
+ case "$line" in
36
+ *"\"$key\":"*)
37
+ printf '%s' "$line" | /usr/bin/sed -E "s/.*\"$key\": *\"([^\"]*)\".*/\\1/"
38
+ ;;
39
+ *) return 1 ;;
40
+ esac
41
+ }
42
+
43
+ conductor_unique_backup_path() {
44
+ local dest="$1" ts candidate n=0
45
+ ts="$(/bin/date +%Y%m%d-%H%M%S)"
46
+ candidate="${dest}.conductor-backup-${ts}"
47
+ while [ -e "$candidate" ]; do
48
+ n=$((n + 1))
49
+ candidate="${dest}.conductor-backup-${ts}-${n}"
50
+ done
51
+ printf '%s' "$candidate"
52
+ }
53
+
54
+ # Preserve the initial pre-CONDUCTOR backup across an unmodified re-install.
55
+ # If the prior emitted file was edited, back up that edit before replacing it.
56
+ conductor_manifest_backup_and_remember() {
57
+ local dest="$1" rel entry prior_sha prior_backup current_sha backup
58
+ MANIFEST_LAST_BACKUP=""
59
+ [ -f "$dest" ] || return 0
60
+
61
+ if [ "$DRY_RUN" = "true" ]; then
62
+ log "would safely back up existing $dest"
63
+ return 0
64
+ fi
65
+
66
+ rel="${dest#$TARGET_ABS/}"
67
+ entry="$(conductor_manifest_entry_for_path "$rel" 2>/dev/null || true)"
68
+ if [ -n "$entry" ]; then
69
+ prior_sha="$(conductor_manifest_field "$entry" sha256 2>/dev/null || true)"
70
+ prior_backup="$(conductor_manifest_field "$entry" backup_path 2>/dev/null || true)"
71
+ current_sha="$(conductor_sha256_file "$dest")"
72
+
73
+ if [ -n "$prior_sha" ] && [ "$current_sha" = "$prior_sha" ]; then
74
+ if [ -n "$prior_backup" ] && [ -f "$TARGET_ABS/$prior_backup" ]; then
75
+ MANIFEST_LAST_BACKUP="$prior_backup"
76
+ log " retained original backup for $dest across re-install"
77
+ else
78
+ log " re-installing unchanged CONDUCTOR file $dest"
79
+ fi
80
+ return 0
81
+ fi
82
+
83
+ if [ -n "$prior_sha" ]; then
84
+ log " preserved user-modified file before re-install: $dest"
85
+ else
86
+ log " preserved legacy manifest file before re-install: $dest"
87
+ fi
88
+ fi
89
+
90
+ backup="$(conductor_unique_backup_path "$dest")"
91
+ /bin/cp "$dest" "$backup"
92
+ MANIFEST_LAST_BACKUP="${backup#$TARGET_ABS/}"
93
+ log " backed up existing $dest -> $backup"
94
+ }
95
+
96
+ conductor_manifest_file_matches() {
97
+ local file="$1" expected_sha="$2"
98
+ [ -n "$expected_sha" ] && [ -f "$file" ] \
99
+ && [ "$(conductor_sha256_file "$file")" = "$expected_sha" ]
100
+ }
101
+
102
+ # Return the one-line block manifest entry matching a host relative path/name.
103
+ conductor_manifest_block_entry() {
104
+ local wanted_path="$1" wanted_name="$2" line found_path found_name
105
+ [ -f "$MANIFEST_PATH" ] || return 1
106
+ while IFS= read -r line; do
107
+ case "$line" in
108
+ *'"type": "block"'*)
109
+ found_path="$(printf '%s' "$line" | /usr/bin/sed -E 's/.*"path": *"([^"]*)".*/\1/')"
110
+ found_name="$(printf '%s' "$line" | /usr/bin/sed -E 's/.*"block": *"([^"]*)".*/\1/')"
111
+ [ "$found_path" = "$wanted_path" ] && [ "$found_name" = "$wanted_name" ] && {
112
+ printf '%s\n' "$line"; return 0;
113
+ }
114
+ ;;
115
+ esac
116
+ done < "$MANIFEST_PATH"
117
+ return 1
118
+ }
@@ -0,0 +1,32 @@
1
+ # CONDUCTOR stale-token list — data file for tools/check-stale-tokens.sh (ADR-039).
2
+ #
3
+ # Format: one rule per line, TAB-separated fields:
4
+ # pattern<TAB>reason<TAB>hint<TAB>allow_regex(optional)
5
+ #
6
+ # - pattern : fixed string (grep -F) that indicates a KNOWN-FALSE claim on living surfaces
7
+ # - reason : why the claim is false (which release/ADR falsified it)
8
+ # - hint : what to write instead
9
+ # - allow_regex : extended regex — a matching LINE is allowed (legacy/historical qualifiers)
10
+ #
11
+ # Waiver: a line containing `stale-ok: <why>` is always skipped.
12
+ # Process rule (R5 extension): an ADR that flips a fact adds the now-false claim here in the SAME PR.
13
+ # Scope: living public surfaces only — frozen history (CHANGELOG / ADRs / audits / plans / specs /
14
+ # archive / private session docs) is NOT scanned. See the checker header for the exact path set.
15
+ .codex/codex.md Codex adapter emits AGENTS.md (v0.6.1, ADR from 0.3.0 era) write AGENTS.md, or qualify as superseded/legacy supersed|early-design|legacy|historical
16
+ .windsurf/rules Windsurf target moved to .devin/rules/ in v0.3.0 (Devin Desktop rebrand) write .devin/rules/ (legacy .windsurf/rules/ still read) legacy|still read|\.devin
17
+ v0.2.0-foundation stale version label; versions are single-sourced from package.json reference package.json / CHANGELOG instead of stamping
18
+ no npx npm package `omniconductor` published since v0.4 remove; the npx CLI exists
19
+ no NPM package npm package `omniconductor` published since v0.4 remove; the npm package exists
20
+ not yet available all 6 adapters + the npx CLI shipped (CLI since v0.2) remove the "planned/not yet available" framing
21
+ until P2 adapter ships all 6 adapters shipped transform.sh in v0.2 drop the "until ... ships" framing
22
+ tools without an adapter all 6 tools have working adapters since v0.2/v0.3 rewrite: all 6 tools have adapters
23
+ ❌ No sub-agent tools ship sub-agent dispatch natively (2026, ADR-031) use capability-vs-emission framing (ADR-031/034)
24
+ ❌ No hooks tools ship hooks natively (2026, ADR-031; Windsurf lacks only session/stop events) use capability-vs-emission framing (ADR-031/034)
25
+ No hooks, no sub-agents tools ship both natively (2026, ADR-031) use capability-vs-emission framing (ADR-031/034)
26
+ no sub-agents/hooks tools ship both natively (2026, ADR-031) use capability-vs-emission framing (ADR-031/034)
27
+ No sub-agents, no hooks tools ship both natively (2026, ADR-031) use capability-vs-emission framing (ADR-031/034)
28
+ Single model per session all 6 tools support per-task model selection (ADR-031) write: manual per-task pick (tool-native); emission is Phase 2
29
+ Single model per Codex invocation Codex supports per-task model selection (ADR-031) write: manual per-task pick (tool-native)
30
+ Codex (T3) Codex tier corrected T3 → T2 in v0.7.0 (matrix is the tier source, ADR-040 M8) write Codex (T2)
31
+ Codex adapter (T3) Codex tier corrected T3 → T2 in v0.7.0 write T2
32
+ pre-1.0 (active v1.0.0 shipped 2026-07-09 — the pre-1.0 framing is falsified reference CHANGELOG for current version