Skip to content

feat(workspace): inject site-agent context into worktrees on creation#46

Merged
chubes4 merged 1 commit into
mainfrom
feat/inject-worktree-context
Apr 21, 2026
Merged

feat(workspace): inject site-agent context into worktrees on creation#46
chubes4 merged 1 commit into
mainfrom
feat/inject-worktree-context

Conversation

@chubes4
Copy link
Copy Markdown
Member

@chubes4 chubes4 commented Apr 21, 2026

Closes #45.

Summary

workspace worktree add now snapshots the originating site's agent memory (MEMORY.md, USER.md, RULES.md) into the new worktree so a fresh agent session starts with the architectural context its parent site has accumulated — not just the repo's auto-loaded files.

Two files are written per worktree, both receiving the same runtime-agnostic payload:

<worktree>/.claude/CLAUDE.local.md     ← Claude Code convention
<worktree>/.opencode/AGENTS.local.md   ← OpenCode convention

The injected paths are added to the repository's info/exclude (per-checkout ignore, not .gitignore), so the tracked repo is never dirtied and git status stays clean.

New CLI surface

# Default-on injection
wp datamachine-code workspace worktree add <repo> <branch>

# Bare worktree, no injection
wp datamachine-code workspace worktree add <repo> <branch> --skip-context-injection

# Rewrite injected files from current site memory state
wp datamachine-code workspace worktree refresh-context <handle>

New ability: datamachine/workspace-worktree-refresh-context. The workspace-worktree-add ability gains an inject_context input (default true).

Metadata

Per-worktree created_from_site metadata (site URL + agent slug + abspath + ISO timestamp) is persisted in the datamachine_worktree_metadata site option keyed by workspace handle, and dropped automatically when the worktree is removed (including path-based removal from cleanup).

Graceful degradation

When Data Machine's agent memory layer is unavailable (plugin inactive, running outside a site context), injection becomes a no-op: the worktree is still created, the response surfaces context_injected=false with a skip reason, and no error fires. This matches the spec requirement about invoking outside a site context.

Spec deviations / revisions

  1. .git/info/exclude semantics. The issue spec says "Appends both paths to <worktree>/.git/info/exclude" and describes the behavior as "Each worktree manages its own injected context independently." That's not how git exclude resolution works: info/exclude is ALWAYS read from the repository's common git dir ($GIT_COMMON_DIR/info/exclude), never from the per-worktree private gitdir. Writing to <primary>/.git/worktrees/<slug>/info/exclude is a silent no-op — git never reads it. This PR writes to the common info/exclude instead, and documents the implication: the exclude patterns are visible to every worktree + the primary checkout, but the patterns (.claude/CLAUDE.local.md, .opencode/AGENTS.local.md) only match files we deliberately create in injected worktrees, so there's no behavior change in non-injected checkouts. The "no repo pollution" property still holds (no tracked files touched). The "each worktree independent" property becomes "the exclude pattern list is shared but file presence is still per-worktree."

  2. CLI flag name. The spec proposes --no-inject-context. WP-CLI's synopsis validation rejects declared [--no-<flag>] flags (it normalizes to inject-context and can't find a match). Switched to --skip-context-injection to stay within WP-CLI conventions. The ability-level input is inject_context=false (unchanged from spec).

Smoke test results

Ran the 8 scenarios from the issue:

# Scenario Result
1 Fresh wiki post on source site as MEMORY marker
2 workspace worktree add creates the worktree
3 .claude/CLAUDE.local.md + .opencode/AGENTS.local.md exist with MEMORY content
4 .git/info/exclude (common dir) contains both paths
5 git status inside worktree shows neither injected file
6 Mutate MEMORY → refresh-context → injected files reflect new content
7 --skip-context-injection → neither file written, exclude unchanged
8 No-DM context → build_payload() returns null → worktree still created, clean skip

Files

  • New: inc/Workspace/WorktreeContextInjector.php — payload builder, renderer, injector, metadata store/forget, common-git-dir resolver.
  • Modified: inc/Workspace/Workspace.phpworktree_add() gains $inject_context; new worktree_refresh_context(); worktree_remove() and the path-based removal both call forget_metadata().
  • Modified: inc/Abilities/WorkspaceAbilities.phpworkspace-worktree-add input schema gains inject_context; new workspace-worktree-refresh-context ability.
  • Modified: inc/Cli/Commands/WorkspaceCommand.php--skip-context-injection flag; refresh-context subcommand; contextual output rendering for both.

Scope

Deliberately out: cross-machine federation, auto-refresh schedules, selective/differential injection, two-way sync. Matches the issue's "Out of scope" section.

Adds a default-on injection step to `workspace worktree add` that
snapshots the originating site's agent memory (MEMORY.md, USER.md,
RULES.md) into the new worktree so a fresh agent session cooking in
that worktree starts with the same architectural context its parent
site has accumulated.

Written files:
  <worktree>/.claude/CLAUDE.local.md    — Claude Code convention
  <worktree>/.opencode/AGENTS.local.md  — OpenCode convention

Both files receive the same runtime-agnostic payload. The injected
paths are added to the repository's `info/exclude` (NOT .gitignore,
so the tracked repo is never dirtied). git's exclude lookup always
uses the common git dir — per-worktree info/exclude is a no-op — so
we resolve commondir from the worktree's `.git` file and write there.
The patterns are narrow enough to be harmless across other worktrees
and the primary checkout, where no injected files ever exist.

New CLI:
  wp datamachine-code workspace worktree add <repo> <branch>
      [--skip-context-injection]
  wp datamachine-code workspace worktree refresh-context <handle>

New ability:
  datamachine/workspace-worktree-refresh-context

Worktree-add ability gains an `inject_context` input (default true);
set false to create a bare worktree. Metadata (`created_from_site`:
URL + agent slug + abspath + timestamp) is persisted in the
`datamachine_worktree_metadata` option keyed by workspace handle, and
dropped when the worktree is removed.

When DM's agent memory layer is unavailable (plugin inactive, running
outside a site context) injection becomes a graceful no-op: the
worktree is still created, the response surfaces
`context_injected=false` with a skip reason, and no error fires.

Closes #45
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.

Inject site-agent context into worktrees on creation

1 participant