Reviewer: independent; no involvement in the conversation that produced
00–05. Method: every load-bearing claim re-derived from the working tree. Where I could not verify, I say "unverifiable" rather than guess. Scope:00-INDEX.md…05-impact-migration-tests.md, all read in full.
Not implementable as written. The architecture is sound and the analysis is unusually good
— 01 in particular is the most carefully verified document I have reviewed in this repo, and
Option A is the right call for the right reasons. But the spec set cannot be handed to an
implementer today, for three reasons that are structural rather than cosmetic. First, the
centerpiece schema rejects the corpus it was written for: 02's DependencyKindSchema omits
the agent, plugin, and config kinds that registry.json actually uses, and its
DependencyInputSchema regex rejects 19 real dependency refs — including every wildcard
(context:core/*) that 01 calls a crown-jewel feature. 05's Layer-1 test ("every
/content/* file parses") would fail on day one. Second, 01 and 05 were never revised
after the v2 decisions landed, and the index's table marks them "✅ v1" as though that were a
status rather than a defect. 05 still says the repo has 160 agents — four times, in its
ground-truth table, in its Stage 3 deliverable, and twice in its test spec — a number the index
itself explicitly flags as false. 01 still carries the committed-.env security alarm the
index retracted as a non-finding. Third, the single hardest task in the migration — merging
.opencode/ and plugins/claude-code/ into /content/ — is owned by nobody. The index
declares it a merge; 05 Stage 3 specifies a "one-shot migration script (content/* from
current .opencode/*)", which is exactly the copy the index forbids. For code-reviewer the
two source bodies are 108 vs 269 lines with entirely different descriptions, and no document
states which wins. Separately, the precedence decision the index calls "CONFIRMED — question
closed" rests on three in-repo documents this project wrote about itself, none of which is
OpenCode's resolver; that is an overclaim on the most load-bearing semantic in the design. Fix
the schema, revise 01/05, and assign the merge an owner, and this becomes buildable — the
bones are good.
Ranked by severity. Every command below is reproducible from the repo root.
05's "160 agents" — false, uncorrected, and load-bearing on a deliverable05:22 states the source of truth is .opencode/ with 160 agent .md files, citing
find .opencode -path '*agent*' -name '*.md'.
$ find .opencode -path '*agent*' -name '*.md' | wc -l
160
$ find .opencode/agent -name '*.md' | wc -l
34
The command reproduces, but the interpretation is wrong: -path '*agent*' matches
.opencode/context/open**agent**s-repo/** (23 files in guides/ alone), and
.opencode/prompts/core/open**agent**/ (8 files). The real count is 34, exactly as
00-INDEX.md:73 says — and the index explicitly calls out "one spec said 160 — false."
This is not a stray typo. 160 propagates into work:
05:22 ground-truth table05:220 Stage 3 goal — *"move all 160 agents + skills/commands/context into /content"*05:222 — *"fixes the 7-vs-160 drift"*05:343 Layer 4 test case 1 — *"guards the 7-vs-160 drift regression directly"*A Stage-3 deliverable and a test assertion are both scoped to a number that is 4.7× reality.
05 is marked "✅ v1" in the index's spec table (00:372) and was never revised.
02's dependency schema rejects 19 real refs — the schema does not fit the corpus02:369-383 defines:
export const DependencyKindSchema = z.enum(["subagent","context","command","skill","tool"]);
export const DependencyInputSchema = z.union([
DependencyRefSchema,
z.string().regex(/^(subagent|context|command|skill|tool):[a-z0-9-]+$/),
]);
and comments that this is *"the REAL on-disk format in both .opencode/config/agent-metadata.json
and registry.json."* It is not. Measured against registry.json:
refs REJECTED by 02 DependencyInputSchema regex: 19
context:core/context-system/* context:core/* context:ui/*
context:development/* context:openagents-repo/* context:project-intelligence/*
agent:openagent agent:opencoder agent:system-builder
agent:copywriter agent:technical-writer agent:data-analyst
agent:eval-runner agent:repo-manager plugin:notify
config:env-example config:agent-metadata config:readme
Three independent defects:
agent: is missing from the kind enum entirely. The IR cannot express a dependency on a
primary agent, though registry.json and all five profiles use it.plugin: and config: are missing, though plugin:notify, config:agent-metadata, and
config:readme are live registry refs (01 §10 discusses config:readme at length).context:core/* is the mechanism 01 §7.2 documents
as expand_context_wildcard and the preservation checklist requires (01:1244: "context:
wildcard expansion preserved"). 02's schema makes it unrepresentable.02 contradicts itself here: 02:799-801 ComponentTypeSchema has nine types
(agent, subagent, command, skill, context, tool, hook, plugin, config) while
DependencyKindSchema has five. The same document cannot agree on the type vocabulary.
Consequence: 05's Layer 1 ("Every /content/* file parses against the Zod IR", 05:317) fails
at Stage 1 on real content.
00:219-229 declares last-match-wins "✅ CONFIRMED (v2.1) — question closed", "Verified
against three independent in-repo sources", concluding: *"No further verification against
OpenCode's resolver is required."*
I verified all three citations. Two are accurate as quotes; one has a wrong line number; and the claim of independence does not hold.
| Cited source | Verified? | What it actually is |
|---|---|---|
docs/archive/planning/12-MASTER-SYNTHESIS.md:432 |
✅ text at :432 | This project's own planning doc, describing a different, rejected format |
.opencode/context/openagents-repo/standards/permission-patterns.md:11 |
✅ verbatim | This project's own context file |
.opencode/context/openagents-repo/standards/agent-frontmatter.md:45 |
✅ verbatim | This project's own context file |
All three are documents this repository wrote about itself. None is OpenCode's resolver, OpenCode's upstream documentation, or an executed test. Three in-repo files agreeing is one belief written down three times — not three independent verifications.
Worse, the 12-MASTER-SYNTHESIS.md:432 source describes a format that does not exist:
"permission": [
{ "deny": "bash(**)" },
{ "allow": "bash(git status)" }
]
That is a JSON array of {decision: pattern} objects — a proposal from a planning doc, not
OpenCode's real map form ({"*": "deny", "git status*": "allow"}). A doc asserting last-match-wins
about its own hypothetical schema is not evidence about OpenCode's behavior.
I am not claiming last-match-wins is wrong. The reasoning in 02 §1.2.4 and 03 §0.5 —
that first-match-wins provably breaks coder-agent and openagent as authored — is sound and
I verified both agent files reproduce exactly as quoted. Last-match-wins is almost certainly
right. The defect is the epistemics: the single most load-bearing semantic in the design is
marked "closed, no further verification required" on the strength of self-citation. The cost of
being wrong is every generated permission block on every target inverting. A ten-minute
experiment against a real OpenCode install would settle it. Do that before freezing.
Also: 03:129 cites this quote at :442; the index cites :432. :432 is correct.
02's00:66-67 asserts: "Verified distribution: high 116, critical 113, low 34, medium 31,
reference 1." Those sum to 295, which matches no count in any document.
02:640-648 asserts: high 113, critical 111, low 34, medium 29, reference 1 (= 288).
Measured over the leading-window markers:
$ for f in $(find .opencode/context -name '*.md'); do head -1 "$f" | grep -o 'Priority: *[a-z]*'; done \
| sed 's/Priority: *//' | sort | uniq -c | sort -rn
112 high
111 critical
34 low
29 medium
1 reference
112 / 111 / 34 / 29 / 1 = 287 — which exactly matches the 287 line-1 markers 02 §4.1
independently derived. 02 is right on four of five values and wrong on high; the index is
wrong on three of five and its total is internally inconsistent. Since the index "wins on
conflict" (00:9), an implementer following the rules adopts the more wrong numbers.
The Priority: reference outlier is real and verified — that finding stands.
01's navigation counts are inflated by a suffix-match bug01:518 — *"79 navigation.md files — one per directory, at every level"*, and 01:651-652
tabulates 79 on disk / 36 in registry.
$ find .opencode/context -name 'navigation.md' | wc -l
76
$ find .opencode/context -name '*navigation.md' | wc -l
79
$ find .opencode/context -name '*navigation.md' ! -name 'navigation.md'
.opencode/context/development/fullstack-navigation.md
.opencode/context/development/ui-navigation.md
.opencode/context/development/backend-navigation.md
The methodology matched *navigation.md and swept in three files that are not navigation
hubs. Registry side is identically inflated: 33 exact-basename entries, 36 by suffix.
The derived claim "43 of 79 navigation hubs are unregistered" (01:655) survives only because
the errors cancel (76−33 = 79−36 = 43). The conclusion is right; the evidence is wrong — and
the "one per directory, at every level" characterization is built on the bad number. Note the
interaction: ui-navigation is also one of the three duplicate registry ids 01 §3.6 flags,
so the bug and the finding overlap.
01 still asserts the committed-.env security alarm the index retracted00:80-82 — *"Non-finding (corrected): an earlier claim of 'committed .env files needing
rotation' is false. Verified: 0 tracked .env files."* I confirm:
$ git ls-files | grep -E '\.env$|\.env\.'
(no output)
$ sed -n '9,13p' .gitignore
.env
.env.local
...
But 01 was never updated and still says:
01:823 — `.opencode/tool/.env` | 🔴 **SECURITY** | A committed `.env` inside the tool dir01:842 — `.opencode/plugin/.env` | 🔴 **SECURITY** | Second committed `.env`01:1429-1431 Q17 — *".opencode/tool/.env and .opencode/plugin/.env are in the repo. Are they real secrets (⇒ rotate + purge history)…? Needs a human to look."*Both files exist on disk but are untracked and gitignored. Q17 asks a human to consider
history purging for a non-incident. The index wins, so Q17 is dead — but nothing in the doc
set says so, and a reader of 01 alone would open a security workstream.
02 and 0300:76-77 — *"Skills (OpenCode) | 2 dirs"* and *"Skills (Claude Code plugin) | ~11"*.
$ ls -d .opencode/skills/*/ → 4 (context-manager, context7, smart-router-skill, task-management)
$ ls -d .opencode/skill/*/ → 2 (project-orchestration, task-management)
$ ls -d plugins/claude-code/skills/*/ | wc -l → 12
OpenCode has 4 + 2 = 6 skill directories across two sibling trees; CC has 12, not ~11.
01 §0 gets this exactly right. The index — which overrides 01 on conflict — gets it wrong,
and both downstream docs inherited it: 02:479 (*"OpenCode has 2 skill dirs, the CC plugin
has ~11") and 03:48 ("2 OpenCode skills, ~11 CC plugin skills"*). Three of five documents
now carry the wrong number because the authoritative one did.
model: sonnet" — 2 of 7 are haiku00:158 and 01:255 both state the CC agents hardcode model: sonnet.
$ grep -H '^model:' plugins/claude-code/agents/*.md
code-reviewer.md:model: sonnet coder-agent.md:model: sonnet
context-scout.md:model: haiku external-scout.md:model: haiku
task-manager.md:model: sonnet context-manager.md:model: sonnet
test-engineer.md:model: sonnet
Five sonnet, two haiku. This is not pedantry: the two haiku agents are the scouts —
cheap, high-frequency, read-only discovery agents. That is a deliberate cost/latency tier,
not an accidental hardcode. Locked decision #2 (model: null) deletes it and no document
proposes a replacement. 01's Q3 asks about "a per-adapter default", which cannot express
"this agent is cheap and that one isn't". Both docs characterize the finding as a uniform,
trivially-droppable constant, which understates what is being thrown away. See L4.
00:75) + 03:47: 296 .md (286 HTML-comment / 3 YAML / 7 neither)02:40 + §4.1: 297 .md (287 line-1 / 7 elsewhere / 3 no-marker)01:38: 300 files (297 .md + paths.json + 2 JSON schemas)Measured:
$ find .opencode/context -name '*.md' | wc -l → 297
$ find .opencode/context -type f -name '*.md' | wc -l → 294 ← note the delta
$ find .opencode/context -type f ! -name '*.md' | wc -l → 3
297 .md path entries, but only 294 regular files. The other three are symlinks (see
U1). 02's 297 and 01's 300 are right as path counts; the index's 296 is wrong and its
286/3/7 bucket split is superseded by 02's verified 287/7/3, which I reproduce exactly.
02 §4.1's parser analysis — including the line-232 and line-301 prose-marker traps and the
dual-format files — is correct and is the best analysis in the doc set. 02's own Q4 flags
the delta as "no design impact"; that is right for the delta, wrong for the composition.
Also 01:1206: "All 300 context files migrated (297 .md + paths.json + 2 task schemas)" —
arithmetically fine, but it means 294 real files + 3 symlinks + 3 JSON.
01's unregistered-context count is off by two01:649 claims 112 on-disk-but-unregistered. Measured: 110 (.md on disk, not matching
any registry path). 01's companion claims verify exactly: registry has 191 entries /
188 unique paths ✅, and 0 registry entries point at a missing .md ✅ (the single
"missing" path is paths.json, which is the non-.md special case 01 correctly documents).
Both index-level and doc-level. This list matters as much as the falsifications:
| Claim | Status |
|---|---|
| 34 agents, 20 commands | ✅ exact |
coder-agent.md / reviewer.md / openagent.md permission blocks quoted verbatim |
✅ byte-exact |
types.ts:116 z.union([z.string(), z.string()]) no-op union |
✅ exact line |
BaseAdapter.ts:187-208 warning templates |
✅ exact strings |
WindsurfAdapter.ts:511-516 hasAllow = permObj.allow !== undefined, fails closed |
✅ — the v2 correction is right |
ClaudeAdapter.ts:95-124 writes .claude/agents/*.md + .claude/config.json (project, not plugin layout) |
✅ |
CapabilityMatrix.ts: agentModes: claude=full, agentCategories: claude=partial, pathPatterns: windsurf=partial, taskDelegation: claude=full |
✅ all four; 03 §5's corrections are justified |
bin/oac.js execFileSync('bun', …) → "Bun is required" |
✅ verbatim |
| 15 files using Bun-only APIs | ✅ exactly 15 |
| Version drift: 0.7.1 / 0.7.1 / 1.0.0 / 0.1.0 / 0.1.1 / 1.0.0 / 1.0.2 | ✅ all seven |
install.sh 1510 lines, 35 functions; get_registry_key():331, get_install_path():353, expand_context_wildcard():361, expand_selected_components():373, resolve_dependencies():420 |
✅ every line number |
add.ts never resolves dependencies (zero references) |
✅ |
/add-context's 3 path-style context deps all resolve to nothing |
✅ verified dead |
Registry: 245 entries (8/19/17/2/1/4/191/3); dupe ids agents, ui-navigation, context-bundle-template; orphan top-level subagents key; version 2.0.0 vs metadata.schemaVersion 1.0.0 |
✅ all |
Profiles 25/41/25/50/68; developer badge RECOMMENDED; advanced.additionalPaths = [".Building/", ".github/workflows/"] |
✅ all |
agent-metadata.json: 28 entries, 8 agent / 20 subagent |
✅ |
CC coder-agent ships tools: Read, Write, Edit, Glob, Grep — no disallowedTools ⇒ live Edit grant, security globs gone |
✅ the LIVE SECURITY GAP (index #10) is real |
bundled.ts uses registry.json-absence as the package-root heuristic |
✅ (and see G4) |
sync-to-claude.sh = 39 lines |
✅ |
apply.ts cursor guard warn: 80*1024, limit: 100*1024 |
✅ |
session-start.sh + escape_for_json() + "SECURITY: Prevents command injection" |
✅ exists, hooks/ only |
.opencode/config.json = {"agent": "eval-runner"} |
✅ |
All router.sh are 0755 |
✅ |
03 §2.3/§2.5/§2.6 CC-documentation quotes ("plugin subagents don't support hooks…",
"only the agent and subagentStatusLine keys are supported", tools: is names-only). These
cite sub-agents.md / plugins.md / permissions.md — external docs not in this repo. I cannot
verify them here. 03 correctly self-labels confidence (high on the doc statement, medium on
applicability) — that is the right way to write an unverified claim and the rest of the set
should copy it. But note the entire CC adapter design rests on them, and 00:352-360
promotes them to a locked "✅ question CLOSED, negative" without preserving that hedge.03 §4 Windsurf format — explicitly self-labelled "Inferred, not verified against a live
install." Honest, and correspondingly Q9 is a genuine blocker for that target.00:161-163 — settings.json {"model": "opusplan"} no-op. Correctly flagged "Unverified …
flagged, not asserted."00:219 marks precedence "✅ CONFIRMED (v2.1) — question closed." Yet:
02:881 Open Question 1 — *"⚠️ Needs ratification. The index says 'first-match-wins'…"*03:96 §0.5 — *"⚠️ BLOCKING CONTRADICTION — first-match-wins vs last-match-wins"*03:836 Q1 — *"BLOCKING … must be resolved before 02 or 03 freeze"*02 and 03 are arguing against an index version that no longer exists. They quote the
index as saying "first-match-wins"; the current index says the opposite, at length, and credits
"Two agents reached this conclusion independently" — i.e. it absorbed 02's and 03's
arguments and then never told them. An implementer reading 03 sees a blocking contradiction
that is resolved two files away. Same pattern, same cause for 03 Q7 ("warning count: 1 or 2?
Recommend accepting 2 and correcting the index") — 00:346-350 already says 2. And 00:352
already closes the settings.json question 03 §2.6 re-derives.
Three of the ~60 open questions are not open. They are stale citations. Not harmless: 03
Q1 is labelled BLOCKING and would stop an implementer cold.
05 Stage 3 specifies exactly the copy the index forbids00:102-107 finding #3 — *"Seeding /content/ is a MERGE, not a copy. Drift is bidirectional
… Seeding from .opencode/ alone destroys all of it."*
05:221 Stage 3 deliverable — "a one-shot migration script (content/* from current
.opencode/*)".
That is the copy. 05 names no merge, no CC-side harvest, no conflict rule — and its Stage 3
goal (05:220) is "move all 160 agents" (F1), i.e. it is not even scoped to the right tree.
Under 05 as written, the 12 CC skills, the 6 CC-only commands, every <example> block, and
session-start.sh are destroyed at Stage 3 — precisely the outcome the index's #1 finding
exists to prevent. See L1.
05's minimal test contradicts 02's schema and 03's transform05:328 Layer 1 case 2: *"model: set to a string (not null) → rejected (enforces locked
decision #2)."*
But 02:330-332: model: z.string().nullable().default(null) — accepts strings, and the
comment says "When set, a neutral family id (e.g. "claude-sonnet-4"); ModelMapper resolves to
the tool's dated id." And 03:210-211 / 03:352-353 both specify inference.model set →
emit model:.
So 05 tests for a rejection that 02 does not implement and 03 explicitly relies on. This
is 02's Q3 ("Should model be authorable at all?") — unresolved, and 05 has already
assumed one answer while 02/03 assume the other. 00:22-23 admits the question is open.
Three documents, three positions, one field.
05's golden test asserts the wrong warning count05:300 — *"the known temperature-dropped warning is emitted"* (singular).
03 §2.8 and 00:346 both say code-reviewer on CC emits 2 warnings (temperature +
scoped-delegate). If the minimal test — "the only test that must exist before any deletion"
(05:287) — asserts one warning, it either fails on correct output or is written loosely
enough to prove nothing.
04 conflates two different /content directories, and admits it in Q900:168 puts /content at the OAC repo root — the source the maintainers author, which
ships inside the npm package.
04:361-363 puts content/ in the user's project (<project>/content/, ~/.config/oac/content/),
tracked in the user's manifest, updated by oac update.
These are different artifacts with different lifecycles, and 04 uses one name for both. 04's
own Q9 (04:509) says the quiet part out loud: *"does the user keep an editable content/ in
their project … or is /content purely internal to the package? This is the biggest UX fork
and affects init, add, update, and migration."* It is not a UX fork — it is a
prerequisite. It decides whether oac build runs on the user's machine at all, and therefore
whether the CLI ships a parser, the adapters, and the whole IR, or just pre-built outputs. §1.1,
§1.2, §1.4, §2, §4.2 and §5.1 are all written as though it were settled in opposite directions.
01 §1.3 recommends a schema 02 did not adopt — with different field names01:200-208 recommends the rule list as { pattern, effect }:
capabilities:
bash:
- { pattern: "*", effect: ask }
00:209-211 and 02:155-158 adopt { scope, decision }. Same concept, different keys. 01 is
the preservation checklist an implementer will grep against; it is now describing a schema that
does not exist. 01:1172 (permission.<tool>.<glob> tri-state → capabilities rule list) is
still right; §1.3's worked YAML is not.
01 disagree on skill counts; 02/03 follow the indexCovered in F7. 01 is right (4+2 / 12); the authoritative index is wrong (2 / ~11); 02:479
and 03:48 inherited the index's error. Since the index wins on conflict, the rule as written
propagates the falsehood.
03 §0.4 escalates to 02 a question 02 never answers03:88-94 — *"Implicit default — when no * rule exists. ⚠️ The IR does not currently
define this … Escalated as Open Question #Q2. Every collapse rule below depends on it."*
02 does not define it. 02 §1.2.5 defines only two cases — capability absent → [] → tool
default; rules present, none match → allow. Neither covers "rules present, all match a subset,
no * rule" — which is coder-agent.edit (five denies, no *), the exact case that produces the
live security gap (index #10). 03 proposes the inference rule ("opposite of the decisions
present; mixed-without-* is an error"); 02 never adopts it; the index never rules. So the
highest-severity finding in the whole set (00:135-142) depends on a rule no document owns.
02 §8 marks hooks a "blocking" hint; 03 treats it as a warning02:868 — hooks | hint (**blocking**) | cursor/windsurf (none), and 02:870 says delegate
on Cursor is *"a hard drop → blocker, not a soft warning."*
03:680 and 03:764 emit both as ordinary ⚠️ warnings; 03:68 says "Builds fail only on
blockers." So oac build --target cursor either fails on every delegating agent (02) or
warns and proceeds (03). Nobody reconciled it, and it changes whether Cursor is a supported
target at all.
03's citation drift03:129 cites docs/archive/planning/12-MASTER-SYNTHESIS.md:442; the index cites :432; :432 is
correct (verified). Minor on its own, but it is the same quote used to close the most important
question in the set (F3), and two of the docs citing it disagree on where it is.
Testing 01 §11's checklist against 02's schema and 03's transforms. Items with no home:
01's checklist demands harvesting from the CC side:
01:1179 — *"CC <example> blocks harvested from plugins/claude-code/agents/"*01:1275 — *"🔴 12 CC skills harvested"*01:1203 — *"6 CC-only commands … harvested"*01:1307-1313 — all six session-start.sh capabilities02 models the result (examples[], SkillSchema) but explicitly disclaims the process
(02:396 — *"a merge concern for Agent E"*). 03 says session-start.sh "must be preserved
into /content/" (03:460) and moves on. Agent E's document (05) specifies a copy from
.opencode/ (C2). The merge is passed to Agent E, and Agent E did not receive it.
This is not a paperwork gap. The concrete unanswered question:
$ wc -l .opencode/agent/subagents/code/reviewer.md plugins/claude-code/agents/code-reviewer.md
108 .opencode/agent/subagents/code/reviewer.md
269 plugins/claude-code/agents/code-reviewer.md
OpenCode description: "Code review, security, and quality assurance agent"
CC description: "Review code for security vulnerabilities, correctness, and quality.
Use after implementation is complete and before committing."
Two source files, 108 vs 269 lines, entirely different descriptions, both claiming to be
code-reviewer. Which body becomes /content/agents/code-reviewer.md? No document says.
Seven agents are in this position.
Tellingly, 00's worked example (00:275) uses the CC description — while 01:80 presents
the OpenCode description as "verified verbatim" for the same agent. The index has silently
already made a merge decision for one field of one agent, and did not record it as a decision.
No schema in 02 — Agent, Skill, Command, Context, Tool, Hook, Registry entry — has a targets:
or applicability field. Everything is implicitly universal. But 01 documents whole classes that
are structurally single-target:
01 §5) implement the OpenCode tool API; CC has no equivalent primitive (01 Q18).context: fork, agent: — 03:441-443) and
advertise as oac: commands. On an OpenCode build they are inert..ts via router.sh (01 §4.4). On a CC build
they need a Bun runtime the CC user never installed.session-start.sh is a CC hook with no OpenCode equivalent (00:104).01's Q16 asks which set seeds /content/skills/ and offers "the union of 16". If the answer
is the union, then oac build --target opencode emits 12 skills OpenCode cannot use and
--target claude emits 6 that CC cannot run — and 03's only lever is a per-agent, per-target
warning, which turns "never silent" into unreadable noise. The union is the right answer and it
requires a schema field that does not exist.
coder-agent's bash allowlist (verified verbatim):
"bash .opencode/skills/task-management/router.sh complete*": "allow"
The scope string hardcodes a build-output path into neutral content. 01 §3.4 catches this
class of problem (~129 files) and Q12 recommends {{CONTEXT_ROOT}}-style tokens + a
no raw .opencode/ in /content/ lint. But:
02's RuleSchema.scope = z.string().min(1) — no token concept, no validation.03 §1.3 serializes scopes verbatim ("<scope>": "<decision>"); 03 §2.3 drops them.04 §2.4 specifies path rewriting for @.opencode/context/ refs on --global/--dir
installs — and only for context refs.So on a global install, coder-agent's allowlist still points at .opencode/skills/..., which is
not where the skill landed. The two router.sh commands are denied. The agent cannot complete or
report task status — the exact breakage Option A exists to prevent (00:38-39), reintroduced
by the install-location feature. 01 catches the path problem in bodies; nobody catches it in
scopes.
Compounding: the CC skill set is disjoint — there is no task-management skill in
plugins/claude-code/skills/. So on the CC target the scope references a skill that does not
exist in the output at all. It is invisible today only because 03 fail-closes and drops Bash.
Per F8: context-scout and external-scout ship model: haiku; the other five ship sonnet.
That encodes "scouts are cheap and fast; coders are capable." Under model: null (00:20-23),
every agent gets the same default and the tiering is gone. The user pays sonnet rates for
every discovery call.
01's Q3 offers (a) never emit model: or (b) *"a per-adapter default in adapter config"*.
Neither expresses per-agent intent. 02's Q3 pushes the other way — drop model from the
authored schema entirely. Nobody has noticed that the thing being deleted is a two-tier cost
policy, not a hardcoded constant.
A tier: fast | balanced | capable field would preserve the intent without naming a model — and
would honor decision #2's actual spirit (no hardcoded models) rather than its letter. Not
proposed anywhere.
oac build cannot express install.sh's additionalPathsregistry.json#profiles.advanced.additionalPaths = [".Building/", ".github/workflows/"]
(verified). 01:1268 requires it "preserved or replaced"; 04 §2.2 goes further and promises
a parity improvement ("install.sh only printed these … the CLI must actually copy them").
But 02's RegistrySchema (02:826-834) has no profiles object at all — only
components. Profiles exist in 02 solely as a ProfileSchema enum tag on each entry
(02:820). Under that model there is nowhere to put additionalPaths, nowhere for a profile
name/description, and nowhere for badge: "RECOMMENDED" (01:1267). Three checklist items
have no home, and 04 §2.2 reads profiles.*.components[] — a shape 02 deleted.
0-category.json icon/order/status have no schema01 §1.6 recommends promoting these into registry.json#categories[] "upgraded to objects",
and 01:1192 puts it on the checklist. 03 §1.5 keeps emitting 0-category.json on the
OpenCode target. But 02's RegistrySchema has no categories key at all — the field is
simply absent. So the OpenCode adapter is specified to emit a file whose source data the IR
cannot hold.
aliases[] is required by 01, absent from 0201:167 — *"The neutral schema needs an explicit id plus an aliases[] field"*;
01:1218 and 01:1255 both require it; 01 §7.2 documents resolve_dependencies matching
.id == id or (.aliases // []) | index(id); three registry entries use it today.
02's RegistryEntrySchema (02:803-824) has no aliases. 02 §1.8 AgentSchema has no
aliases. The tester/test-engineer/TestEngineer problem 01 §1.2 calls "highest-risk
aliasing" therefore has no representation, and 01's Q1 recommendation ("filename == id, with
aliases[] retained for dependency back-compat") is unimplementable against 02.
coder-agent declares task: { TestEngineer: "allow" } (verified). 02 §1.2.3 says the delegate
scope namespace is *"agent id (kebab)"* — but RuleSchema.scope is z.string().min(1), which
happily accepts TestEngineer, and IdSchema's kebab regex is never applied to scopes. 03:208
maps [{scope:X, decision:D}] → task: { X: "D" } verbatim.
So the pipeline preserves the bug: TestEngineer goes in, TestEngineer comes out, and the
registry id is tester. 01 §1.4 flags it ("The neutral delegate: map must pick one
(canonical id)"); no document specifies the canonicalization step or where it runs.
Version: X.Y normalization is lossy on round-trip02 §4.3 normalizes context X.Y → X.Y.0 for VersionSchema (verified real distribution:
1.0×271, 2.0×10, 1.1×4, plus 3.1, 2.1, 1.3). 02:684 says adapters re-emit the MVI
one-liner. But nothing specifies re-emitting 2.0.0 back to 2.0. A build that round-trips
context files rewrites 297 markers from Version: 2.0 to Version: 2.0.0 — churning every
context file on first build and breaking 01's "byte-for-byte" contract (03:281) and 04's
determinism requirement (04:245, "oac build && oac build is a no-op").
.oac.json discovery protocol is on the checklist and in no schema01:1221 — *".oac.json discovery protocol preserved (fast path → chain → validity check →
self-heal)"*, which 01 §3.3 argues is "strictly better" than paths.json and should become
the neutral, cross-tool mechanism (Q11). 02 has no schema for it. 03 mentions
.context-manifest.json (§2.4) but never .oac.json. 04 never mentions it. The
navigation.md-presence validity check (01:1222) likewise has no home.
Where an implementer hits a wall. Walked end-to-end as instructed.
coder-agent.md → /content → oac build. It stops at step 2.Step 1 — Author /content/agents/coder-agent.md. Fine; 00:299-312 shows the target.
Step 2 — Parse. FAIL. The agent's real metadata (agent-metadata.json) carries
dependencies: ["subagent:task-manager", "context:standards-code", …]. Its registry entry and
the profiles carry agent:openagent, config:agent-metadata, context:core/*. Per F2,
02's DependencyInputSchema rejects the agent:, config:, plugin: kinds and every
wildcard. Nineteen refs. The implementer must invent the vocabulary; 02 §7 gives nine types and
§1.5 gives five, and they disagree.
Step 3 — Desugar capabilities. edit has five denies and no * rule. 02 §1.2.5 covers
"absent" and "no rule matched", not this. 03 §0.4 says the IR must define it and escalates to
02; 02 doesn't. The implementer must guess — and guessing wrong here is the live security
gap (00:135). Wall. (See C8.)
Step 4 — Resolve delegate: {TestEngineer: allow}. Canonicalize to tester? To
test-engineer? Leave it? 01 says canonicalize to id; 02/03 pass it through. Wall. (L8.)
Step 5 — Build --target opencode. Emit permission.bash with the router.sh scopes. Are
they rewritten for --global? 04 §2.4 says only context refs. Wall. (L3.)
Step 6 — Build --target claude. Fail-closed on Bash — well specified and correct. But
03 §2.8 predicts 4 warnings for this agent, one of which is "the one to act on" because
it is a silent security downgrade with no remedy (03:601). Whether that blocks the build is
03's Q3, unanswered. Wall.
Six steps, four walls, on the agent the index nominates as the hardest case. That is the correct agent to have chosen — but the choosing was not followed by resolving.
Take .opencode/context/core/standards/code-quality.md:
<!-- Context: standards/code | Priority: critical | Version: 2.0 | Updated: 2025-01-21 -->
Parse: 02 §4.2's spec handles this correctly, including the leading-window rule that avoids
the line-232/line-301 prose traps. This is the best-specified part of the whole set. Two gaps:
description is required and unobtainable. 02:665 — description: DescriptionSchema
(z.string().min(1), not optional). The MVI marker has no description field — it is
Context | Priority | Version | Updated. 02 §4.2 step 5 derives "id/name from path,
priority = medium" on failure, but never says where description comes from. Every one of
the 294 context files fails ContextSchema validation. Same for name.category from the marker is standards/code — a {category}/{function} compound.
02:666 stores it whole in CategorySchema (open string). But 01:491 requires the dual
taxonomy be split into "Neutral category + function", and 01:1209 puts it on the
checklist. 02 has no function field. The concepts/examples/guides/lookup/errors taxonomy
(01:697, on the checklist at 01:1226) is silently flattened into a string.Serialize: per L9, Version: 2.0 comes back as 2.0.0. The file churns.
Exclusion list: 02 §4.2 step 6 hardcodes a two-path allowlist and 02's own Q5 admits
"A path allowlist is brittle as content grows." It is also already incomplete — 02 §4.1
identifies four marker-as-prose files (agents.md:232, categories.md:301, templates.md:25,
frontmatter.md:43) and excludes two. The other two (agents.md, categories.md) are handled
by the leading-window rule, which is correct — but then the allowlist is doing something different
from what its own §4.1 analysis implies, and nothing says which mechanism owns which file.
02's registry schema cannot drive 04's pipeline04 §2.2 reads profiles.*.components[] and additionalPaths. 02 §7 has no profiles object
(L5), no categories (L6), no aliases (L7). 04 §3 row 14 maps get_profile_components →
lib/registry.ts getProfileComponents() "Reads profiles.*.components[]" — against a schema
that deleted the key.
Meanwhile 02:833 — components: z.record(z.string(), z.array(RegistryEntrySchema)) — is a
bare record, so the singular/plural key mapping (get_registry_key, 01:914, checklist at
01:1246) is unconstrained. 04 §3 row 17 says "ComponentTypeSchema + plural section keys;
fold aliases in" — but ComponentTypeSchema is singular ("agent") and the registry keys are
plural ("agents"), with config staying singular as a documented special case. Nobody
specifies the mapping; three docs assume someone else did.
bundled.ts breaks on two anchors, not one00:126 finding #7 — *"It identifies the package root by the absence of registry.json — but
registry.json will ship inside the package."* Verified. But the function has three
conditions:
const hasOpencode = existsSync(join(current, ".opencode"));
const hasPackageJson = existsSync(join(current, "package.json"));
// + NOT registry.json
and BUNDLED_SUBDIRS = [".opencode/agent", ".opencode/context", ".opencode/skills"].
Post-refactor, 04 §5.1 explicitly stops shipping .opencode/. So hasOpencode is false at
every level and the walk throws before the registry.json heuristic is ever consulted. The index
names one bug; there are two, and the .opencode/ anchor fails first. 04 §5.1 does say to
"replace the heuristic with an explicit package.json name check", which would incidentally
fix both — but only if the implementer knows to look. Note also BUNDLED_SUBDIRS omits
.opencode/command today, so commands are not currently bundled at all — an existing bug
neither 01 nor 04 records, and 04's retarget list (04:458) would silently inherit it.
03 §1.3's "emit a key only when a rule is not a plain *: allow" is unspecified for the round-trip03:236-239 — "read/grep/glob are granted by default in OpenCode; emit a key only when a
rule is not a plain *: allow."
So read: allow → no read: key emitted. But 02 §1.2.5 says an absent capability
parses back to [] → "tool default", not to [{scope:"*", decision:"allow"}]. Therefore
IR → OpenCode → IR maps read: [{*, allow}] to read: []. Not idempotent. This directly
violates 04:245 ("oac build && oac build is a no-op"), 05 Layer 2 case 2 (".opencode
output → re-parse → re-build → equals first build"), and 00:330 (*"OpenCode round-trips
Option A exactly"*). The claim of exact round-trip is false for exactly the three capabilities
03 optimizes.
04 §2.1 requires byte-identical output; 04 Stage 6 routes writes through installer.ts, which
skips user-modified files unless --yolo. So oac build on a repo with one hand-edited
generated file produces output that is not a function of /content — and --check
(04:116, "exit 1 if drift") will report drift that build then refuses to fix. Neither 04
nor 05 addresses the interaction, and 05's Stage 5 exit criterion ("a fresh clone → oac
build → clean git diff") only holds on a fresh clone — i.e. it tests the one case where the
conflict cannot arise.
05's Stage 2 gate has an escape hatch that voids it05:215 — *"generated .opencode/ code-reviewer is byte-identical (or diff-explained) to
hand-maintained."*
"Or diff-explained" makes the gate unfalsifiable. 05:91 calls this "the single most important
gate — it is the 'left' of both delete arrows." A gate that passes on a prose explanation is not
a gate. Given that the two code-reviewer sources differ by 161 lines (L1), the diff will be
enormous and "explaining" it will be the entire merge decision, made under deadline pressure at
the moment the deletion is blocked on it.
Count check: the brief says ~25+. Actual: 60 (01: 24, 02: 10, 03: 10, 04: 9, 05: 7). The volume is itself a finding — 60 open questions is not a spec, it is a research note. Below I triage all that matter; unlisted ones are defaults.
| Question | Verdict | Recommendation |
|---|---|---|
| 03 Q1 — first/last-match-wins (self-labelled BLOCKING) | already-answered | 00:219 closed it (last-match-wins). Delete from 03. But F3: re-verify against a real OpenCode install before freeze — the "confirmed" rests on self-citation. |
| 02 Q1 — same, "needs ratification" | already-answered | Same. Delete. |
| 03 Q7 — code-reviewer 1 or 2 warnings | already-answered | 00:346 says 2. Delete; fix 05:300 (C4). |
01 Q17 — committed .env, rotate history? |
already-answered | 00:80 retracted it as false (F6). Close now. Retain only the build-hygiene rule: never glob .env into /content. |
| 01 Q2 — flat vs rule-list capabilities | already-answered | Locked decision #5. Delete. |
03 Q2 / 02 (missing) — implicit default with no * rule |
🔴 BLOCKER | Nobody owns it (C8) and the live security gap depends on it. Adopt 03's rule: opposite of decisions present; mixed-without-* is an error. Put it in 02 §1.2.5. Nothing builds until this exists. |
02 Q3 / 01 Q3 / 05 L1-case-2 — is model authorable? |
🔴 BLOCKER | Three docs, three answers (C3). Decide: model not authorable; add tier: fast\|balanced\|capable to preserve the haiku/sonnet intent (L4). Then 05's test is right and 03 drops its model set → rows. |
04 Q9 — is content/ in the user's project? |
🔴 BLOCKER | Mislabelled "UX fork" (C5). It decides whether the CLI ships the IR + adapters at all. Recommend: yes, editable content/ in-project — otherwise oac build has no input and the whole "author once, build many" premise dies at the user boundary. |
01 Q16 — which skill set seeds /content/skills/ |
🔴 BLOCKER | Answer is the union of 16 — and that answer requires a targets: [] schema field that does not exist (L2). Add the field, then the question is trivial. |
01 Q12 — de-hardcoding .opencode/ from ~129 bodies |
🔴 BLOCKER | Take 01's recommendation (tokens + lint), and extend it to permission scopes (L3), which 01 did not consider. Without it, generated CC agents point at nonexistent paths — 01 correctly suspects this is why the CC plugin was hand-written. |
| 01 Q10 — HTML comment or YAML | already-answered | Locked decision #6. Close. 02 §4 is the implementation and is good. |
| 01 Q1 — canonical id vs filename | 🔴 BLOCKER | 02 has no aliases[] (L7), so 01's own recommendation is unimplementable. Adopt filename == id + add aliases[] to 02 §1.8 and §7. Blocks every dependency edge. |
| 01 Q20 — fail-fast on unresolvable deps | default | Yes, hard error. 02:812 already says so. 01 correctly warns this "will surface currently-hidden breakage" — budget for it (the three /add-context deps are verified dead today). |
02 Q2 — Priority: reference |
default | Fix the one file → low. Keep the enum closed. Do it now; it is a one-line commit. |
| 03 Q3 — dropped security globs: warn or block? | 🟠 blocker-ish | This is the live gap (index #10) shipping in production. Recommend: blocker with explicit --allow-unsafe-degradation opt-in. A warning in a build nobody reads is how the gap got there. |
| 03 Q9 — Windsurf real format | 🟠 blocker for that target only | The whole adapter is "inferred, never checked against a live install." Either verify or cut Windsurf from v1. Do not ship an adapter written from a stub. |
| 04 Q1 — Node-portable vs compiled binaries | default | Option A (Node shim). 04 already recommends it; 00:88 calls it "mechanical". Close it. |
| 05 Q1 — test runner | default | vitest for new packages. Close. |
| 05 Q3 — commit generated output? | default | Commit through Stage 4, flip at Stage 5, exactly as 05 recommends. Close. |
| 01 Q15 — Bun runtime for skills | 🟠 blocker for skills | router.sh is bash + Bun .ts. 04 §4.3 sells "No shell scripts" as the Windows fix while every skill ships one (all four verified 0755). The contradiction is unaddressed. |
| 01 Q22 — profile drift, 5 diffs | blocker, human-only | Correctly identified as un-automatable. Assign it. Note 02 deleted the profiles object it needs (L5). |
| 01 Q7 — the dark orchestration feature | default | Delete or register; do not carry ambiguity into /content. Recommend delete (unregistered, uninstallable, undocumented outside itself) — reversible via git. |
| 01 Q4 (0-category), Q6 (author), Q9 (version format), Q14 (index vs navigation), Q21 (registry versions), Q24 (config: components) | bikeshedding | Close all six with defaults: promote icon/order/status + delete common*; author: oac; SemVer everywhere with X.Y→X.Y.0 and a re-emit rule (L9); navigation.md canonical; one schemaVersion + one contentVersion; drop config:readme. None blocks code. |
02 Q6 (web capability), Q10 (primary\|subagent enough), 03 Q10 (cursor cell semantics), 04 Q5 (Windows global path), 04 Q8 (backup retention) |
bikeshedding | Defaults: keep web; two roles; none; %APPDATA%; oac clean. |
| 02 Q4 — 296 vs 297 | already-answered by measurement | Neither. 297 path entries = 294 files + 3 symlinks (F9, U1). Fix all four docs. |
| 02 Q5 — exclusion allowlist brittleness | default | Use <!-- oac:no-parse -->. The allowlist is already incomplete (G2). |
The dangerous gaps — risks no document mentions at all.
$ find .opencode/context -name '*.md' ! -type f -exec ls -la {} \;
core/standards/tests.md -> test-coverage.md
core/standards/docs.md -> documentation.md
core/standards/code.md -> code-quality.md
Zero mentions of symlinks across all six documents. Consequences, none considered:
core.symlinks=true checks out a symlink
as a plain text file containing the target path. code.md becomes a ~16-byte file whose
entire content is the string code-quality.md. Any agent instructed to read
.opencode/context/core/standards/code.md gets garbage. 04 §4.3 is a dedicated Windows
section (issues #304/#312) covering path separators, line endings, and case sensitivity — and
misses the one thing that silently corrupts content.04 §5 specifies files: ["content/", …] with no symlink policy.04 §2.1
demands byte-identical output and 05 Stage 5 demands git diff clean after build. A
dereferenced symlink is a new file in the diff, every time.01 §3.6 flags code-quality.md,
test-coverage.md, documentation.md as having "two registry ids each" and requires
collapsing to aliases[] (01:1218). It concludes *"Two mechanisms for one concept"*.
There are three — and the third is the filesystem. Collapsing to aliases[] deletes the
symlinks, and three files reference the alias paths directly:
.opencode/agent/subagents/core/context-retriever.md,
.opencode/context/openagents-repo/templates/context-bundle-template.md, and
plugins/claude-code/skills/test-generation/SKILL.md. Deleting the symlinks breaks all three.05 §1.3 is proud that *"Users migrate by doing nothing; the payload behind the same entry
point improves"* (05:124). That is also the risk, and it is never named. CC users on
/plugin install oac receive whatever oac build --target claude commits — automatically. If a
build regression ships (say, 03 §2.3's fail-closed rule firing on an agent that needed Bash),
every CC user's plugin breaks with no action on their part and no way to pin the old version.
No document specifies: a rollback procedure, a canary/staged rollout, a --pin for the
marketplace, or an abort criterion. 05 §2 defines exit criteria for entering each stage and
none for reversing one. Stage 3 deletes sync-to-claude.sh — the only thing producing CC
output today — behind a gate whose escape hatch is "or diff-explained" (G7).
oac build --target opencode can reproduce today's .opencode/The entire plan assumes it can. But .opencode/ currently contains, verified: prompts/ (10
per-model variants + committed eval results), node_modules/, bun.lock, .env ×2 (untracked),
tool/ TypeScript workspaces with their own package.json/tsconfig.json, plugin/*.ts +
.ts.disabled twin, docs/, scripts/task-cli.ts, profiles/, opencode.json, config.json.
03 §1.1's OpenCode layout emits seven things: agent/, config/agent-metadata.json,
skill/, command/, context/, opencode.json, config.json. Everything else in the real
.opencode/ is not generated by anything. So after Stage 5's "stop committing generated
trees", either those files vanish or .opencode/ is a hybrid of generated and hand-maintained
content — which is the exact dual-source mess (05:253) Stage 5 exists to end. 01 inventories
these assets; nobody checks them against 03's output surface.
session-start.sh proves the threat is understoodsession-start.sh carries escape_for_json() with the comment "SECURITY: Prevents command
injection attacks from malicious SKILL.md files" (verified) — i.e. the project already knows
that content can be hostile, and 01:1313 correctly requires the defense be preserved.
But the refactor inverts the trust model and nobody notices. Today install.sh copies inert
files. After the refactor, oac build parses untrusted content and generates executable
artifacts — permission blocks, hooks.json shell commands (03:464), router.sh invocations
baked into allowlists. A malicious or compromised /content/agents/*.md can inject an
{scope: "*", decision: "allow"} bash rule, or a HookActionSchema.command (02:768 —
command: z.string().min(1), no validation whatsoever) that runs at every SessionStart.
There is no content signing, no checksum verification path (02:822 adds a checksum field
but nothing verifies it), no provenance check, and no threat model. 01 §9.2 preserves one
escaping function; nobody asks what the build itself is now trusted to do.
oac build --target opencode must parse 294 context files + 34 agents + 20 commands + 6 skill
trees, resolve a 245-node dependency graph, and write hundreds of files — on every add,
remove, and update (04 §1.9 marks all three "runs build pipeline: yes"). No document states
a budget, and 04 §2 specifies no incremental build or caching. If oac add context:foo takes
20 seconds, the UX regresses hard against install.sh. Unmeasurable today (nothing is built), but
unbudgeted is how it becomes unfixable.
02 §4.2 step 4 — *"Merge precedence when both YAML and MVI are present: MVI wins for the
four MVI fields; YAML supplies any extra keys."* Sensible. But nobody checked whether the three
files (csharp.md, csharp-project-structure.md, registry-dependencies.md — all verified to
start with ---) actually agree across the two blocks. If a file's YAML says
priority: high and its MVI marker says Priority: critical, MVI-wins silently discards an
authored value. Three files, five minutes to check, never checked.
--strict is specified into a corner04:116 — --strict "treat adapter warnings as errors → exit 1". But 03 §2.8 predicts 2
warnings for the simplest agent and 4 for coder-agent, and 33 of 34 agents carry
temperature (verified) which always warns on the CC target. So --strict can never pass on
the CC target for any real agent. 05 §3.3 wires golden tests into CI without specifying whether
they run strict. A flag that cannot be satisfied is a flag nobody will use — and it is the only
mechanism 04 offers for "never ship a degradation."
Ranked by what unblocks the most work and what costs most if skipped.
02's schema so it accepts the corpus it describes. (F2, C8, L5, L6, L7, G2, G3)02 is the centerpiece and it does not fit the data. Minimum:
DependencyKindSchema: add agent, plugin, config; reconcile with ComponentTypeSchema's
nine types (one vocabulary, one place). Allow wildcards and path-style refs in
DependencyInputSchema, or 01's crown-jewel wildcard expansion is unrepresentable.* rule) in §1.2.5. Adopt 03 §0.4's rule. The live
security gap depends on this and nobody owns it today.ContextSchema.description/name → optional or derived. As written, all 294 context
files fail validation — the MVI marker has no description field.function to split the {category}/{function} taxonomy 01 requires.profiles (object, with additionalPaths/badge/description), categories, and
aliases[] to the registry schema — 01 requires all three, 04 reads two of them, 02
deleted them.targets: [] to every content schema (L2). Without it there is no union skill set, no
home for tools, and no way to say "OpenCode-only."Nothing downstream is real until this is done. 05's Layer-1 test is the forcing function: write
it first and let it fail.
01 and 05 to v2, or stop citing them as authoritative. (F1, F6, C2, C3, C4, C6)The index marks both "✅ v1" as a status. They are stale, and they are the two documents an
implementer touches most — 01 is the preservation checklist, 05 is the build order.
05: purge 160 (four sites); rewrite Stage 3's "copy from .opencode/" into the merge the
index mandates; fix the Layer-1 model test and the Layer-0 warning count; delete "or
diff-explained" from the Stage-2 gate.01: retract the .env security alarm and close Q17; fix 79→76 / 36→33 navigation
counts; align §1.3's {pattern, effect} to {scope, decision}; fix 112→110.The index's finding #3 is correct and orphaned. Someone must decide, per content type and per conflict:
code-reviewer is 108 vs 269 lines with
different descriptions. The index's own worked example already silently picked the CC
description while 01 presents the OpenCode one as verbatim — an undocumented decision
already made. Make it explicit or it will be made again, differently, under deadline.targets: [] (fix #1).session-start.sh: which schema holds it? 02 §6's HookSchema is "load-bearing, not
theoretical" by its own admission but has never been tested against the real file's six
capabilities.<example> blocks / 6 CC-only commands: mechanical harvest, but nobody scheduled it.This is the highest-risk task in the project and currently the least specified. It is also the
gate on deleting sync-to-claude.sh, so it will be done in a hurry unless it is done first.
Sixty open questions is not a spec. Exactly three block code:
content/ in the user's project? (04 Q9 — decides whether the CLI ships the IR at all)model authorable? (02 Q3 / 01 Q3 / 05 — three docs, three answers; and see the
tier: proposal in L4, which nobody raised)* rule (03 Q2 → 02 — folded into fix #1)Four more are already answered and just not reconciled (03 Q1, 03 Q7, 02 Q1, 01 Q17, 01
Q2, 01 Q10) — delete them; 03 Q1 is labelled BLOCKING and will stop an implementer for
nothing. The rest get defaults today. Then re-verify last-match-wins against a real OpenCode
install (F3) — ten minutes to retire the largest piece of unearned confidence in the set.
aliases[] plan simultaneously — while being the same
three files as 01 §3.6's duplicate-id bug. Decide now: keep as symlinks (and specify Windows
aliases[] (and fix the three files that reference
the alias paths).05 sells that as the migration's elegance and
never asks what happens when a bad build ships to every CC user at once. Define the abort
criterion and the pin/rollback path before Stage 3 deletes the only working CC producer.oac build turns content into executable artifacts —
HookActionSchema.command is an unvalidated z.string() that runs at every SessionStart. The
project already ships escape_for_json() because it knows SKILL.md can be hostile. The refactor
widens that surface enormously and has no threat model.The parts of this spec set that are good are very good: 02 §4's MVI parser spec (the
line-232/line-301 trap analysis is exactly right and I reproduced its bucket counts exactly),
03 §2.3's fail-closed asymmetry argument, 03 §2.6's verified-negative on settings.json, and
01's registry/dependency forensics — where I checked ~20 claims and found two counting errors
and one stale retraction. The live security gap (index #10) is real, correctly diagnosed, and
shipping in production today; that finding alone justifies the exercise.
The failure mode here is not sloppiness. It is that five authors revised at different times against a moving index, and the index recorded its conclusions without telling the documents that supplied them. Fixes #1–#4 are all versions of the same repair: make the documents agree with each other and with the disk. Do that and this is buildable.
Gate closure: 2026-07-15, canonical-refactor subtask 10. Rule:
fixedmeans the governing specification now contains the required decision or correction.acceptedmeans the limitation is intentionally outside the first-class v1 scope and has an explicit guardrail. These dispositions override the original review text where they conflict, per12-DISPATCH.md.
| Finding | Disposition | Resolution evidence |
|---|---|---|
| F1 | fixed | 05-impact-migration-tests.md v2 replaces the false 160-agent scope with the disk-verified inventory and merge stages. |
| F2 | fixed | 02-canonical-schema.md v3 §0.2 unifies all dependency kinds and accepts path/wildcard refs; Stage 2's corpus gate covers the 19 former rejects. |
| F3 | fixed | 10-PRECEDENCE-EXPERIMENT.md confirms last-match-wins through live OpenCode 1.17.20 probes and resolver .findLast() evidence. |
| F4 | fixed | 00-INDEX.md v2.2 and 02-canonical-schema.md §4 use the corrected leading-window priority distribution. |
| F5 | fixed | 01-feature-inventory.md v2 uses exact-basename navigation counts and preserves the separate suffix-named files. |
| F6 | fixed | 01-feature-inventory.md v2 retracts the committed-secret claim; .env remains an untracked build-hygiene exclusion only. |
| F7 | fixed | 00-INDEX.md v2.2, 01 §4.5, 02 §2, 03 §0.1, and 09 §4 align on 12 CC skills plus 4 canonical OpenCode skills. |
| F8 | fixed | 00-INDEX.md v2.2 and 02's inference.tier preserve the five-sonnet/two-haiku semantic cost tiers without authoring model ids. |
| F9 | fixed | 01-feature-inventory.md v2 and 09-MERGE-RULES.md §§7,9 distinguish regular files, symlinks, and JSON; later work must recount after tree changes. |
| F10 | fixed | 01-feature-inventory.md v2 corrects the unregistered-context result and separates path aliases from canonical files. |
| C1 | fixed | 03-adapter-specs.md §0.5 and its question dispositions now cite 10-PRECEDENCE-EXPERIMENT.md; stale precedence blockers are removed. |
| C2 | fixed | 05-impact-migration-tests.md v2 defines a two-tree merge; 09-MERGE-RULES.md owns the per-content-type decisions. |
| C3 | fixed | 02-canonical-schema.md v3 makes raw model ids non-authorable and uses inference.tier; 05 tests the same contract. |
| C4 | fixed | 05-impact-migration-tests.md and 07-EXECUTION-PLAN.md assert exact CC warning counts: two for code-reviewer, four for coder-agent. |
| C5 | fixed | 08-STRUCTURE-AND-PACKAGING.md §§2.1–2.2 distinguishes bundled canonical content from each user's editable project content/. |
| C6 | fixed | 01-feature-inventory.md v2 aligns capability examples to {scope, decision}, matching 02. |
| C7 | fixed | The corrected skill census is aligned across 00, 01, 02, 03 §0.1, and 09 §4. |
| C8 | fixed | 02-canonical-schema.md §1.2.5 owns the implicit-default rule; 03 §0.4 consumes it without escalation. |
| C9 | accepted | 07-EXECUTION-PLAN.md limits first-class v1 targets to OpenCode and Claude; Cursor is experimental and Windsurf is cut, so unsupported hooks/delegation remain explicit blockers outside the v1 promise. |
| C10 | fixed | 03-adapter-specs.md corrects the archived citation to line 432; live behavior is now sourced from 10, not that citation. |
| L1 | fixed | 09-MERGE-RULES.md is the signed-off owner and conflict contract for every content type. |
| L2 | fixed | 02-canonical-schema.md v3 adds targets:[]; 09 uses it for the 16-skill union and target-specific hooks. |
| L3 | fixed | 09-MERGE-RULES.md §§3,6 tokenizes path-bearing bodies and permission scopes; 07 Stage 4 adds a no-raw-.opencode/ lint. |
| L4 | fixed | 02-canonical-schema.md v3 adds `inference.tier: fast |
| L5 | fixed | 02-canonical-schema.md v3 restores profile objects including additionalPaths, badges, descriptions, and components. |
| L6 | fixed | 02-canonical-schema.md v3 models registry categories and the promoted icon/order/status metadata. |
| L7 | fixed | 02-canonical-schema.md v3 models aliases; 09 §7 defines symlink and duplicate-id collapse into aliases[]. |
| L8 | fixed | 09-MERGE-RULES.md §§3,7 resolves delegate names through canonical ids or aliases, preserving mixed-case legacy scopes such as TestEngineer. |
| L9 | fixed | 02-canonical-schema.md's MVI serializer specifies SemVer normalization with compact X.Y re-emission; 05 requires byte-stable round trips. |
| L10 | fixed | 08-STRUCTURE-AND-PACKAGING.md §2 defines .oac.json as project discovery for editable content/; 04 owns CLI discovery/self-healing behavior. |
| G1 | fixed | 02 resolves dependency vocabulary/defaults, 09 resolves aliases/path scopes, and 07 Stage 3 makes security loss a blocker with explicit opt-in. |
| G2 | fixed | 02-canonical-schema.md v3 defines path-derived context identity, optional metadata, split taxonomy, leading-window MVI parsing, and stable re-emission. |
| G3 | fixed | 02 restores profiles/categories/aliases and plural registry sections; 04 §2.3 defines dependency and section-key normalization. |
| G4 | fixed | 08-STRUCTURE-AND-PACKAGING.md §4 replaces both brittle anchors with an explicit package-name check plus OAC_PACKAGE_ROOT. |
| G5 | fixed | 02 §1.2.5 distinguishes absent/default capabilities from explicit *: allow; 05 requires OpenCode parse→build idempotence. |
| G6 | fixed | 04-cli-build-distribution.md separates deterministic build/check output from update-time sha256 user-edit protection; 07 Stage 5 tests preservation and --yolo. |
| G7 | fixed | 05-impact-migration-tests.md v2 removes the diff-explained escape hatch; 07 Stage 3 requires reviewed, byte-stable goldens and explicit merge decisions. |
All 37 F/C/L/G findings are dispositioned above: 36 fixed, 1 accepted. U2 is closed by
11-ROLLBACK.md; U1 and U3 are closed by 09-MERGE-RULES.md; remaining U-items are carried as
explicit implementation/test constraints in 02, 05, and 07 rather than open Stage 1 gates.