Skip to content

refactor(materialize/renderers): introduce {SHARED_DIR} placeholder + move _shared/ for primary-agent installs - #71

Merged
davidarce merged 3 commits into
mainfrom
refactor/sdd-shared-dir-placeholder
May 10, 2026
Merged

refactor(materialize/renderers): introduce {SHARED_DIR} placeholder + move _shared/ for primary-agent installs#71
davidarce merged 3 commits into
mainfrom
refactor/sdd-shared-dir-placeholder

Conversation

@davidarce

Copy link
Copy Markdown
Owner

Summary

Pairs with the catalog refactor that replaces `{WORKFLOW_DIR}/_shared/` with `{SHARED_DIR}/` in all SDD templates.

Adds a new `{SHARED_DIR}` placeholder. Each renderer resolves it to the path where it actually installs the workflow's `_shared/` assets:

Agent Resolves to _shared/ install path
Claude / Factory / Codex `workflowDir/_shared/` `.{agent}/skills//_shared/` (no change — orchestrator IS a real skill on disk)
OpenCode `workspaceRoot//_shared/` `.opencode//_shared/` (NEW — was `.opencode/sdd-orchestrator/_shared/` fantasma)
Copilot `workspaceRoot//_shared/` `.github//_shared/` (NEW — was `.github/skills/sdd-orchestrator/_shared/` fantasma)

For OpenCode/Copilot the orchestrator is a primary agent (in `opencode.json` / a flat `.agent.md`), so the per-orchestrator skill directory was empty (fantasma container). Installing `_shared/` at a workflow-namespaced top-level path removes that fantasma directory and makes the layout reflect reality.

Changes

`helpers.go`

  • `buildWorkflowPlaceholderReplacements`: emit `{SHARED_DIR}` per agent — `workspaceRoot/wf.Name/_shared` for opencode/copilot, `workflowDir/_shared` for everything else.
  • `buildWorkflowPathReplacements` (used by codex/factory which take simpler path-only params): emit `{SHARED_DIR}` as `workflowDir/_shared` (the only sensible value when no agentName is available).

`opencode.go`

  • `_shared/` copy destination: was `workflowDir/_shared` → now `workspaceRoot/wf.Name/_shared`.
  • Added `resolvePlaceholders` pass on the new shared root (`.opencode//`).
  • Kept the legacy `workflowDir` placeholder pass as a no-op fallback for any catalog still dropping content there.

`copilot.go`

  • Same pattern: `_shared/` copy destination changed to `workspaceRoot/wf.Name/_shared`.
  • Added `resolvePlaceholders` pass for the new shared root.

Tests

  • `TestOpenCodeRenderer_InstallWorkflow_SkillsUnderSkillsDir`: now asserts `_shared/` at the new path AND that the legacy fantasma `sdd-orchestrator/_shared/` is NOT created.
  • `TestCopilotRenderer_InstallWorkflow_SkillsUnderSkillsDir`: same flip.
  • `TestOpenCodeRenderer_InstallWorkflow_SharedVariantSuffixStripping` / Copilot equivalent: paths updated.
  • `TestBuildWorkflowPathReplacements_OnlyPathKeys`: expects 3 keys ({SKILLS_PATH}, {WORKFLOW_DIR}, {SHARED_DIR}) instead of 2.
  • `TestBuildWorkflowPathReplacements_NoModelKeysEvenWithRoles`: allowed-keys set extended to include {SHARED_DIR}.
  • All tests pass (`go test ./...` clean).

Compatibility

  • Old catalog + new renderer: catalog still uses `{WORKFLOW_DIR}/_shared/` references. Renderer copies `_shared/` to the new path, so old-style references break. This is the same scenario the catalog PR addresses — merge them together.
  • New catalog + old renderer: catalog uses `{SHARED_DIR}` placeholder. Old renderer doesn't know it, leaves the placeholder unresolved → broken references. Same: merge together.
  • Both new: works correctly. References resolve to actual install paths.

Branched from

This PR is branched from `fix/sdd-opencode-copilot-slim-orchestrator` (PR #70) which is still open. Will need to be rebased onto main after PR #70 merges. The two PRs touch overlapping code in opencode.go / copilot.go but the changes are sequential (PR #70 removes REGISTRY consumption, this PR moves _shared/ install path) — rebase should be clean.

Pairing PR

Catalog: https://github.com/davidarce/devrune-starter-catalog/pull/new/refactor/sdd-shared-dir-placeholder

Recommended merge order: PR #70 → catalog #60 (already merged) → this DevRune PR → catalog `refactor/sdd-shared-dir-placeholder`.

Test plan

  • `go test ./...` passes (verified locally).
  • Merge both PRs (DevRune + catalog), rebuild devrune binary, `devrune sync` against an OpenCode workspace.
  • Verify `.opencode/sdd/_shared/` exists with all expected files (launch-templates.md, advisor-templates.md, envelope-contract.md, persistence-contract.md, recovery.md).
  • Verify `.opencode/sdd-orchestrator/` does NOT exist (fantasma directory eliminated).
  • Verify `opencode.json` orchestrator prompt resolves `{SHARED_DIR}` to `.opencode/sdd/_shared` everywhere.
  • Same for Copilot install: `.github/sdd/_shared/` exists, `.github/skills/sdd-orchestrator/` does NOT.
  • Claude install unchanged: `.claude/skills/sdd-orchestrator/_shared/` still works.

davidarce added 3 commits May 10, 2026 16:41
…lot installs

OpenCode and Copilot have a primary `sdd-orchestrator` agent the user
explicitly selects (a custom agent in opencode.json / a `.agent.md` in
.github/agents/). The orchestrator playbook lives in that agent's prompt
— rendered from ORCHESTRATOR.{opencode,copilot}.md alone.

Until now, the renderer was also:
- prepending REGISTRY.{opencode,copilot}.md to the orchestrator prompt
- capturing the same registry content into r.registryContents for later
  injection into AGENTS.md / copilot-instructions.md

Both paths leaked workflow-specific rules into non-workflow sessions
(ambient catalog) and duplicated / contradicted the orchestrator agent's
own playbook (prompt prepend). They were a Claude/Factory pattern
applied where it does not fit.

This change removes both consumers for OpenCode and Copilot:
- opencode.go: skip captureRegistryContent + pass registryPath="" to
  synthesizeAgents.
- copilot.go: skip captureRegistryContent + pass registryPath="" to
  installOrchestratorAgent.

buildOrchestratorEntry / installOrchestratorAgent still accept a
registryPath argument for backward compatibility — non-empty paths are
still read and prepended — but the canonical caller now passes "".

Tests updated to assert the new contract (RegistryContents must NOT
contain the workflow name for OpenCode/Copilot installs). Renames the
two test functions accordingly.

Pairs with devrune-starter-catalog change that deletes REGISTRY.opencode.md
/ REGISTRY.copilot.md (now dead code from the catalog's perspective) and
slims ORCHESTRATOR.{opencode,copilot}.md to remove duplication with the
deleted REGISTRY blocks.
… move _shared/ for primary-agent installs

Adds a {SHARED_DIR} placeholder that the catalog uses to reference
workflow-shared assets (launch-templates, advisor-templates, envelope-
contract, persistence-contract, recovery). Each renderer resolves it to
its idiomatic path so the same template body works for all agents.

Path resolution per agent:
- claude/factory/codex: workflowDir/_shared/ (legacy — _shared/ stays
  inside the orchestrator skill dir, which is a real skill on disk).
- opencode/copilot:    workspaceRoot/<workflow-name>/_shared/ — a
  workflow-namespaced top-level directory. The legacy
  .opencode/sdd-orchestrator/ and .github/skills/sdd-orchestrator/
  directories were fantasma containers (no SKILL.md, the orchestrator
  is a primary agent in opencode.json / a flat .agent.md). Installing
  _shared/ at the top level removes that fantasma directory entirely.

Code changes:
- helpers.go: add {SHARED_DIR} to both buildWorkflowPlaceholderReplacements
  (used by opencode/copilot/claude/factory) and buildWorkflowPathReplacements
  (used by codex/factory). Each picks the right resolution based on
  agentName and workflow.Name.
- opencode.go: change _shared/ copy destination to
  workspaceRoot/<workflow-name>/_shared/. Add resolvePlaceholders pass
  for the new shared root. Keep legacy workflowDir resolve as a no-op
  fallback for backward compat.
- copilot.go: same pattern, destination .github/<workflow-name>/_shared/.

Tests:
- TestOpenCodeRenderer_InstallWorkflow_SkillsUnderSkillsDir / Copilot
  equivalent: assert _shared/ at the new path AND assert legacy fantasma
  paths are NOT created.
- TestOpenCodeRenderer_InstallWorkflow_SharedVariantSuffixStripping /
  Copilot equivalent: same path update.
- TestBuildWorkflowPathReplacements_OnlyPathKeys: now expects 3 keys
  ({SKILLS_PATH}, {WORKFLOW_DIR}, {SHARED_DIR}) instead of 2.
- TestBuildWorkflowPathReplacements_NoModelKeysEvenWithRoles: allowed key
  set extended to include {SHARED_DIR}.

Pairs with devrune-starter-catalog refactor that updates all templates
to use {SHARED_DIR} instead of {WORKFLOW_DIR}/_shared/.
@davidarce
davidarce merged commit 718257b into main May 10, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant