Synergy Tracker
Manage the SYNERGY-*.md files that track shared patterns, divergences,
extraction candidates, and capabilities observed in sibling projects while
building this one. This skill tracks patterns and capabilities across
peer/sibling projects under the same maintainer — not upstream dependency
friction (use /upstream-tracker for that).
Tracking Files
Sibling projects (permanent files)
Sibling projects are declared in .claude/synergy-registry.json (if it exists)
as an array of {name, file, remote, bm-entity, relationship} objects. See
references/synergy-entry-format.md for the full registry schema.
To discover which projects are registered, read .claude/synergy-registry.json
if it exists. If it does not exist, glob for SYNERGY-*.md to infer sibling
names from existing files. If neither is present, ask the user which sibling
project is involved before proceeding.
File naming
Files are named SYNERGY-<project-name>.md. The project name is taken from
the name field in the registry (tier 3 of the canonical derivation
algorithm — synergy-tracker's subject is always sibling). For projects
outside the registry, fall back to tier 4 (directory basename), then
normalize. Full algorithm:
references/project-name-derivation.md. Examples: vp-knowledge →
SYNERGY-vp-knowledge.md, @scope/shared-utils →
SYNERGY-scope--shared-utils.md.
See references/synergy-entry-format.md for the full naming convention
(normalization rules) and references/project-name-derivation.md for the
tiered derivation algorithm shared with /sibling-sync.
Lifecycle
SYNERGY files are permanent — they always exist for registered sibling
projects, even when all sections are empty. When an entry is resolved or
dismissed, remove it and restore the _No entries yet._ placeholder for that
section. Do not delete the file.
Private overlay (PRIVATE-SYNERGY-*.md)
For entries that must stay out of a public repo (a proprietary sibling's
internal paths, client names, unreleased plans), use a gitignored
PRIVATE-SYNERGY-<project-name>.md companion to the committed
SYNERGY-<project-name>.md. It holds additional private entries under the same
four section headings.
The PRIVATE- prefix is the privacy mechanism. It keeps the overlay
OUTSIDE the SYNERGY-*.md glob namespace, so every public consumer (this
skill's boundary-crossing workflows, /sibling-sync, /retrospective, the
sprint-review agent, session-start.sh) structurally cannot read it — no
per-consumer exclusion is needed or used. Only the deliberate local-only read
in workflow 2 (Review) additionally globs PRIVATE-SYNERGY-*.md to assemble the
combined private view.
Invariant: private-overlay entries are NEVER promoted to Basic Memory
(workflow 5 (Promote to Basic Memory)) and NEVER reciprocated/written to a
sibling. With the prefix scheme this is enforced structurally — the promote
and reciprocate paths glob SYNERGY-*.md, which cannot match
PRIVATE-SYNERGY-*.md. This keeps the public view a sibling sees stable (so
bilateral convergence still works) and prevents a proprietary↔public leak.
When a PRIVATE-SYNERGY-*.md overlay exists, the committed
SYNERGY-<project-name>.md carries a one-line pointer noting it (gitignored
files are invisible to collaborators and git grep). Gitignored via the
explicit PRIVATE-SYNERGY-*.md line; session-start.sh warns if any is
tracked. Full convention: CLAUDE.md ### Synergy tracking convention.
Same-title shadowing (workflow 2 (Review) only). When a private entry shares
a title with a committed entry, surface both in the review view — the
committed row (still promotable/reciprocable) labelled [committed, shadowed]
and the private row labelled [local] — so the reviewer never loses sight that
a public twin exists. This local-view assembly never participates in
/sibling-sync's title-keyed bilateral matching, which reads committed entries
only.
Structure
All SYNERGY files use the same four-section structure:
## Shared Patterns
_No entries yet._
## Divergences
_No entries yet._
## Extraction Candidates
_No entries yet._
## They Have / We Don't
_No entries yet._
Section semantics:
Shared Patterns — practices, conventions, or implementations that exist
in both projects and should stay aligned
Divergences — places where the two projects intentionally or accidentally
differ in approach to the same problem
Extraction Candidates — code, patterns, or logic in this project that is
a strong candidate for extraction into a shared package or library
They Have / We Don't — capabilities in the sibling project that this
project lacks and may want to adopt. Apply the domain-fit test before
logging:
Pass test: "this project has the underlying need but lacks the implementation."
Fail test: "the sibling has a capability in a different domain than this project's."
Without this test, every comparison run produces noise the user must
dismiss. Worked failures from Sprint 19: vp-beads's swarm-wave (sprint
orchestration is a different domain from vp-claude's research fan-outs)
and vp-beads's vendor-sync (vp-beads vendors content; vp-claude has no
vendored surface to sync) both passed the surface filter but failed the
domain-fit test.
Workflows
Determine which workflow the user needs based on their request. If ambiguous,
default to workflow 1 (log) for "synergy" or "log this" requests, workflow 2
(review) for "review synergies" or "what's open" requests, and workflow 3
(compare) for "compare with" requests.
1. Log a synergy entry
When the user observes a shared pattern, a divergence, an extraction candidate,
or a capability gap between this project and a sibling — add it to the correct
file. Infer the details from the current conversation context: what code was
being discussed, what pattern was noticed, what contrast was made.
Steps:
- Identify which sibling project is involved from the conversation context.
If the user named a sibling project in their request (e.g., "compare with
vp-knowledge", "log this for vp-knowledge"), use that name directly — do
not ask again. Otherwise, check
.claude/synergy-registry.json first. If
the registry does not identify the sibling, glob for SYNERGY-*.md as a
fallback. If neither resolves it, ask the user.
Step 1b — Guided registry creation (only when
.claude/synergy-registry.json is absent AND a sibling has been named in
step 1). If the registry file already exists (returning user adding a
second sibling, or any case where a registry was hand-written), skip this
step silently and proceed to step 2 — do not modify hand-written
registries. Append-to-existing is not supported; that case falls back
to manual editing of .claude/synergy-registry.json (add a new
{name, file, remote, bm-entity, relationship} entry per the schema in
references/synergy-entry-format.md). Tracked separately as a future
enhancement (vp-beads-bma). Otherwise, run the following bootstrap so
the project can route future syncs and comparisons through the registry
rather than re-asking each time.
Confirm the sibling name resolved from step 1. If step 1 deferred to
asking the user, defer this step too — only proceed once a name is in
hand.
Derive both project names before proceeding.
<sibling> — the name confirmed in step 1. In practice this is already
the tier-3 registry name field (or, for unregistered siblings, the
directory basename: tier 4). No additional derivation is needed; just
carry the value forward.
<this-project> — this project's own canonical name, derived using
the four-tier algorithm in
skills/synergy-tracker/references/project-name-derivation.md (self
subject: tiers 1-4, starting with the sibling's back-pointer). In the
common case — vp-beads with a registered sibling — tier 1 or tier 2
resolves immediately. Use the normalized result as <this-project>
everywhere below: in the bm-entity value and in any reciprocal file
references.
Auto-derive the four registry fields that have unambiguous defaults:
name — already known from step 1.
file — mechanical: SYNERGY-<sibling>.md.
remote — probe the sibling's git origin if the sibling repo is
accessible on disk at ../<sibling>/:
git -C ../<sibling> remote get-url origin 2>/dev/null | sed 's/\.git$//'
If the command fails or the path is not accessible, leave remote as
an empty string in the preview.
bm-entity — apply the canonical convention
engineering/agents/vp-plugins-<this-project>-and-<sibling> (see
references/synergy-entry-format.md).
Prompt only the residuals. At most three AskUserQuestion calls
(Anthropic SDK caps the header field at 12 characters):
- First, a
header: "Visibility" (10 chars) call — "Public (committed
registry) or Private (gitignored — the sibling's name is never committed)?"
Default Public. Choose Private for a proprietary partner whose very
existence must stay out of a public repo. When the user picks Private,
switch to the private-sibling path:
- Write the entry to
.claude/synergy-registry.local.json only (never the
committed base), with file set to PRIVATE-SYNERGY-<sibling>.md.
- Omit
bm-entity entirely — a private name in a Basic Memory path would
leak it (the validator warns if present).
- Create the gitignored content file
PRIVATE-SYNERGY-<sibling>.md from the
four-section template (it is the sibling's sole synergy file — there is no
committed SYNERGY-<sibling>.md).
- Verify both
.claude/synergy-registry.local.json and the
PRIVATE-SYNERGY-*.md wildcard are gitignored (git check-ignore -q).
Never add a per-name PRIVATE-SYNERGY-<sibling>.md line to .gitignore
— that line would itself commit the name; the PRIVATE-SYNERGY-*.md
wildcard already covers it. If the wildcard is missing, warn the user to
add the wildcard line (not a per-name one).
- The preview shows ONLY the
.local.json block (no committed-registry
block, no bm-entity). Skip the Relationship-into-base step; relationship
still goes into the .local.json entry.
- One call with
header: "Relationship" (12 chars). The validator's
KNOWN_RELATIONSHIPS set caps options at six; AskUserQuestion caps at
4 visible options + auto "Other". Surface the four most common —
sibling-plugin (default), shared-tooling, fork, consumer — and
let the auto "Other" route to a free-text fallback that the workflow
then validates against the remaining two (coordinated-release,
dependency). If the user types anything else, warn that the value will
trigger a validate-plugin.mjs warning and confirm before writing. See
references/synergy-entry-format.md "Relationship vocabulary" for the
canonical set.
- Only when
../<sibling>/ does not resolve to an accessible directory, a
second call with header: "Local path" (10 chars) — free-text or
skip. If the user provides a path, it goes into
.claude/synergy-registry.local.json, never into the base registry.
Preview both files in a single message before writing anything. Use
this shape (omit the .local.json block when no local-path was
supplied). Show BOTH the placeholder schema and a worked substitution so
the user can see how <this-project> and <sibling> resolve. Annotate
auto-derived fields with their source as inline comments after rendering
(e.g. "remote": "https://github.com/voxpelli/vp-claude" # from <sibling> git origin, "bm-entity": "engineering/agents/..." # canonical convention) so the user can spot derivation errors before approving:
Proposed .claude/synergy-registry.json (schema):
[
{
"name": "<sibling>",
"file": "SYNERGY-<sibling>.md",
"remote": "<derived-or-blank>",
"bm-entity": "engineering/agents/vp-plugins-<this-project>-and-<sibling>",
"relationship": "<chosen>"
}
]
Worked example (this-project = vp-beads, sibling = vp-knowledge):
[
{
"name": "vp-knowledge",
"file": "SYNERGY-vp-knowledge.md",
"remote": "https://github.com/voxpelli/vp-claude",
"bm-entity": "engineering/agents/vp-plugins-vp-beads-and-vp-knowledge",
"relationship": "sibling-plugin"
}
]
Proposed .claude/synergy-registry.local.json (only if local-path given):
[
{ "name": "<sibling>", "local-path": "<path>" }
]
Confirm? [yes / edit / skip]
Handle the user's response. On yes, proceed to write. On edit,
re-prompt each derived field individually so the user can correct
name, remote, bm-entity, relationship, or the optional
local-path, then re-render the preview. On skip, continue the
workflow without writing the registry — resume to step 2.
Write the files using the Write tool. Always write
.claude/synergy-registry.json, and additionally write
.claude/synergy-registry.local.json only when a local-path was
provided. The .local.json is always a separate file — never embed
local-path in the committed base registry.
Verify round-trip. Use the Read tool to re-read each written file,
then validate the JSON via
node -e 'JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"))' <path>
(any non-zero exit signals invalid JSON). Confirm: base registry contains
entries with name and file set; if .local.json was written, it
contains name and local-path. On base-registry parse failure or
missing required fields, abort step 1b — report the problem and offer to
re-run; do not proceed to step 2 with a broken base registry. On
.local.json parse failure only, warn and continue without the local
override (the base registry is still usable).
Check that .local.json is gitignored when one was written:
git check-ignore -q .claude/synergy-registry.local.json
Exit status semantics: 0 = file is gitignored (no action); 1 = file
is not gitignored — warn the user with the exact line to add: "Add
.claude/*.local.json to your .gitignore (covers both
synergy-registry.local.json and vendor-registry.local.json, and is
forward-compatible with future .local.json registries)." Do not
auto-edit .gitignore — it is user-owned. 128 = the check itself
failed (not a git repo, or another git error) — report the underlying
error and skip the gitignore warning rather than emitting a
false-positive.
Resume to step 2 (Basic Memory pre-check).
- Basic Memory pre-check. If Basic Memory MCP tools are available, make
two
mcp__basic-memory__search_notes calls: one with the sibling project
name, one with 2-4 keywords describing the topic being logged (e.g.,
"PreCompact prompt command hook" or "edit_note append gotcha"). If either
search returns a matching note with synergy-related or engineering-pattern
content, surface it to the user: "This pattern is already tracked in Basic
Memory: [summary]. Logging it locally as well so this project tracks it."
If Basic Memory tools are not available, skip this step silently.
- If no
SYNERGY-<project>.md file exists yet, create it from the template
in references/synergy-entry-format.md (four sections with placeholders).
- Classify the entry into one of the four sections:
- Shared Pattern — the same approach exists in both projects
- Divergence — the projects handle this differently
- Extraction Candidate — this project has something worth extracting
- They Have / We Don't — the sibling has something we lack
- Read the target file. If the entry is proprietary (names a private
sibling's internal paths, client names, or unreleased plans that must not
reach a public repo), target the gitignored
PRIVATE-SYNERGY-<project>.md
overlay instead of the committed SYNERGY-<project>.md (see
### Private overlay). Before the first write to a new overlay, verify it is
ignored: git check-ignore -q PRIVATE-SYNERGY-<project>.md (exit 0 = ignored,
safe; exit 1 = NOT ignored — stop and have the user add PRIVATE-SYNERGY-*.md
to .gitignore first). Then, only for a public sibling that has a committed
SYNERGY-<project>.md, add a one-line pointer to that committed file
(e.g. "A private PRIVATE-SYNERGY-<project>.md overlay exists for this
sibling.") so collaborators know it may exist. For a fully-private sibling
(registered only in .local.json with file: PRIVATE-SYNERGY-<name>.md),
there is no committed SYNERGY-<name>.md and you write no pointer — a
pointer would commit the private name. Otherwise (a public sibling, ordinary
entry) target the committed SYNERGY-<project>.md.
- Compose the entry from this project's perspective using the entry format from
references/synergy-entry-format.md. Focus on impact and adoption cost, not
implementation internals.
- Add the entry under the correct section heading, using today's date. When
adding the first entry to a section, replace the
_No entries yet._
placeholder. Keep entries concise — 1-3 sentences. The title should be
scannable. Entries written to a PRIVATE-SYNERGY-*.md overlay are never
promoted to Basic Memory or reciprocated to a sibling (privacy invariant —
structurally enforced by the prefix).
Structured fields (all optional — omit fields that add no signal):
| Field |
Values |
Section |
Status: |
aligned · drifting |
Shared Patterns |
Last verified: |
YYYY-MM-DD (use today's date for new entries) |
Shared Patterns |
Convergence path: |
accept-difference · adopt-theirs · propose-shared |
Divergences |
Readiness: |
ready · needs-cleanup · proof-of-concept |
Extraction Candidates |
Priority: |
adopt-soon · consider · deferred |
They Have / We Don't |
Effort: |
trivial · moderate · significant |
Extraction Candidates, They Have / We Don't |
See references/synergy-entry-format.md for full entry format templates and
field value definitions.
Bilateral reciprocation mandate. When the sibling has already written
entries from their side (resolve the sibling path via the registry-with-override
pattern from workflow 3 (Compare with sibling) — local-path from the merged
registry, falling back to ../<sibling>/; check
<resolved-path>/SYNERGY-<this-project>.md if the sibling repo is accessible
on disk), reciprocate by re-verifying each
entry from this project's angle, recording your verification dates, and
noting any drift you observe. Do not skip duplicates — the reciprocation IS
the verification step. As captured in BM
engineering/agents/vp-plugins-vp-beads-and-vp-knowledge:
SYNERGY entries describe two parallel implementations that happen to be
aligned, not one shared thing — both sides need their own record so each
can verify from their POV at their own cadence. "Reciprocation IS the
verification step."
Sprint 19 evidence: vp-claude reciprocated 9 shared-pattern entries from
vp-beads's SYNERGY-vp-knowledge.md to a new SYNERGY-vp-beads.md, and the
re-verification surfaced 3 actively drifting artifacts
(validate-plugin.mjs 358 vs 333 lines; scripts/check-hooks.mjs 366 vs
284 lines; npm-run-all2 since-converged) plus 1 stale aligned row
(PreCompact retired post-v0.28.0 on the vp-claude side). When logging an
entry with no reciprocal yet on the sibling, prompt the user to file the
reciprocal entry on the sibling project (typically a follow-up task in the
sibling repo's bd backlog).
Eager promotion check. If Basic Memory MCP tools are available, assess
the project's tempo:
git rev-list --count --since="90 days ago" HEAD 2>/dev/null
Guard: skip if the repo has zero commits total
(git log --oneline -1 2>/dev/null returns empty). Also skip if this is
the first entry in any SYNERGY file for this project (the user is still
learning the workflow — promotion is premature).
| Tempo |
Commits in 90 days |
Promotion behavior |
| Dormant |
0–4 |
Offer inline promotion for any promotable entry |
| Moderate |
5–14 |
Offer only for Extraction Candidates with Readiness: ready |
| Active |
15+ |
Skip — the normal sprint cadence handles promotion |
When offering inline promotion, say: "This project has low commit
frequency — SYNERGY entries can sit unread for months. This entry looks
promotable to Basic Memory. Want to promote it now?"
If the user agrees, defer to workflow 5 (Promote to Basic Memory) for the
actual write — invoke its single-entry path scoped to this entry (steps 3-4
of workflow 5 (Promote to Basic Memory)). Step 8 only offers promotion;
workflow 5 (Promote to Basic Memory) performs it. This split keeps
## Cross-Project Synergy writes within workflow 5 (Promote to Basic Memory)'s
sole-owner boundary.
If the user declines, or if Basic Memory tools are not available, or if the
project is active, skip silently.
2. Review open synergies
Summarize the current state of all synergy tracking files.
Steps:
Glob for all SYNERGY-*.md files and read them. This is the one local-only
review, so ALSO glob PRIVATE-SYNERGY-*.md private overlays and assemble
the combined view per ### Private overlay: merge public + private entries
per section and label every private (PRIVATE-SYNERGY-sourced) row
[local]. When a [local] entry shares a title with a committed entry,
show both (committed row [committed, shadowed], private row [local])
so the promotable public twin stays visible. A [local] row is review-only:
never offer it as a reciprocation or Basic Memory promotion candidate (those
paths glob SYNERGY-*.md, which structurally cannot reach
PRIVATE-SYNERGY-*.md).
Present a summary grouped by file, showing counts per section and listing
each open entry with title and date.
Flag stale entries (older than 3 months with no activity). A Trend Review
entry resets the staleness clock for the entire file.
Highlight actionable items:
- Extraction Candidates with
Readiness: ready — extractable now
- Divergences with
Convergence path: of adopt-theirs or propose-shared
- They Have / We Don't with
Priority: adopt-soon
Inverse-file glob staleness detection (optional). For each sibling
represented by a SYNERGY-<sibling>.md file on this side, attempt to
read the inverse file — the sibling's SYNERGY-<this-project>.md —
to surface cross-side drift that single-side review cannot detect.
Reuse the registry-with-override path-resolution pattern from workflow 3
(Compare with sibling): prefer the local-path field on the merged
.claude/synergy-registry.json + .claude/synergy-registry.local.json
entry (relative paths resolve from the current project root); if absent,
fall back to ../<sibling>/SYNERGY-<this-project>.md. <this-project>
is this project's canonical name derived per
references/project-name-derivation.md (tiers 1–4, sibling
back-pointer first; in the common case the registered sibling's own
registry resolves it immediately).
Bilateral first. When the sibling repo is accessible AND the user wants
more than a single-side enrichment — full reciprocation gaps, status drift
in both directions, auto-reciprocation — defer to /sibling-sync
workflow 2 (Sync sibling SYNERGY) instead. Step 5 here only surfaces
inverse-file findings as a side-channel of the single-side review;
/sibling-sync is the authoritative bilateral tool.
Degradation. If the registry is missing, the sibling path does not
resolve, the inverse file is not present, or any read fails for any
reason, skip this step silently and continue with the regular
single-side review. Never hard-fail. The inverse-file step is a
best-effort enrichment, not a gate.
When the inverse file IS accessible, surface two classes of drift:
- Stale
aligned rows (staleness threshold: workflow 4
(Trend review (quarterly)) canonical — Last verified: more than two
trend-review cycles, ≈8 sprints). Entries marked Status: aligned on
this side whose corresponding entry on the sibling side shows measurable
drift — either the sibling lists the entry under ## Divergences
(contradicting our aligned), or the sibling's entry carries
Status: drifting, or the sibling has annotated the entry as
resolved/retired. The motivating example: vp-knowledge retired
PreCompact in v0.28.0 while this project's
SYNERGY-vp-knowledge.md still tagged PreCompact aligned 2026-04-05
— no automated detection caught the staleness until manual
reciprocation. Flag each such row with the sibling's contradicting
state so the user can reconcile.
- Missing-this-side rows. Features the sibling tracks under
## Shared Patterns (or any section) that no longer exist in this
project — typically because this project retired the feature without
updating the sibling-tracked entry. Surface them so the user can
either re-add the feature, mark the entry as resolved on both sides,
or escalate to /sibling-sync for bilateral reconciliation.
For deeper bilateral reconciliation (reciprocation gaps, status drift
in both directions, auto-reciprocation), defer to /sibling-sync
workflow 2 (Sync sibling SYNERGY) — workflow 2 (Review) here only
surfaces inverse-file findings as part of the single-side review
summary.
Output format:
## Synergy Status
### <project-name>
- Shared Patterns: N (N drifting)
- Divergences: N (N with active convergence path)
- Extraction Candidates: N (N ready)
- They Have / We Don't: N (N adopt-soon)
- [list each entry with title and date]
### Notes
- [stale entries]
- [actionable items]
- [inverse-file findings, if any: stale aligned rows, missing-this-side rows]
If all files are empty or no SYNERGY files exist, say so and suggest whether a
comparison run (workflow 3 (Compare with sibling)) would be useful — note that it
works best when the sibling repo is accessible on disk at the registry-resolved
path (local-path from the merged .claude/synergy-registry.json +
.claude/synergy-registry.local.json, falling back to ../<project-name>).
Source. The inverse-file glob step is sourced from
../vp-claude/UPSTREAM-vp-beads.md entry 3 (2026-05-04).
3. Compare with sibling
Perform a direct comparison between this project and a named sibling to surface
unlogged synergy observations.
Steps:
Identify the sibling from the user's request or the argument-hint — if
the user named a project, use that name directly without re-asking. Load
the registry with override merge:
- Read
.claude/synergy-registry.json for the sibling's remote and any
metadata.
- If
.claude/synergy-registry.local.json exists, read it and merge it on
top of the base registry. Match entries by the name key (the
human-stable identifier across machines and BM entity paths); for each
matched entry, fields present in .local.json win. A .local.json-only
entry (no matching base name) is added as a private sibling when its
file is PRIVATE-SYNERGY-<name>.md, and otherwise ignored (the base
registry is the authoritative source of which public siblings exist).
See references/synergy-entry-format.md "Private sibling entries".
If no project is identified from the argument, merged registry, or existing
SYNERGY files, ask the user which sibling project to compare with.
If no registry exists at all, also offer to create one via workflow 1 (Log a synergy entry) step 1b before proceeding with the comparison.
Gather sibling context. Resolve the sibling's local path: prefer the
local-path field on the merged registry entry (relative paths are resolved
from the current project root); if absent, fall back to ../<project-name>
relative to the current project root. If the resolved path is not
accessible, ask the user for the path (and suggest they record it in
.claude/synergy-registry.local.json to avoid re-prompting). Read the
sibling's key files if accessible:
package.json — dependencies, scripts, entry points
CLAUDE.md — conventions, architecture, workflow documentation
- Skill files (
Glob for skills/**/SKILL.md) — what skills exist, their
workflows, trigger phrases
- Hook definitions (
hooks/hooks.json) — event handling patterns
- Agent files (
Glob for agents/*.md) — what agents exist
If neither local files, conversation context, nor Basic Memory provides
substantive information about the sibling, stop and tell the user that a
meaningful comparison requires access to the sibling repo or prior
knowledge. Do not generate speculative entries.
Diff patterns. Compare against this project's equivalent files.
Identify observations in each of the four categories:
- Shared Patterns: conventions both projects use (same frontmatter fields,
same BM integration approach, same commit style)
- Divergences: structural or stylistic differences (different hook handling,
different reference doc organization, different BM section ownership)
- Extraction Candidates: logic in this project that would apply to the
sibling (validation scripts, shared reference formats, utility functions)
- They Have / We Don't: features or patterns in the sibling absent here
(agents, skills, hooks, conventions this project lacks)
Propose new entries. Present each observation as a candidate entry with
draft text matching the format in references/synergy-entry-format.md. For
each, ask: "Log this as a [category] entry for [sibling]?" The user
approves, edits, or skips each candidate. No mutations without approval.
Log confirmed entries by classifying, reading the file, composing, and
adding per workflow 1 (Log) steps 4–7. Skip step 8 (eager promotion) for batch
entries to avoid prompt fatigue. Offer a single summary at the end:
"N entries logged. Run workflow 2 (Review) to review the full picture."
If the user skips all candidates, report that no entries were logged and
suggest whether a follow-up comparison with different focus areas would be
useful.
5. Promote to Basic Memory
Promote generalizable cross-project synergy entries from project-local
SYNERGY-*.md files into sibling project entity notes in Basic Memory. This
creates or extends a ## Cross-Project Synergy section in the target sibling
note (typically at engineering/agents/vp-plugins-<this-project>-and-<sibling>).
Use only MCP tools from this skill's allowed-tools —
mcp__basic-memory__search_notes, mcp__basic-memory__read_note,
mcp__basic-memory__edit_note. If Basic Memory MCP tools are not available,
report that promotion is unavailable and suggest checking Basic Memory manually.
Steps:
- Scan for candidates. Glob all
SYNERGY-*.md files and read them. (This
glob structurally excludes every PRIVATE-SYNERGY-*.md — both private
overlays of public siblings and the sole content file of a private
sibling registered via .local.json. Neither is ever promoted; their names
must never reach Basic Memory. See ### Private overlay and
references/synergy-entry-format.md "Private sibling entries".) Filter
eligible entries by section + structured fields:
- Extraction Candidates with
Readiness: ready (always)
- Shared Patterns with
Status: aligned (always)
- Shared Patterns with
Status: drifting (flag the drift in the draft)
- Extraction Candidates with
Readiness: needs-cleanup or
proof-of-concept (lower priority — surface but mark as such)
- Divergences with
Convergence path: adopt-theirs or propose-shared
(skip accept-difference — by definition not promotion-worthy)
- They Have / We Don't with
Priority: adopt-soon (skip deferred)
- Skip any entry already annotated with
_(Promoted YYYY-MM-DD)_ —
that annotation is the dedup signal written in step 4 below.
- Present candidates to the user. Per-entry, never auto-promote. For
each candidate, show:
- Sibling project name and target BM note path
- Section (Shared Pattern / Divergence / Extraction Candidate / They
Have / We Don't) and entry title
- A draft generalized version. Apply the transforms documented in
references/synergy-bm-format.md: strip dates from titles,
project-specific file paths, sprint numbers, and bd issue IDs;
rewrite the prose from a neutral symmetric POV (so any sibling can
read the entry as authoritative); keep Status:, Convergence path:,
Readiness:, Priority:, and Effort: fields verbatim because they
carry cross-project meaning.
- Whether a Basic Memory note already exists for this sibling.
Let the user approve, edit, or skip each candidate.
- Route by target. For each approved candidate, look up the sibling's
bm-entity value from .claude/synergy-registry.json (with
.claude/synergy-registry.local.json merged on top by the name key).
If bm-entity is present, use it as the BM note path. If absent, call
mcp__basic-memory__search_notes with the sibling project name and
surface the candidate matches to the user. Legacy bm-entity form
warning: if the registered bm-entity does NOT start with
engineering/agents/vp-plugins- (the canonical convention from v0.12.1
onward), warn the user before proceeding: "Registered bm-entity
<value> does not match the canonical
engineering/agents/vp-plugins-<this-project>-and-<sibling> form. This
may be a pre-v0.12.1 registry pointing at a single-project entity note
(e.g. npm/<sibling>) — workflow 5 (Promote to Basic Memory) will
write to that legacy location, which may scatter cross-project content.
Recommended: update the registry to the canonical form and migrate any
existing ## Cross-Project Synergy content to
engineering/agents/vp-plugins-<this-project>-and-<sibling> first." Ask
the user whether to proceed anyway, abort, or migrate. Stale
bm-entity fallback:
if bm-entity is present but step 4's read_note returns not-found
(the registry path has been renamed or deleted), warn the user
("BM note not found at <bm-entity> — registry may be stale") and
fall through to mcp__basic-memory__search_notes exactly as the
absent-bm-entity row does. The full routing table — including the
fallback search order (engineering/agents/ for relationship notes,
then projects/ and npm/ as last-resort fallbacks for unregistered
siblings) — lives in references/synergy-bm-format.md.
- Write or flag. Three branches per approved candidate:
- Note exists, has
## Cross-Project Synergy with target subsection —
call mcp__basic-memory__read_note first to fetch exact content, then
mcp__basic-memory__edit_note with find_replace anchored to the next
### heading for uniqueness, expected_replacements=1. Deduplicate by
entry title (case-insensitive, whitespace-trimmed) before appending — if
the title already appears in the subsection, skip. If find_replace
returns zero replacements despite expected_replacements=1, the note was
edited between read_note and edit_note — do NOT annotate the local
entry; defer this candidate (increment a deferred-count) and continue
with the next candidate. The step 6 report includes deferred entries
under "deferred (BM note changed mid-write): N entries — re-run
workflow 5 (Promote to Basic Memory) once BM writes settle." Do NOT
re-invoke workflow 5 (Promote to Basic Memory) automatically inside
the same run; persistent contention would otherwise loop.
- Note exists, no
## Cross-Project Synergy section — call
mcp__basic-memory__edit_note with insert_before_section on
Relations to add the full section block (all five subsections per the
template in references/synergy-bm-format.md).
- No note exists — do NOT create a thin note. Flag for enrichment:
"No Basic Memory note for
<sibling>. Enrich it first (manual creation
under engineering/agents/), then re-run workflow 5 (Promote to Basic
Memory)."
- After successful write, annotate the local SYNERGY entry with
_(Promoted YYYY-MM-DD)_ via the Edit tool. This is the dedup signal
that step 1 consults on subsequent runs.
- See
references/synergy-bm-format.md for edit_note gotchas (never
use append with section, always read_note first, anchor
find_replace matches to the next ### heading).
- Prune pass. For entries already annotated
_(Resolved ...)_ in the
local SYNERGY file, offer to move the corresponding BM entry to the
### Resolved subsection of ## Cross-Project Synergy in the sibling
note. The user confirms each. Mirrors upstream-tracker workflow 6
(Promote to Basic Memory) prune-pass behavior.
- Report. Summarize: promoted count, pruned count, skipped count
(already-promoted), and flagged-for-enrichment count. Suggest verifying
the result with
build_context("memory://engineering/agents/vp-plugins-<this-project>-and-<sibling>").
See references/synergy-bm-format.md for the target section structure,
generalization transform rules, and edit_note gotchas.
Division of labor: This workflow owns the ## Cross-Project Synergy
section of sibling project entity notes in Basic Memory. The
upstream-tracker skill's workflow 6 (Promote to Basic Memory) owns
## Upstream Friction in package/tool entity notes. The retrospective
skill's step 7 owns engineering/* notes (patterns, conventions, lessons).
These three sections never overlap — synergy entries are cross-project,
upstream friction is package-specific, learnings are domain-specific.
4. Trend review (quarterly)
Every 4th sprint, perform a cross-cutting analysis of all SYNERGY tracking
files. This cadence aligns with the every-4th-sprint trend review used by
/retrospective and /upstream-tracker (see CLAUDE.md and MEMORY.md). It
replaces the interim workaround of running workflow 2 (Review) manually at
trend-review boundaries.
Input signals:
- Glob for all
SYNERGY-*.md files and read them. (Trend review is a
boundary-crossing path — step 5 cross-references Basic Memory and step 8
recommends workflow 5 (Promote to Basic Memory) — so private overlays must be
excluded; the SYNERGY-*.md glob does this structurally, since it cannot
match PRIVATE-SYNERGY-*.md. Private entries are reviewed via workflow 2
(Review)'s local-only view, never trend-prom
…(truncated)
1---2name: synergy-tracker3description: Manage cross-project synergy tracking between sibling projects. Use when the user wants to log a shared pattern, a divergence, an extraction candidate, or something a sibling project has that this one doesn't. Also use when the user wants to promote synergy entries to Basic Memory (workflow 5). NOT for upstream dependency bugs or vendor friction (use /upstream-tracker for those). Trigger phrases: 'synergy', 'sibling project', 'cross-project', 'extraction candidate', 'compare with [project]', 'both projects do', 'they have X we don't', 'shared pattern', 'divergence', 'cross-project alignment', 'review synergies', 'log this pattern', 'we should extract this', 'they handle this differently', 'promote synergy', 'promote to basic memory', 'promote synergy entries', 'sync synergy to memory', or any mention of patterns, divergences, shared practices, or BM promotion across related projects.4---56# Synergy Tracker78Manage the `SYNERGY-*.md` files that track shared patterns, divergences,9extraction candidates, and capabilities observed in sibling projects while10building this one. This skill tracks patterns and capabilities across11peer/sibling projects under the same maintainer — not upstream dependency12friction (use `/upstream-tracker` for that).1314## Tracking Files1516### Sibling projects (permanent files)1718Sibling projects are declared in `.claude/synergy-registry.json` (if it exists)19as an array of `{name, file, remote, bm-entity, relationship}` objects. See20`references/synergy-entry-format.md` for the full registry schema.2122To discover which projects are registered, read `.claude/synergy-registry.json`23if it exists. If it does not exist, glob for `SYNERGY-*.md` to infer sibling24names from existing files. If neither is present, ask the user which sibling25project is involved before proceeding.2627### File naming2829Files are named `SYNERGY-<project-name>.md`. The project name is taken from30the `name` field in the registry (tier 3 of the canonical derivation31algorithm — synergy-tracker's subject is always sibling). For projects32outside the registry, fall back to tier 4 (directory basename), then33normalize. Full algorithm:34`references/project-name-derivation.md`. Examples: `vp-knowledge` →35`SYNERGY-vp-knowledge.md`, `@scope/shared-utils` →36`SYNERGY-scope--shared-utils.md`.3738See `references/synergy-entry-format.md` for the full naming convention39(normalization rules) and `references/project-name-derivation.md` for the40tiered derivation algorithm shared with `/sibling-sync`.4142### Lifecycle4344SYNERGY files are **permanent** — they always exist for registered sibling45projects, even when all sections are empty. When an entry is resolved or46dismissed, remove it and restore the `_No entries yet._` placeholder for that47section. Do not delete the file.4849### Private overlay (`PRIVATE-SYNERGY-*.md`)5051For entries that must stay out of a public repo (a proprietary sibling's52internal paths, client names, unreleased plans), use a **gitignored**53`PRIVATE-SYNERGY-<project-name>.md` companion to the committed54`SYNERGY-<project-name>.md`. It holds additional private entries under the same55four section headings.5657**The `PRIVATE-` prefix is the privacy mechanism.** It keeps the overlay58OUTSIDE the `SYNERGY-*.md` glob namespace, so every public consumer (this59skill's boundary-crossing workflows, `/sibling-sync`, `/retrospective`, the60`sprint-review` agent, `session-start.sh`) **structurally cannot read it** — no61per-consumer exclusion is needed or used. Only the deliberate *local-only* read62in workflow 2 (Review) additionally globs `PRIVATE-SYNERGY-*.md` to assemble the63combined private view.6465**Invariant: private-overlay entries are NEVER promoted to Basic Memory66(workflow 5 (Promote to Basic Memory)) and NEVER reciprocated/written to a67sibling.** With the prefix scheme this is enforced structurally — the promote68and reciprocate paths glob `SYNERGY-*.md`, which cannot match69`PRIVATE-SYNERGY-*.md`. This keeps the public view a sibling sees stable (so70bilateral convergence still works) and prevents a proprietary↔public leak.7172When a `PRIVATE-SYNERGY-*.md` overlay exists, the committed73`SYNERGY-<project-name>.md` carries a one-line pointer noting it (gitignored74files are invisible to collaborators and `git grep`). Gitignored via the75explicit `PRIVATE-SYNERGY-*.md` line; `session-start.sh` warns if any is76tracked. Full convention: CLAUDE.md `### Synergy tracking convention`.7778**Same-title shadowing (workflow 2 (Review) only).** When a private entry shares79a title with a committed entry, surface **both** in the review view — the80committed row (still promotable/reciprocable) labelled `[committed, shadowed]`81and the private row labelled `[local]` — so the reviewer never loses sight that82a public twin exists. This local-view assembly never participates in83`/sibling-sync`'s title-keyed bilateral matching, which reads committed entries84only.8586### Structure8788All SYNERGY files use the same four-section structure:8990```91## Shared Patterns9293_No entries yet._9495## Divergences9697_No entries yet._9899## Extraction Candidates100101_No entries yet._102103## They Have / We Don't104105_No entries yet._106```107108Section semantics:109110- **Shared Patterns** — practices, conventions, or implementations that exist111 in both projects and should stay aligned112- **Divergences** — places where the two projects intentionally or accidentally113 differ in approach to the same problem114- **Extraction Candidates** — code, patterns, or logic in this project that is115 a strong candidate for extraction into a shared package or library116- **They Have / We Don't** — capabilities in the sibling project that this117 project lacks and may want to adopt. Apply the **domain-fit test** before118 logging:119120 > Pass test: "this project has the underlying need but lacks the implementation."121 > Fail test: "the sibling has a capability in a different domain than this project's."122123 Without this test, every comparison run produces noise the user must124 dismiss. Worked failures from Sprint 19: vp-beads's `swarm-wave` (sprint125 orchestration is a different domain from vp-claude's research fan-outs)126 and vp-beads's `vendor-sync` (vp-beads vendors content; vp-claude has no127 vendored surface to sync) both passed the surface filter but failed the128 domain-fit test.129130## Workflows131132Determine which workflow the user needs based on their request. If ambiguous,133default to workflow 1 (log) for "synergy" or "log this" requests, workflow 2134(review) for "review synergies" or "what's open" requests, and workflow 3135(compare) for "compare with" requests.136137### 1. Log a synergy entry138139When the user observes a shared pattern, a divergence, an extraction candidate,140or a capability gap between this project and a sibling — add it to the correct141file. Infer the details from the current conversation context: what code was142being discussed, what pattern was noticed, what contrast was made.143144**Steps:**1451461. Identify which sibling project is involved from the conversation context.147 If the user named a sibling project in their request (e.g., "compare with148 vp-knowledge", "log this for vp-knowledge"), use that name directly — do149 not ask again. Otherwise, check `.claude/synergy-registry.json` first. If150 the registry does not identify the sibling, glob for `SYNERGY-*.md` as a151 fallback. If neither resolves it, ask the user.152153**Step 1b — Guided registry creation (only when154`.claude/synergy-registry.json` is absent AND a sibling has been named in155step 1).** If the registry file already exists (returning user adding a156second sibling, or any case where a registry was hand-written), skip this157step silently and proceed to step 2 — do not modify hand-written158registries. **Append-to-existing is not supported**; that case falls back159to manual editing of `.claude/synergy-registry.json` (add a new160`{name, file, remote, bm-entity, relationship}` entry per the schema in161`references/synergy-entry-format.md`). Tracked separately as a future162enhancement (`vp-beads-bma`). Otherwise, run the following bootstrap so163the project can route future syncs and comparisons through the registry164rather than re-asking each time.165166- **Confirm the sibling name** resolved from step 1. If step 1 deferred to167 asking the user, defer this step too — only proceed once a name is in168 hand.169170- **Derive both project names before proceeding.**171 - `<sibling>` — the name confirmed in step 1. In practice this is already172 the tier-3 registry `name` field (or, for unregistered siblings, the173 directory basename: tier 4). No additional derivation is needed; just174 carry the value forward.175 - `<this-project>` — this project's own canonical name, derived using176 the four-tier algorithm in177 `skills/synergy-tracker/references/project-name-derivation.md` (self178 subject: tiers 1-4, starting with the sibling's back-pointer). In the179 common case — `vp-beads` with a registered sibling — tier 1 or tier 2180 resolves immediately. Use the normalized result as `<this-project>`181 everywhere below: in the `bm-entity` value and in any reciprocal file182 references.183184- **Auto-derive the four registry fields** that have unambiguous defaults:185 - `name` — already known from step 1.186 - `file` — mechanical: `SYNERGY-<sibling>.md`.187 - `remote` — probe the sibling's git origin if the sibling repo is188 accessible on disk at `../<sibling>/`:189190 ```bash191 git -C ../<sibling> remote get-url origin 2>/dev/null | sed 's/\.git$//'192 ```193194 If the command fails or the path is not accessible, leave `remote` as195 an empty string in the preview.196 - `bm-entity` — apply the canonical convention197 `engineering/agents/vp-plugins-<this-project>-and-<sibling>` (see198 `references/synergy-entry-format.md`).199200- **Prompt only the residuals.** At most three `AskUserQuestion` calls201 (Anthropic SDK caps the `header` field at 12 characters):202 - **First, a `header: "Visibility"` (10 chars) call** — "Public (committed203 registry) or Private (gitignored — the sibling's name is never committed)?"204 Default **Public**. Choose **Private** for a proprietary partner whose very205 existence must stay out of a public repo. When the user picks **Private**,206 switch to the private-sibling path:207 - Write the entry to `.claude/synergy-registry.local.json` only (never the208 committed base), with `file` set to `PRIVATE-SYNERGY-<sibling>.md`.209 - **Omit `bm-entity` entirely** — a private name in a Basic Memory path would210 leak it (the validator warns if present).211 - Create the gitignored content file `PRIVATE-SYNERGY-<sibling>.md` from the212 four-section template (it is the sibling's sole synergy file — there is no213 committed `SYNERGY-<sibling>.md`).214 - Verify both `.claude/synergy-registry.local.json` and the215 `PRIVATE-SYNERGY-*.md` wildcard are gitignored (`git check-ignore -q`).216 **Never add a per-name `PRIVATE-SYNERGY-<sibling>.md` line to `.gitignore`**217 — that line would itself commit the name; the `PRIVATE-SYNERGY-*.md`218 wildcard already covers it. If the wildcard is missing, warn the user to219 add the wildcard line (not a per-name one).220 - The preview shows ONLY the `.local.json` block (no committed-registry221 block, no `bm-entity`). Skip the Relationship-into-base step; relationship222 still goes into the `.local.json` entry.223 - One call with `header: "Relationship"` (12 chars). The validator's224 `KNOWN_RELATIONSHIPS` set caps options at six; `AskUserQuestion` caps at225 4 visible options + auto "Other". Surface the four most common —226 `sibling-plugin` (default), `shared-tooling`, `fork`, `consumer` — and227 let the auto "Other" route to a free-text fallback that the workflow228 then validates against the remaining two (`coordinated-release`,229 `dependency`). If the user types anything else, warn that the value will230 trigger a `validate-plugin.mjs` warning and confirm before writing. See231 `references/synergy-entry-format.md` "Relationship vocabulary" for the232 canonical set.233 - Only when `../<sibling>/` does not resolve to an accessible directory, a234 second call with `header: "Local path"` (10 chars) — free-text or235 skip. If the user provides a path, it goes into236 `.claude/synergy-registry.local.json`, never into the base registry.237238- **Preview both files in a single message** before writing anything. Use239 this shape (omit the `.local.json` block when no local-path was240 supplied). Show BOTH the placeholder schema and a worked substitution so241 the user can see how `<this-project>` and `<sibling>` resolve. **Annotate242 auto-derived fields with their source** as inline comments after rendering243 (e.g. `"remote": "https://github.com/voxpelli/vp-claude" # from <sibling>244 git origin`, `"bm-entity": "engineering/agents/..." # canonical245 convention`) so the user can spot derivation errors before approving:246247 ```248 Proposed .claude/synergy-registry.json (schema):249 [250 {251 "name": "<sibling>",252 "file": "SYNERGY-<sibling>.md",253 "remote": "<derived-or-blank>",254 "bm-entity": "engineering/agents/vp-plugins-<this-project>-and-<sibling>",255 "relationship": "<chosen>"256 }257 ]258259 Worked example (this-project = vp-beads, sibling = vp-knowledge):260 [261 {262 "name": "vp-knowledge",263 "file": "SYNERGY-vp-knowledge.md",264 "remote": "https://github.com/voxpelli/vp-claude",265 "bm-entity": "engineering/agents/vp-plugins-vp-beads-and-vp-knowledge",266 "relationship": "sibling-plugin"267 }268 ]269270 Proposed .claude/synergy-registry.local.json (only if local-path given):271 [272 { "name": "<sibling>", "local-path": "<path>" }273 ]274275 Confirm? [yes / edit / skip]276 ```277278- **Handle the user's response.** On `yes`, proceed to write. On `edit`,279 re-prompt each derived field individually so the user can correct280 `name`, `remote`, `bm-entity`, `relationship`, or the optional281 `local-path`, then re-render the preview. On `skip`, continue the282 workflow without writing the registry — resume to step 2.283284- **Write the files using the `Write` tool.** Always write285 `.claude/synergy-registry.json`, and additionally write286 `.claude/synergy-registry.local.json` only when a `local-path` was287 provided. The `.local.json` is always a separate file — never embed288 `local-path` in the committed base registry.289290- **Verify round-trip.** Use the `Read` tool to re-read each written file,291 then validate the JSON via292 `node -e 'JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"))' <path>`293 (any non-zero exit signals invalid JSON). Confirm: base registry contains294 entries with `name` and `file` set; if `.local.json` was written, it295 contains `name` and `local-path`. On base-registry parse failure or296 missing required fields, abort step 1b — report the problem and offer to297 re-run; do not proceed to step 2 with a broken base registry. On298 `.local.json` parse failure only, warn and continue without the local299 override (the base registry is still usable).300301- **Check that `.local.json` is gitignored** when one was written:302303 ```bash304 git check-ignore -q .claude/synergy-registry.local.json305 ```306307 Exit status semantics: `0` = file is gitignored (no action); `1` = file308 is **not** gitignored — warn the user with the exact line to add: "Add309 `.claude/*.local.json` to your `.gitignore` (covers both310 synergy-registry.local.json and vendor-registry.local.json, and is311 forward-compatible with future `.local.json` registries)." Do not312 auto-edit `.gitignore` — it is user-owned. `128` = the check itself313 failed (not a git repo, or another git error) — report the underlying314 error and skip the gitignore warning rather than emitting a315 false-positive.316317- **Resume to step 2** (Basic Memory pre-check).3183192. **Basic Memory pre-check.** If Basic Memory MCP tools are available, make320 two `mcp__basic-memory__search_notes` calls: one with the sibling project321 name, one with 2-4 keywords describing the topic being logged (e.g.,322 "PreCompact prompt command hook" or "edit\_note append gotcha"). If either323 search returns a matching note with synergy-related or engineering-pattern324 content, surface it to the user: "This pattern is already tracked in Basic325 Memory: \[summary]. Logging it locally as well so this project tracks it."326 If Basic Memory tools are not available, skip this step silently.3273. If no `SYNERGY-<project>.md` file exists yet, create it from the template328 in `references/synergy-entry-format.md` (four sections with placeholders).3294. Classify the entry into one of the four sections:330 - **Shared Pattern** — the same approach exists in both projects331 - **Divergence** — the projects handle this differently332 - **Extraction Candidate** — this project has something worth extracting333 - **They Have / We Don't** — the sibling has something we lack3345. Read the target file. **If the entry is proprietary** (names a private335 sibling's internal paths, client names, or unreleased plans that must not336 reach a public repo), target the gitignored `PRIVATE-SYNERGY-<project>.md`337 overlay instead of the committed `SYNERGY-<project>.md` (see338 `### Private overlay`). Before the first write to a new overlay, verify it is339 ignored: `git check-ignore -q PRIVATE-SYNERGY-<project>.md` (exit 0 = ignored,340 safe; exit 1 = NOT ignored — stop and have the user add `PRIVATE-SYNERGY-*.md`341 to `.gitignore` first). Then, **only for a public sibling that has a committed342 `SYNERGY-<project>.md`**, add a one-line pointer to that committed file343 (e.g. "*A private `PRIVATE-SYNERGY-<project>.md` overlay exists for this344 sibling.*") so collaborators know it may exist. **For a fully-private sibling**345 (registered only in `.local.json` with `file: PRIVATE-SYNERGY-<name>.md`),346 there is **no** committed `SYNERGY-<name>.md` and you write **no** pointer — a347 pointer would commit the private name. Otherwise (a public sibling, ordinary348 entry) target the committed `SYNERGY-<project>.md`.3496. Compose the entry from this project's perspective using the entry format from350 `references/synergy-entry-format.md`. Focus on impact and adoption cost, not351 implementation internals.3527. Add the entry under the correct section heading, using today's date. When353 adding the first entry to a section, replace the `_No entries yet._`354 placeholder. Keep entries concise — 1-3 sentences. The title should be355 scannable. **Entries written to a `PRIVATE-SYNERGY-*.md` overlay are never356 promoted to Basic Memory or reciprocated to a sibling** (privacy invariant —357 structurally enforced by the prefix).358359**Structured fields** (all optional — omit fields that add no signal):360361| Field | Values | Section |362| ------------------- | ------------------------------------------------------- | ------------------------------------------- |363| `Status:` | `aligned` · `drifting` | Shared Patterns |364| `Last verified:` | `YYYY-MM-DD` (use today's date for new entries) | Shared Patterns |365| `Convergence path:` | `accept-difference` · `adopt-theirs` · `propose-shared` | Divergences |366| `Readiness:` | `ready` · `needs-cleanup` · `proof-of-concept` | Extraction Candidates |367| `Priority:` | `adopt-soon` · `consider` · `deferred` | They Have / We Don't |368| `Effort:` | `trivial` · `moderate` · `significant` | Extraction Candidates, They Have / We Don't |369370See `references/synergy-entry-format.md` for full entry format templates and371field value definitions.372373**Bilateral reciprocation mandate.** When the sibling has already written374entries from their side (resolve the sibling path via the registry-with-override375pattern from workflow 3 (Compare with sibling) — `local-path` from the merged376registry, falling back to `../<sibling>/`; check377`<resolved-path>/SYNERGY-<this-project>.md` if the sibling repo is accessible378on disk), reciprocate by re-verifying each379entry from this project's angle, recording your verification dates, and380noting any drift you observe. Do not skip duplicates — the reciprocation IS381the verification step. As captured in BM382`engineering/agents/vp-plugins-vp-beads-and-vp-knowledge`:383384> SYNERGY entries describe two parallel implementations that *happen* to be385> aligned, not one shared thing — both sides need their own record so each386> can verify from their POV at their own cadence. "Reciprocation IS the387> verification step."388389Sprint 19 evidence: vp-claude reciprocated 9 shared-pattern entries from390vp-beads's `SYNERGY-vp-knowledge.md` to a new `SYNERGY-vp-beads.md`, and the391re-verification surfaced 3 actively drifting artifacts392(`validate-plugin.mjs` 358 vs 333 lines; `scripts/check-hooks.mjs` 366 vs393284 lines; `npm-run-all2` since-converged) plus 1 stale `aligned` row394(PreCompact retired post-v0.28.0 on the vp-claude side). When logging an395entry with no reciprocal yet on the sibling, prompt the user to file the396reciprocal entry on the sibling project (typically a follow-up task in the397sibling repo's bd backlog).3983998. **Eager promotion check.** If Basic Memory MCP tools are available, assess400 the project's tempo:401402 ```bash403 git rev-list --count --since="90 days ago" HEAD 2>/dev/null404 ```405406 Guard: skip if the repo has zero commits total407 (`git log --oneline -1 2>/dev/null` returns empty). Also skip if this is408 the first entry in any SYNERGY file for this project (the user is still409 learning the workflow — promotion is premature).410411 | Tempo | Commits in 90 days | Promotion behavior |412 | ------------ | ------------------ | ------------------------------------------------------------ |413 | **Dormant** | 0–4 | Offer inline promotion for any promotable entry |414 | **Moderate** | 5–14 | Offer only for Extraction Candidates with `Readiness: ready` |415 | **Active** | 15+ | Skip — the normal sprint cadence handles promotion |416417 When offering inline promotion, say: "This project has low commit418 frequency — SYNERGY entries can sit unread for months. This entry looks419 promotable to Basic Memory. Want to promote it now?"420421 If the user agrees, defer to workflow 5 (Promote to Basic Memory) for the422 actual write — invoke its single-entry path scoped to this entry (steps 3-4423 of workflow 5 (Promote to Basic Memory)). Step 8 only **offers** promotion;424 workflow 5 (Promote to Basic Memory) **performs** it. This split keeps425 `## Cross-Project Synergy` writes within workflow 5 (Promote to Basic Memory)'s426 sole-owner boundary.427428 If the user declines, or if Basic Memory tools are not available, or if the429 project is active, skip silently.430431### 2. Review open synergies432433Summarize the current state of all synergy tracking files.434435**Steps:**4364371. Glob for all `SYNERGY-*.md` files and read them. **This is the one local-only438 review, so ALSO glob `PRIVATE-SYNERGY-*.md` private overlays** and assemble439 the combined view per `### Private overlay`: merge public + private entries440 per section and **label every private (`PRIVATE-SYNERGY`-sourced) row441 `[local]`**. When a `[local]` entry shares a title with a committed entry,442 show **both** (committed row `[committed, shadowed]`, private row `[local]`)443 so the promotable public twin stays visible. A `[local]` row is review-only:444 never offer it as a reciprocation or Basic Memory promotion candidate (those445 paths glob `SYNERGY-*.md`, which structurally cannot reach446 `PRIVATE-SYNERGY-*.md`).4472. Present a summary grouped by file, showing counts per section and listing448 each open entry with title and date.4493. Flag stale entries (older than 3 months with no activity). A Trend Review450 entry resets the staleness clock for the entire file.4514. Highlight actionable items:452 - Extraction Candidates with `Readiness: ready` — extractable now453 - Divergences with `Convergence path:` of `adopt-theirs` or `propose-shared`454 - They Have / We Don't with `Priority: adopt-soon`4555. **Inverse-file glob staleness detection (optional).** For each sibling456 represented by a `SYNERGY-<sibling>.md` file on this side, attempt to457 read the inverse file — the sibling's `SYNERGY-<this-project>.md` —458 to surface cross-side drift that single-side review cannot detect.459 Reuse the registry-with-override path-resolution pattern from workflow 3460 (Compare with sibling): prefer the `local-path` field on the merged461 `.claude/synergy-registry.json` + `.claude/synergy-registry.local.json`462 entry (relative paths resolve from the current project root); if absent,463 fall back to `../<sibling>/SYNERGY-<this-project>.md`. `<this-project>`464 is this project's canonical name derived per465 `references/project-name-derivation.md` (tiers 1–4, sibling466 back-pointer first; in the common case the registered sibling's own467 registry resolves it immediately).468469 **Bilateral first.** When the sibling repo is accessible AND the user wants470 more than a single-side enrichment — full reciprocation gaps, status drift471 in both directions, auto-reciprocation — defer to `/sibling-sync`472 workflow 2 (Sync sibling SYNERGY) instead. Step 5 here only surfaces473 inverse-file findings as a side-channel of the single-side review;474 `/sibling-sync` is the authoritative bilateral tool.475476 **Degradation.** If the registry is missing, the sibling path does not477 resolve, the inverse file is not present, or any read fails for any478 reason, **skip this step silently and continue with the regular479 single-side review.** Never hard-fail. The inverse-file step is a480 best-effort enrichment, not a gate.481482 When the inverse file IS accessible, surface two classes of drift:483484 - **Stale `aligned` rows** (staleness threshold: workflow 4485 (Trend review (quarterly)) canonical — `Last verified:` more than two486 trend-review cycles, ≈8 sprints). Entries marked `Status: aligned` on487 this side whose corresponding entry on the sibling side shows measurable488 drift — either the sibling lists the entry under `## Divergences`489 (contradicting our `aligned`), or the sibling's entry carries490 `Status: drifting`, or the sibling has annotated the entry as491 resolved/retired. The motivating example: vp-knowledge retired492 `PreCompact` in v0.28.0 while this project's493 `SYNERGY-vp-knowledge.md` still tagged `PreCompact aligned 2026-04-05`494 — no automated detection caught the staleness until manual495 reciprocation. Flag each such row with the sibling's contradicting496 state so the user can reconcile.497 - **Missing-this-side rows.** Features the sibling tracks under498 `## Shared Patterns` (or any section) that no longer exist in this499 project — typically because this project retired the feature without500 updating the sibling-tracked entry. Surface them so the user can501 either re-add the feature, mark the entry as resolved on both sides,502 or escalate to `/sibling-sync` for bilateral reconciliation.503504 For deeper bilateral reconciliation (reciprocation gaps, status drift505 in both directions, auto-reciprocation), defer to `/sibling-sync`506 workflow 2 (Sync sibling SYNERGY) — workflow 2 (Review) here only507 surfaces inverse-file findings as part of the single-side review508 summary.509510**Output format:**511512```513## Synergy Status514515### <project-name>516517- Shared Patterns: N (N drifting)518- Divergences: N (N with active convergence path)519- Extraction Candidates: N (N ready)520- They Have / We Don't: N (N adopt-soon)521- [list each entry with title and date]522523### Notes524525- [stale entries]526- [actionable items]527- [inverse-file findings, if any: stale aligned rows, missing-this-side rows]528```529530If all files are empty or no SYNERGY files exist, say so and suggest whether a531comparison run (workflow 3 (Compare with sibling)) would be useful — note that it532works best when the sibling repo is accessible on disk at the registry-resolved533path (`local-path` from the merged `.claude/synergy-registry.json` +534`.claude/synergy-registry.local.json`, falling back to `../<project-name>`).535536**Source.** The inverse-file glob step is sourced from537`../vp-claude/UPSTREAM-vp-beads.md` entry 3 (2026-05-04).538539### 3. Compare with sibling540541Perform a direct comparison between this project and a named sibling to surface542unlogged synergy observations.543544**Steps:**5455461. Identify the sibling from the user's request or the `argument-hint` — if547 the user named a project, use that name directly without re-asking. Load548 the registry with override merge:549550 1. Read `.claude/synergy-registry.json` for the sibling's `remote` and any551 metadata.552 2. If `.claude/synergy-registry.local.json` exists, read it and merge it on553 top of the base registry. Match entries by the `name` key (the554 human-stable identifier across machines and BM entity paths); for each555 matched entry, fields present in `.local.json` win. A `.local.json`-only556 entry (no matching base `name`) is **added** as a private sibling when its557 `file` is `PRIVATE-SYNERGY-<name>.md`, and otherwise **ignored** (the base558 registry is the authoritative source of which *public* siblings exist).559 See `references/synergy-entry-format.md` "Private sibling entries".560561 If no project is identified from the argument, merged registry, or existing562 SYNERGY files, ask the user which sibling project to compare with.563 If no registry exists at all, also offer to create one via workflow 1 (Log a synergy entry) step 1b before proceeding with the comparison.5642. **Gather sibling context.** Resolve the sibling's local path: prefer the565 `local-path` field on the merged registry entry (relative paths are resolved566 from the current project root); if absent, fall back to `../<project-name>`567 relative to the current project root. If the resolved path is not568 accessible, ask the user for the path (and suggest they record it in569 `.claude/synergy-registry.local.json` to avoid re-prompting). Read the570 sibling's key files if accessible:571572 - `package.json` — dependencies, scripts, entry points573 - `CLAUDE.md` — conventions, architecture, workflow documentation574 - Skill files (`Glob` for `skills/**/SKILL.md`) — what skills exist, their575 workflows, trigger phrases576 - Hook definitions (`hooks/hooks.json`) — event handling patterns577 - Agent files (`Glob` for `agents/*.md`) — what agents exist578579 If neither local files, conversation context, nor Basic Memory provides580 substantive information about the sibling, stop and tell the user that a581 meaningful comparison requires access to the sibling repo or prior582 knowledge. Do not generate speculative entries.5833. **Diff patterns.** Compare against this project's equivalent files.584 Identify observations in each of the four categories:585 - Shared Patterns: conventions both projects use (same frontmatter fields,586 same BM integration approach, same commit style)587 - Divergences: structural or stylistic differences (different hook handling,588 different reference doc organization, different BM section ownership)589 - Extraction Candidates: logic in this project that would apply to the590 sibling (validation scripts, shared reference formats, utility functions)591 - They Have / We Don't: features or patterns in the sibling absent here592 (agents, skills, hooks, conventions this project lacks)5934. **Propose new entries.** Present each observation as a candidate entry with594 draft text matching the format in `references/synergy-entry-format.md`. For595 each, ask: "Log this as a \[category] entry for \[sibling]?" The user596 approves, edits, or skips each candidate. **No mutations without approval.**5975. Log confirmed entries by classifying, reading the file, composing, and598 adding per workflow 1 (Log) steps 4–7. Skip step 8 (eager promotion) for batch599 entries to avoid prompt fatigue. Offer a single summary at the end:600 "N entries logged. Run workflow 2 (Review) to review the full picture."601602 If the user skips all candidates, report that no entries were logged and603 suggest whether a follow-up comparison with different focus areas would be604 useful.605606### 5. Promote to Basic Memory607608Promote generalizable cross-project synergy entries from project-local609`SYNERGY-*.md` files into sibling project entity notes in Basic Memory. This610creates or extends a `## Cross-Project Synergy` section in the target sibling611note (typically at `engineering/agents/vp-plugins-<this-project>-and-<sibling>`).612Use only MCP tools from this skill's `allowed-tools` —613`mcp__basic-memory__search_notes`, `mcp__basic-memory__read_note`,614`mcp__basic-memory__edit_note`. If Basic Memory MCP tools are not available,615report that promotion is unavailable and suggest checking Basic Memory manually.616617**Steps:**6186191. **Scan for candidates.** Glob all `SYNERGY-*.md` files and read them. (This620 glob structurally excludes every `PRIVATE-SYNERGY-*.md` — both private621 *overlays* of public siblings and the sole content file of a *private622 sibling* registered via `.local.json`. Neither is ever promoted; their names623 must never reach Basic Memory. See `### Private overlay` and624 `references/synergy-entry-format.md` "Private sibling entries".) Filter625 eligible entries by section + structured fields:626 - Extraction Candidates with `Readiness: ready` (always)627 - Shared Patterns with `Status: aligned` (always)628 - Shared Patterns with `Status: drifting` (flag the drift in the draft)629 - Extraction Candidates with `Readiness: needs-cleanup` or630 `proof-of-concept` (lower priority — surface but mark as such)631 - Divergences with `Convergence path: adopt-theirs` or `propose-shared`632 (skip `accept-difference` — by definition not promotion-worthy)633 - They Have / We Don't with `Priority: adopt-soon` (skip `deferred`)634 - **Skip any entry already annotated with `_(Promoted YYYY-MM-DD)_`** —635 that annotation is the dedup signal written in step 4 below.6362. **Present candidates to the user.** Per-entry, never auto-promote. For637 each candidate, show:638 - Sibling project name and target BM note path639 - Section (Shared Pattern / Divergence / Extraction Candidate / They640 Have / We Don't) and entry title641 - A draft generalized version. Apply the transforms documented in642 `references/synergy-bm-format.md`: strip dates from titles,643 project-specific file paths, sprint numbers, and bd issue IDs;644 rewrite the prose from a neutral symmetric POV (so any sibling can645 read the entry as authoritative); keep `Status:`, `Convergence path:`,646 `Readiness:`, `Priority:`, and `Effort:` fields verbatim because they647 carry cross-project meaning.648 - Whether a Basic Memory note already exists for this sibling.649 Let the user approve, edit, or skip each candidate.6503. **Route by target.** For each approved candidate, look up the sibling's651 `bm-entity` value from `.claude/synergy-registry.json` (with652 `.claude/synergy-registry.local.json` merged on top by the `name` key).653 If `bm-entity` is present, use it as the BM note path. If absent, call654 `mcp__basic-memory__search_notes` with the sibling project name and655 surface the candidate matches to the user. **Legacy `bm-entity` form656 warning:** if the registered `bm-entity` does NOT start with657 `engineering/agents/vp-plugins-` (the canonical convention from v0.12.1658 onward), warn the user before proceeding: "Registered bm-entity659 `<value>` does not match the canonical660 `engineering/agents/vp-plugins-<this-project>-and-<sibling>` form. This661 may be a pre-v0.12.1 registry pointing at a single-project entity note662 (e.g. `npm/<sibling>`) — workflow 5 (Promote to Basic Memory) will663 write to that legacy location, which may scatter cross-project content.664 Recommended: update the registry to the canonical form and migrate any665 existing `## Cross-Project Synergy` content to666 `engineering/agents/vp-plugins-<this-project>-and-<sibling>` first." Ask667 the user whether to proceed anyway, abort, or migrate. **Stale668 `bm-entity` fallback:**669 if `bm-entity` is present but step 4's `read_note` returns not-found670 (the registry path has been renamed or deleted), warn the user671 ("BM note not found at `<bm-entity>` — registry may be stale") and672 fall through to `mcp__basic-memory__search_notes` exactly as the673 absent-`bm-entity` row does. The full routing table — including the674 fallback search order (`engineering/agents/` for relationship notes,675 then `projects/` and `npm/` as last-resort fallbacks for unregistered676 siblings) — lives in `references/synergy-bm-format.md`.6774. **Write or flag.** Three branches per approved candidate:678 - **Note exists, has `## Cross-Project Synergy` with target subsection** —679 call `mcp__basic-memory__read_note` first to fetch exact content, then680 `mcp__basic-memory__edit_note` with `find_replace` anchored to the next681 `###` heading for uniqueness, `expected_replacements=1`. Deduplicate by682 entry title (case-insensitive, whitespace-trimmed) before appending — if683 the title already appears in the subsection, skip. If `find_replace`684 returns zero replacements despite `expected_replacements=1`, the note was685 edited between `read_note` and `edit_note` — do NOT annotate the local686 entry; defer this candidate (increment a deferred-count) and continue687 with the next candidate. The step 6 report includes deferred entries688 under "deferred (BM note changed mid-write): N entries — re-run689 workflow 5 (Promote to Basic Memory) once BM writes settle." Do NOT690 re-invoke workflow 5 (Promote to Basic Memory) automatically inside691 the same run; persistent contention would otherwise loop.692 - **Note exists, no `## Cross-Project Synergy` section** — call693 `mcp__basic-memory__edit_note` with `insert_before_section` on694 `Relations` to add the full section block (all five subsections per the695 template in `references/synergy-bm-format.md`).696 - **No note exists** — do NOT create a thin note. Flag for enrichment:697 "No Basic Memory note for `<sibling>`. Enrich it first (manual creation698 under `engineering/agents/`), then re-run workflow 5 (Promote to Basic699 Memory)."700 - **After successful write,** annotate the local SYNERGY entry with701 `_(Promoted YYYY-MM-DD)_` via the `Edit` tool. This is the dedup signal702 that step 1 consults on subsequent runs.703 - See `references/synergy-bm-format.md` for `edit_note` gotchas (never704 use `append` with `section`, always `read_note` first, anchor705 `find_replace` matches to the next `###` heading).7065. **Prune pass.** For entries already annotated `_(Resolved ...)_` in the707 local SYNERGY file, offer to move the corresponding BM entry to the708 `### Resolved` subsection of `## Cross-Project Synergy` in the sibling709 note. The user confirms each. Mirrors upstream-tracker workflow 6710 (Promote to Basic Memory) prune-pass behavior.7116. **Report.** Summarize: promoted count, pruned count, skipped count712 (already-promoted), and flagged-for-enrichment count. Suggest verifying713 the result with714 `build_context("memory://engineering/agents/vp-plugins-<this-project>-and-<sibling>")`.715716See `references/synergy-bm-format.md` for the target section structure,717generalization transform rules, and `edit_note` gotchas.718719**Division of labor:** This workflow owns the `## Cross-Project Synergy`720section of sibling project entity notes in Basic Memory. The721upstream-tracker skill's workflow 6 (Promote to Basic Memory) owns722`## Upstream Friction` in package/tool entity notes. The retrospective723skill's step 7 owns `engineering/*` notes (patterns, conventions, lessons).724These three sections never overlap — synergy entries are cross-project,725upstream friction is package-specific, learnings are domain-specific.726727### 4. Trend review (quarterly)728729Every 4th sprint, perform a cross-cutting analysis of all SYNERGY tracking730files. This cadence aligns with the every-4th-sprint trend review used by731`/retrospective` and `/upstream-tracker` (see CLAUDE.md and MEMORY.md). It732replaces the interim workaround of running workflow 2 (Review) manually at733trend-review boundaries.734735**Input signals:**7367371. Glob for all `SYNERGY-*.md` files and read them. (Trend review is a738 boundary-crossing path — step 5 cross-references Basic Memory and step 8739 recommends workflow 5 (Promote to Basic Memory) — so private overlays must be740 excluded; the `SYNERGY-*.md` glob does this structurally, since it cannot741 match `PRIVATE-SYNERGY-*.md`. Private entries are reviewed via workflow 2742 (Review)'s local-only view, never trend-prom743744…(truncated)