Re-synchronizes the vault with external sources. In default mode, scans the `sources` field of all entities, deduplicates URLs, fetches updated content from each source (Confluence, GDocs, GitHub, Markdown), performs incremental diff and delegates writing to /bedrock:preserve. With --people, scans actor repositories via GitHub API and identifies active contributors. With --github, detects relevant activity in repositories and correlates PRs with topics/projects via LLM semantic matching. Use ...
Scanned 9/6/2026
Install to Claude Code
npx -y skills add ccplugins/awesome-claude-code-plugins --skill sync --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Sync?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/ccplugins-sync)More formats (shields.io, HTML) on the badges page.
---
name: sync
description: >
Re-synchronizes the vault with external sources. In default mode, scans the `sources`
field of all entities, deduplicates URLs, fetches updated content from each source
(Confluence, GDocs, GitHub, Markdown), performs incremental diff and delegates writing to
/bedrock:preserve. With --people, scans actor repositories via GitHub API and
identifies active contributors. With --github, detects relevant activity in
repositories and correlates PRs with topics/projects via LLM semantic matching.
Use when: "bedrock sync", "bedrock-sync", "/bedrock:sync", "synchronize",
"update sources", "sync people", "sync github".
user_invocable: true
allowed-tools: Bash, Read, Write, Edit, Glob, Grep, Skill, Agent, mcp__plugin_github_github__*, mcp__plugin_atlassian_atlassian__*
---
# /bedrock:sync — Vault Synchronization
## Plugin Paths
Entity definitions and templates are in the plugin directory, not in the vault root.
Use the "Base directory for this skill" provided at invocation to resolve paths:
- Entity definitions: `<base_dir>/../../entities/`
- Templates: `<base_dir>/../../templates/{type}/_template.md`
- Plugin CLAUDE.md: `<base_dir>/../../CLAUDE.md` (already injected automatically into context)
Where `<base_dir>` is the path provided in "Base directory for this skill".
---
## Vault Resolution
Resolve which vault to sync. This skill can be invoked from any directory.
**Step 1 — Parse `--vault` flag:**
Check if the input arguments include `--vault <name>`. If found, extract the vault name and remove it from the arguments before parsing `--people` or `--github`.
**Step 2 — Resolve vault path:**
1. **If `--vault <name>` was provided:**
Read the vault registry at `<base_dir>/../../vaults.json`. Find the entry matching the name.
If not found: error — "Vault `<name>` is not registered. Run `/bedrock:vaults` to see available vaults."
If found: set `VAULT_PATH` to the entry's `path` value. Store the resolved vault name as `VAULT_NAME`.
2. **If no `--vault` flag — CWD detection:**
Read `<base_dir>/../../vaults.json`. Check if the current working directory is inside any registered vault path
(CWD starts with a registered vault's absolute path). If multiple match, use the longest path (most specific).
If found: set `VAULT_PATH` to the matching vault's `path`. Store its name as `VAULT_NAME`.
3. **If CWD detection fails — default vault:**
From the registry, find the vault with `"default": true`.
If found: set `VAULT_PATH` to the default vault's `path`. Store its name as `VAULT_NAME`.
4. **If no resolution:**
Error — "No vault resolved. Available vaults:" followed by the registry listing.
"Use `--vault <name>` to specify, or run `/bedrock:setup` to register a vault."
**Step 3 — Validate vault path:**
```bash
test -d "<VAULT_PATH>" && echo "exists" || echo "missing"
```
If missing: error — "Vault path `<VAULT_PATH>` does not exist on disk. Run `/bedrock:setup` to re-register."
**Step 4 — Read vault config:**
```bash
cat <VAULT_PATH>/.bedrock/config.json 2>/dev/null
```
Extract `language`, `git.strategy`, and other relevant fields for use in later phases.
**From this point forward, ALL vault file operations use `<VAULT_PATH>` as the root.**
- Entity directories: `<VAULT_PATH>/actors/`, `<VAULT_PATH>/people/`, etc.
- Git operations: `git -C <VAULT_PATH> <command>`
- When delegating to `/bedrock:preserve`, pass `--vault <VAULT_NAME>`
---
## Overview
This skill synchronizes the vault with external sources. It operates in three modes:
| Mode | Flag | Description |
|---|---|---|
| **Sources (default)** | _(none)_ | Re-synchronizes entities with a populated `sources` field |
| **People** | `--people` | Scans actor repositories and identifies active contributors |
| **GitHub** | `--github` | Detects activity in repos and correlates PRs with topics/projects |
---
## Routing
Analyze the argument passed by the user:
1. If argument contains `--people` → go to **Mode: Sync People** (below)
2. If argument contains `--github` → go to **Mode: Sync GitHub** (below)
3. Otherwise → go to **Mode: Sync Sources (default)** (below)
> **Note:** If no argument is passed, or the argument does not contain recognized flags,
> execute the default mode (Sync Sources).
---
---
# Mode: Sync Sources (default)
## Overview
This skill scans the `sources` field of all vault entities, deduplicates by URL,
fetches updated content from each external source, compares with existing entities
in the vault (incremental diff), and delegates all changes to `/bedrock:preserve` for centralized writing.
`/bedrock:sync` **does NOT write entities directly** — all entity writing goes through `/bedrock:preserve`.
After re-sync, `/bedrock:preserve` updates `synced_at` in the `sources` field of affected entities.
`/bedrock:sync` **does NOT ingest new sources** — for that, use `/bedrock:teach`.
**You are an execution agent.** Follow the phases below in order, without skipping steps.
---
## Phase 0 — Synchronize the Vault
Execute:
```bash
git -C <VAULT_PATH> pull --rebase origin main
```
If the pull fails:
- No remote configured: warn "No remote configured. Working locally." and proceed.
- Pull conflict: `git -C <VAULT_PATH> rebase --abort` and warn the user. Do NOT proceed without resolving.
- Otherwise: proceed.
---
## Phase 1 — Collect Syncable Sources
Provenance is recorded in the `sources` field of each entity's frontmatter.
Scan all entities to collect unique URLs.
1. Use Grep to find entities with a non-empty `sources` field:
```
Grep pattern "^sources:" in directories: actors/, people/, teams/, topics/, discussions/, projects/, fleeting/
```
2. For each file found, use Read to extract the `sources` field from the YAML frontmatter.
Each entry has: `{url, type, synced_at}`
3. **Build URL → entities map:**
Deduplicate by URL. For each unique URL, record all entities that reference it:
```
{
"https://mycompany.atlassian.net/...": {
type: "confluence",
synced_at: "2026-04-09",
entities: ["actors/billing-api.md", "topics/2026-04-feature-x.md"]
},
"https://github.com/acme-corp/billing-api": {
type: "github-repo",
synced_at: "2026-04-10",
entities: ["actors/billing-api.md"]
}
}
```
4. **Filter syncable sources:**
- Keep only URLs with `type` in (`confluence`, `gdoc`, `github-repo`, `markdown`)
- Ignore URLs with `type` = `csv` or `manual` (log: "URL X ignored — non-syncable type")
5. Store the list of syncable URLs with their entity maps
Report: "Phase 1: N entities with sources, M unique URLs found, K syncable, J ignored (non-syncable type)."
---
## Phase 2 — Re-read Sources
For each syncable source, fetch updated content:
### 2.1 Confluence
For sources with `source_type: confluence`:
1. Read the internal fetcher at `<base_dir>/../confluence-to-markdown/SKILL.md`
2. Follow its instructions to parse the URL, choose layer (MCP → API → browser), and extract content
3. The fetcher returns Markdown content and page title
### 2.2 Google Docs
For sources with `source_type: gdoc`:
1. Read the internal fetcher at `<base_dir>/../gdoc-to-markdown/SKILL.md`
2. Follow its instructions to parse the URL, detect document type, choose layer (MCP → API/public export → browser), and extract content
3. The fetcher returns Markdown content and document metadata
### 2.3 GitHub Repository
For sources with `source_type: github-repo`:
1. Extract `owner/repo` from the URL (path segments after `github.com/`)
2. Use GitHub MCP directly (NOT via subagent — MCP permissions are not inherited):
- `mcp__plugin_github_github__get_file_contents` → read the repo's README.md
- `mcp__plugin_github_github__list_commits` → last 10 commits
- `mcp__plugin_github_github__list_pull_requests` → last 5 PRs (state=all, sort=updated)
3. Compile everything into a single markdown text
> **Best-effort:** If any MCP call fails, continue with what was obtained. Do NOT block the sync.
### 2.4 Local Markdown
For sources with `source_type: markdown`:
1. Extract the path from the `url` field
2. Use Read to read the file directly
3. If the file does not exist: log and skip
### 2.5 Error handling
- If reading a source fails (MCP unavailable, broken URL, missing file):
- Log the error: "Source X failed — reason"
- Continue with remaining sources
- Do NOT abort the entire execution for one source
Report: "Phase 2: N sources read successfully, M failed (list)."
---
## Phase 3 — Incremental Diff + Entity Extraction
### 3.1 Load entity definitions
Use Read to read ALL entity definition files from the plugin (see "Plugin Paths" section):
`<base_dir>/../../entities/*.md`
These files define what each entity type is, when to create, and how to distinguish them.
Internalize these definitions — you will use them to classify content.
### 3.2 Catalog existing entities
Use Glob to list all files in each entity directory (excluding `_template.md`):
- `<VAULT_PATH>/actors/*.md`
- `<VAULT_PATH>/people/*.md`
- `<VAULT_PATH>/teams/*.md`
- `<VAULT_PATH>/topics/*.md`
- `<VAULT_PATH>/discussions/*.md`
- `<VAULT_PATH>/projects/*.md`
- `<VAULT_PATH>/fleeting/*.md`
For each file found:
- Extract the filename without extension (e.g.: `billing-api`)
- Use Read to extract the `name` (or `title`) and `aliases` fields from the YAML frontmatter
- Store: `{filename, name, aliases, type}` for matching
### 3.3 Analyze content and detect changes
For each source successfully read in Phase 2:
1. **Identify entities mentioned in the updated content:**
- For each entity cataloged in Phase 3.2, check if the filename, name, or alias appears in the content
- Match rules:
- Normalize for comparison: lowercase, no accents, no hyphens
- Partial match acceptable for compound names (e.g.: "billing api" matches "billing-api")
- Do NOT match substrings of 3 letters or fewer (e.g.: "api" does NOT match "billing-api")
- Do NOT match generic words (e.g.: "company", "service", "system")
2. **Compare with the source's `entities_generated`:**
- Entity in content AND already in vault → candidate for `update` (if there is new info in the content)
- Entity in content but NOT in vault → candidate for `create`
- Entity in `entities_generated` but NOT in updated content → **keep** (do not delete)
3. **Classify new entities:**
- For `create` candidates, consult the entity definitions:
- "When to create" section → positive criteria
- "When NOT to create" section → exclusion criteria
- "How to distinguish from other types" section → disambiguation
> **Projects:** `project` is a valid type in extraction. When classifying new entities,
> pay special attention to signals of initiatives with closed scope (deadline, deliverables,
> focal points). Consult `entities/project.md` for creation criteria. An excerpt that
> mentions migration with a deadline and responsible person is probably a project, not a topic.
4. **Record** for each detected entity:
- Type (actor, person, team, topic, discussion, project)
- Canonical name (filename or suggested slug)
- Action: `create` or `update`
- Extracted info: excerpt of the content where it appears
- Source of origin: source slug
Report: "Phase 3: N entities detected (P creates, Q updates) across M sources."
---
## Phase 4 — Consolidated Confirmation
**REQUIRED:** Before creating/updating any entity, present a SINGLE list
with all changes from ALL sources:
```
## Sync — Proposed Changes
| # | Source | Type | Name | Action | Info |
|---|---|---|---|---|---|
| 1 | roadmap-26q1 | topic | 2026-04-feature-x | create | New topic mentioned |
| 2 | eventos-cobranca | actor | webhook-receiver | update | Description updated |
| ... | ... | ... | ... | ... | ... |
Total: N creates, M updates across P sources.
Confirm? (yes/no/adjust)
```
- **yes**: proceed to Phase 5
- **no**: abort with "Sync cancelled. No entities modified."
- **adjust**: ask what to adjust, modify list, re-present
**If no changes detected in any source:**
- Report: "No changes detected in any source. Vault is already up to date."
- Skip to Phase 6 (update `last_synced` anyway)
**Do NOT proceed without explicit user confirmation.**
---
## Phase 5 — Delegate to /bedrock:preserve
### 5.1 Compile structured list
Build the entity list in the format accepted by `/bedrock:preserve`:
```yaml
entities:
- type: topic
name: "2026-04-feature-x"
action: create
content: "relevant excerpt from content extracted in Phase 3..."
relations:
actors: ["actor-slug-1"]
people: ["person-slug-1"]
source: "confluence"
- type: actor
name: "webhook-receiver"
action: update
content: "new context extracted in Phase 3..."
source: "github-repo"
```
**Compilation rules:**
- `type` and `name`: extracted from Phase 3
- `action`: `create` or `update` as identified
- `content`: excerpt of the source content that justifies the entity
- `relations`: infer relationships between entities in the list (if A mentions B, include B in A's relations)
- `source`: use the `source_type` of the originating source
### 5.2 Invoke /bedrock:preserve
Use the Skill tool to invoke `/bedrock:preserve --vault <VAULT_NAME>` passing the structured list as argument.
The `--vault <VAULT_NAME>` flag ensures preserve writes to the same vault.
`/bedrock:preserve` handles:
- Textual matching with existing entities
- Creation of new entities following templates
- Updating existing entities (merge/append-only)
- Bidirectional linking (wikilinks)
- Git commit of entities
### 5.3 Await result
`/bedrock:preserve` returns:
- List of created/updated entities
- Commit hash (if there was a commit)
- Any errors or warnings
Record the result for use in the final report (Phase 7).
---
## Phase 6 — Update synced_at in Entities
After re-sync of each URL, `/bedrock:preserve` has already updated the entities with new content.
Additionally, for each URL processed successfully, pass `source_url` and `source_type`
to `/bedrock:preserve` so it updates `synced_at` in the `sources` field of each mapped entity.
The URL → entities map (built in Phase 1) indicates which entities need
`synced_at` updated for each re-synced URL.
> **Note:** `/bedrock:preserve` already handles the entity commit. `/bedrock:sync` does NOT make a separate commit.
---
## Phase 7 — Report
Present to the user:
```
## Report
| Metric | Value |
|---|---|
| Sources found | N |
| Sources synchronized | N |
| Sources ignored (type) | N |
| Sources with error | N |
| Entities created | N |
| Entities updated | N |
### Per source
| Source | Type | Entities | Status |
|---|---|---|---|
| roadmap-26q1 | confluence | 3 creates, 2 updates | ✅ |
| acme-corp-billing-api | github-repo | 0 creates, 1 update | ✅ |
| manual-notes | manual | — | ⏭️ ignored |
| broken-source | confluence | — | ❌ error (reason) |
### Git
- Commit (entities): <hash from /bedrock:preserve or "no entities">
- Commit (sources): vault: syncs N sources [source: sync]
- Push: ✅ success / ❌ failed (reason)
### Suggestions
- [sources with errors that can be fixed]
- [entities mentioned in content but not created, if any]
```
---
## Critical Rules
| # | Rule |
|---|---|
| 1 | **NEVER write entities directly** — all entity writing goes through `/bedrock:preserve` |
| 2 | **NEVER create sources** — `/bedrock:sync` only processes URLs already registered in entities' `sources` field |
| 3 | **NEVER delete entities** — entities absent from updated content are kept |
| 4 | **ALWAYS confirm** consolidated proposal with user before executing (Phase 4) |
| 5 | **Best-effort for external sources** — never block due to unavailable MCP or broken URL |
| 6 | **MCP in main context** — do NOT use subagents for GitHub/Atlassian MCP calls |
| 7 | **csv and manual sources are ignored** — static types with no URL to re-fetch |
| 8 | **Maximum 2 push attempts** — after that, abort and inform |
| 9 | **Sensitive data** — NEVER include credentials, tokens, passwords, PANs, CVVs |
| 10 | **Frontmatter keys in English**, values in the vault's configured language |
| 11 | **Bare wikilinks** — `[[name]]`, never `[[dir/name]]` |
---
---
# Mode: Sync People (--people)
Skill that populates `people/` from recent commits in repositories listed in `actors/`.
**You are an execution agent.** Follow the phases below in order, without skipping steps.
Do not make git commit/push. Do not update `topics/` or `actors/`. Do not read CLAUDE.md from repositories.
---
## Phase 1 — Actor collection
1. Use Glob to list all files `<VAULT_PATH>/actors/*.md`
2. Exclude `<VAULT_PATH>/actors/_template.md`
3. For each file, use Read to extract from the YAML frontmatter:
- `repository` — GitHub URL (e.g.: `https://github.com/acme-corp/billing-api/`)
- `team` — squad wikilink (e.g.: `[[squad-payments]]`)
- `name` — canonical name of the actor (e.g.: `billing-api`)
4. Parse `owner/repo` from the URL: extract the two path segments after `github.com/`
5. **Skip** actors without a `repository` field, with an empty URL, or with a URL that does not contain `github.com`
6. Store the list of valid actors: `{name, owner, repo, team_wikilink, team_slug}`
- `team_slug`: extracted from the wikilink, e.g.: `[[squad-payments]]` → `squad-payments`
At the end of this phase, report: "Phase 1: N actors found, M with valid repository, K skipped."
---
## Phase 2 — Commit collection
For each actor in the list (in parallel when possible):
1. Calculate the date 30 days ago in ISO 8601 format (e.g.: `2026-03-04T00:00:00Z`)
2. Execute via Bash:
```
gh api "repos/{owner}/{repo}/commits?since={date_30_days}&per_page=100" 2>/dev/null
```
3. If the command fails (404, 403, network error): **log and skip** — do not fail the execution
4. For each commit in the JSON result, extract:
- `author.login` — GitHub login (may be `null` if commit via email without linked account)
- `commit.author.name` — author's display name
5. **Filter bots:** ignore commits where:
- `author.login` is `null`
- `author.login` contains `[bot]`
- `author.login` (case-insensitive) is exactly: `dependabot`, `renovate`, `github-actions`, `snyk-bot`, `codecov`, `sonarcloud`, `renovate-bot`, `depfu`
6. Store the valid commits associated with the actor
At the end of this phase, report: "Phase 2: N repositories accessed, M with commits, K inaccessible (list). Total of L commits from P unique contributors."
---
## Phase 3 — Aggregation
1. Group all commits by `author.login` (lowercase)
2. For each unique person, build:
- `github`: login in lowercase
- `name`: `commit.author.name` from the most recent commit (fallback: login if name is empty)
- `focal_points`: list of canonical actor names where the person has commits (no duplicates)
- `team_counts`: commit count by squad (e.g.: `{squad-payments: 15, squad-notifications: 3}`)
- `team`: squad with most commits; in case of tie, first alphabetically
- `filename`: derived from `name` → lowercase, no accents (normalize NFD and remove combining marks), spaces→hyphens, special characters removed, kebab-case
- E.g.: `Alice Smith` → `alice-smith.md`
- E.g.: `José María` → `jose-maria.md`
- Fallback: if name not available, use login as filename
3. **Duplicate detection by filename:**
- If two contributors (different logins) generate the same filename: append `-2`, `-3`, etc. to the second
- If a file `people/{filename}` already exists with a different `github`: treat as different person, append suffix
At the end of this phase, report: "Phase 3: N unique contributors identified. Distribution by squad: [list]."
---
## Phase 4 — Write people
For each person:
### If `people/{filename}` does NOT exist — CREATE:
Use Write to create the file with this exact content (replace the placeholders):
```markdown
---
type: person
name: "{display_name}"
role: ""
team: "[[{team_slug}]]"
focal_points: [{focal_points_yaml}]
github: "{github_login}"
jira: ""
updated_at: {today_date_YYYY-MM-DD}
updated_by: "sync-people"
tags: [type/person]
---
# {Display Name}
> Active contributor identified via commits in the last 30 days.
## Team
Member of [[{team_slug}]].
## Focal Points
{focal_points_list}
## Active Topics
_No topics linked yet._
```
Where:
- `{focal_points_yaml}` = YAML array of wikilinks, e.g.: `["[[billing-api]]", "[[notification-service]]"]`
- `{focal_points_list}` = markdown list, e.g.:
```
- [[billing-api]] — recent commits
- [[notification-service]] — recent commits
```
- `{today_date_YYYY-MM-DD}` = today's date in `YYYY-MM-DD` format
### If `people/{filename}` ALREADY exists — UPDATE:
1. Use Read to read the existing file
2. **Merge focal_points:** add new actors to the existing YAML array, without removing those already there
3. **Update team:** overwrite with the new calculation (squad with most commits)
4. **Update updated_at:** today's date
5. **Update updated_by:** `"sync-people"`
6. Update the "Focal Points" section in the markdown body to reflect the merged list
7. Use Edit to apply the changes (do not rewrite the entire file — preserve manual content)
**Identification by login:** Before creating a new file, use Grep to search for `github: "{login}"` in `<VAULT_PATH>/people/*.md`. If found, update that file instead of creating a new one (even if the filename does not match).
At the end of this phase, report: "Phase 4: N people created, M updated."
---
## Phase 5 — Update teams
For each squad that received new people:
1. Use Read to read `teams/{team_slug}.md`
2. Extract the `members` array from the YAML frontmatter
3. For each person in the squad: add `"[[{person_filename_without_ext}]]"` to the array if it does not exist
4. Update `updated_at` and `updated_by: "sync-people"` in the frontmatter
5. Use Edit to apply the changes to the frontmatter
**Do not modify** any other section of the team file.
At the end of this phase, report: "Phase 5: N teams updated. [list of squads → number of members added]."
---
## Phase 6 — Final report
Print a consolidated summary:
```
## Sync People — Report
| Metric | Value |
|---|---|
| Actors scanned | N |
| Repositories accessed | N |
| Inaccessible repositories | N |
| Commits analyzed | N |
| Contributors found | N |
| People created | N |
| People updated | N |
| Teams updated | N |
### People by squad
| Squad | People |
|---|---|
| squad-payments | alice, bob |
| squad-notifications | carol |
| ... | ... |
### Inaccessible repositories
- owner/repo — error (if any)
```
---
## General rules
- **Language:** Use the vault's configured language for content, technical terms in English
- **Filenames:** kebab-case, no accents, lowercase
- **Wikilinks:** no path — `[[name]]`, never `[[people/name]]`
- **Frontmatter:** valid YAML, double quotes for strings with special characters
- **Idempotency:** identify people by `github` login, not by filename
- **Errors:** log and continue — never fail the entire execution for one repo or person
- **No git:** do not commit, push, or perform any git operations
- **No topics:** do not create/update files in `topics/`
- **No actors:** do not modify files in `actors/`
---
---
# Mode: Sync GitHub (--github)
## Overview
This is an **autonomous agent** designed to run in background without human interaction.
It traverses all actors with `status: active` and a populated `repository` field,
fetches recent PRs via GitHub MCP, filters noise, uses LLM semantic matching to correlate
PRs with existing topics/projects in the vault, and delegates updates to `/bedrock:preserve`.
**Operating mode: autonomous.**
- Does NOT ask for confirmation — processes and writes automatically
- Safety guaranteed by: (1) only HIGH confidence correlations generate updates,
(2) topics/projects receive informational notes (append), never status overwrite,
(3) medium confidence correlations are recorded in the report for human review
- Generates a report in `fleeting/` at the end of each execution
**For recurring execution:**
- Via `/loop`: `/loop 6h /bedrock:sync --github`
- Via `/schedule`: configure cron with this skill
`/bedrock:sync --github` **does NOT write entities directly** — all entity writing goes through `/bedrock:preserve`.
Exception: watermark fields (`last_synced_at`, `last_synced_sha`) in actor frontmatter are written
directly via Edit (they are not new entities, they are sync metadata).
`/bedrock:sync --github` **does NOT create new topics or projects** — it only updates existing ones.
**You are an autonomous execution agent.** Follow the phases below in order, without skipping steps.
Do NOT ask for user confirmation in any phase.
---
## Phase 0 — Synchronize the Vault
Execute:
```bash
git -C <VAULT_PATH> pull --rebase origin main
```
If the pull fails:
- No remote configured: log "No remote configured. Working locally." and proceed.
- Pull conflict: `git -C <VAULT_PATH> rebase --abort`, log the error and **ABORT** the entire execution.
Record in the report: "Aborted — git conflict on initial pull."
- Otherwise: proceed.
---
## Phase 1 — Collect Syncable Actors
1. Use Glob to list all files `<VAULT_PATH>/actors/*.md`
2. Exclude `<VAULT_PATH>/actors/_template.md`
3. For each file, use Read to extract from the YAML frontmatter:
- `status` — actor status
- `repository` — GitHub repository URL
- `name` — actor name
- `last_synced_at` — date of last GitHub sync (may not exist)
- `last_synced_sha` — SHA of last sync (may not exist)
4. **Filter syncable actors:**
- Keep only actors with `status: active` (or `in-development`)
- Keep only actors with a populated `repository` field containing `github.com`
- Ignore actors with `status: deprecated` (log: "Actor X ignored — deprecated")
- Ignore actors without `repository` or with an invalid URL (log: "Actor X ignored — no GitHub repository")
5. For each syncable actor, extract `owner/repo` from the URL:
- Parse the URL: `https://github.com/<owner>/<repo>/` → `owner`, `repo`
- Remove trailing slashes and `.git` suffix if present
6. Store the list of syncable actors with: `{filename, name, owner, repo, last_synced_at, last_synced_sha}`
Log: "Phase 1: N actors found, M syncable, K ignored (deprecated/no repo)."
---
## Phase 2 — Fetch PRs via GitHub MCP
For each syncable actor, fetch recent PRs:
1. Use GitHub MCP directly (NOT via Agent tool — MCP permissions are not inherited by subagents):
- `mcp__plugin_github_github__list_pull_requests` with parameters:
- `owner`: repo owner
- `repo`: repo name
- `state`: "all" (open, merged, closed)
- `sort`: "updated"
- `per_page`: 20
2. **Filter by watermark:**
- If the actor has `last_synced_at`: keep only PRs with `updated_at` >= `last_synced_at`
- If the actor does NOT have `last_synced_at`: keep only PRs from the last 30 days
3. Record for each PR:
- `number`, `title`, `body` (description), `state` (open/closed), `merged` (bool)
- `user.login` (author)
- `updated_at`, `created_at`
- `head.sha` (latest commit SHA)
> **Best-effort:** If the MCP call fails for an actor (rate limit, private repo, invalid URL):
> - Log the error: "Actor X failed — reason"
> - Continue with the remaining actors
> - Do NOT abort the entire execution for one actor
> **Quick skip:** If no PRs were returned or all PRs are older than the watermark,
> log "Actor X — no relevant activity" and skip to the next actor.
Log: "Phase 2: N actors queried, M with relevant PRs, K without activity, J with errors."
---
## Phase 3 — Filter Noise
For each PR collected in Phase 2, apply noise filters:
### 3.1 Filter by author
Remove PRs from bots and automated tools:
- Author contains `[bot]` or `bot` in login (e.g.: `dependabot[bot]`, `renovate[bot]`, `github-actions[bot]`)
- Author is `dependabot`, `renovate`, `snyk-bot`, `greenkeeper`
### 3.2 Filter by title
Remove PRs with titles indicating automatic or irrelevant changes:
- Title starts with: `Bump `, `chore(deps)`, `build(deps)`, `Update dependency`
- Title contains: `version bump`, `dependency update`, `auto-merge`
- Title is just a version number (e.g.: `v1.2.3`, `1.2.3`)
### 3.3 Record result
For each actor, maintain a list of relevant PRs (post-filter).
If all PRs from an actor were filtered: log "Actor X — all PRs filtered (noise)" and skip.
Log: "Phase 3: N total PRs, M relevant after filter, K filtered as noise."
---
## Phase 4 — LLM Semantic Matching
### 4.1 Load topics and projects catalog
Use Glob + Read to collect:
**Topics (`<VAULT_PATH>/topics/*.md`, excluding `_template.md`):**
- `filename` (without extension)
- `title`
- `aliases`
- `status` (open, in-progress, completed, cancelled)
- `actors` (list of wikilinks)
- `objective`
- `category`
**Projects (`projects/*.md`, excluding `_template.md`):**
- `filename` (without extension)
- `name`
- `aliases`
- `status` (planning, active, blocked, completed)
- `related_actors` (list of wikilinks)
- `blockers`
- `action_items` (list with description, status)
- `progress`
Store as catalog for matching.
### 4.2 Prepare matching batch
For each actor with relevant PRs, build a block:
```
Actor: <actor-name> (<owner>/<repo>)
Relevant PRs:
- PR #<number>: "<title>" (state: <open|closed|merged>, author: <login>, date: <date>)
Description: <first 200 chars of body>
- PR #<number>: ...
```
### 4.3 Execute semantic matching
With the topics/projects catalog and the PR batch, analyze semantically:
For each relevant PR, determine:
1. **Correlation with topic/project:** Does the PR relate to an existing topic or project?
- Consider: PR title, description, originating actor, aliases of topics/projects
- Match by: theme (deprecation, feature, bugfix), mentioned system, aligned objective
- Do NOT match generically — require clear semantic relationship
2. **Status implication:** If there is a correlation, does the PR imply a status change?
- Merged PR in an actor listed in an "open" topic → suggests status "in-progress" or "completed"
- Merged PR that resolves a project blocker → suggests blocker removal
- Open PR for a feature in a "blocked" topic → suggests status "in-progress"
- Closed PR without merge → no status implication
3. **Change classification:**
- `status_hint` — suggested status (e.g.: "in-progress", "completed")
- `evidence` — evidence description (e.g.: "PR #42 merged implements feature X of topic Y")
- `confidence` — high, medium, low
- `entity_type` — "topic" or "project"
- `entity_name` — topic/project filename
**Matching rules:**
- Prioritize high confidence matches (PR title explicitly mentions the topic/project)
- Discard low confidence matches (vague correlation, only by domain)
- If no PRs from an actor have correlation: record "no correlation" and proceed
- Do NOT create new topics/projects — only correlate with existing ones
### 4.4 Classify results by action level
Separate correlations into two groups:
**HIGH confidence → automatic action:**
- Will be processed automatically in Phase 5 (without human confirmation)
- Criteria: PR title explicitly mentions the topic/project, OR the PR is in an actor
listed in the `actors`/`related_actors` frontmatter of the topic/project AND the PR theme
aligns with the topic's `objective` or the project's `progress`/`action_items`
**MEDIUM confidence → report only:**
- Will NOT be processed automatically
- Will be recorded in the final report (Phase 7) for human review
- Criteria: plausible semantic correlation but without explicit evidence
Log: "Phase 4: N correlations (P high confidence → action, Q medium confidence → report). R actors with activity without correlation."
---
## Phase 5 — Delegate to /bedrock:preserve and Update Actors
### 5.1 Compile list for /bedrock:preserve
Build the entity list in the format accepted by `/bedrock:preserve`.
**Include ONLY HIGH confidence correlations + actor activity.**
**For topics with HIGH correlation:**
```yaml
- type: topic
name: "topic-filename"
action: update
content: |
## GitHub Activity (sync-github YYYY-MM-DD)
| PR | Repo | Status | Evidence |
|---|---|---|---|
| #42 | billing-api | merged | Implements feature X |
> [!info] Suggested status: in-progress
> Based on PR #42 merged in billing-api that implements feature X.
> Automatically detected by sync-github@agent.
source: "github"
```
**For projects with HIGH correlation:**
```yaml
- type: project
name: "project-filename"
action: update
content: |
## GitHub Activity (sync-github YYYY-MM-DD)
| PR | Repo | Status | Evidence |
|---|---|---|---|
| #15 | orders-api | merged | Resolves blocker Y |
> [!info] Suggested status: active
> Based on PR #15 merged in orders-api that resolves blocker Y.
> Project status reflects a management decision — review this suggestion.
> Automatically detected by sync-github@agent.
source: "github"
```
**For actors with relevant activity (all, not just those with correlation):**
```yaml
- type: actor
name: "actor-name"
action: update
content: |
## Recent Activity (sync-github YYYY-MM-DD)
| PR | Title | Status | Author | Date |
|---|---|---|---|---|
| #42 | Feature X | merged | alice | 2026-04-10 |
| #34 | Refactoring Y | open | bob | 2026-04-09 |
source: "github"
```
**Compilation rules:**
- Content for topics/projects: append-only. Add "GitHub Activity" section with `[!info]` callout suggesting status. NEVER overwrite the `status` field directly.
- Content for actors: merge-ok. The "Recent Activity" section replaces the previous version (if it exists).
- `source: "github"` for all entities
- Bare wikilinks: `[[actor-name]]`, never `[[actors/actor-name]]`
### 5.2 Invoke /bedrock:preserve
Use the Skill tool to invoke `/bedrock:preserve --vault <VAULT_NAME>` passing the structured list as argument.
The `--vault <VAULT_NAME>` flag ensures preserve writes to the same vault.
> **IMPORTANT for background execution:** When invoking `/bedrock:preserve`, include in the
> instruction that `/bedrock:preserve` must also operate without human confirmation.
> Add to the prompt: "Autonomous mode — do not ask for confirmation, process directly."
`/bedrock:preserve` handles:
- Textual matching with existing entities
- Updating existing entities (merge/append-only)
- Bidirectional linking (wikilinks)
- Git commit of entities
### 5.3 Update actor watermarks
After `/bedrock:preserve` completes, update the frontmatter of EACH processed actor (with or without correlation):
1. Use Read to read the actor file
2. Use Edit to update in the frontmatter:
- `last_synced_at`: today's date (YYYY-MM-DD)
- `last_synced_sha`: SHA of the most recent commit from the most recent PR (or keep previous if no PRs)
- `updated_at`: today's date
- `updated_by`: `"sync-github@agent"`
3. If the fields `last_synced_at` and `last_synced_sha` do not exist: add them to the frontmatter (before `updated_at`)
### 5.4 Git commit of watermarks
```bash
git -C <VAULT_PATH> add actors/
git -C <VAULT_PATH> diff --cached --quiet && echo "Nothing to commit" && exit 0
```
#### Read git strategy
Read the vault's git strategy from `.bedrock/config.json`:
```bash
cat <VAULT_PATH>/.bedrock/config.json 2>/dev/null
```
Extract the `git.strategy` field. If the file does not exist or has no `git` key, default to `"commit-push"`.
Valid values: `"commit-push"`, `"commit-push-pr"`, `"commit-only"`.
Prepare the commit message following the convention:
```
vault(source): syncs github activity for N actors [source: github]
```
#### Dispatch by strategy
**Strategy: `commit-push`** (default)
```bash
git -C <VAULT_PATH> commit -m "<message per convention>"
git -C <VAULT_PATH> push origin main
```
If push fails (conflict):
```bash
git -C <VAULT_PATH> pull --rebase origin main
git -C <VAULT_PATH> push origin main
```
If it fails 2x: log the error and continue to the report.
If there is no remote: commit locally and log.
---
**Strategy: `commit-push-pr`**
First, check that `gh` is available:
```bash
which gh 2>/dev/null
```
If `gh` is not found: warn the user and **fall back to `commit-push`** strategy (above).
If `gh` is available:
1. **Create a branch.** Derive the branch name from the commit message:
`vault/<YYYY-MM-DD>-sync-github-<N>-actors` (e.g., `vault/2026-04-15-sync-github-5-actors`)
Check for collisions:
```bash
git -C <VAULT_PATH> branch --list "vault/<YYYY-MM-DD>-sync-github*"
```
If the branch already exists, append a counter: `vault/2026-04-15-sync-github-5-actors-2`.
```bash
git -C <VAULT_PATH> checkout -b <branch-name>
```
2. **Commit and push the branch:**
```bash
git -C <VAULT_PATH> commit -m "<message per convention>"
git -C <VAULT_PATH> push origin <branch-name>
```
3. **Open a pull request:**
```bash
cd <VAULT_PATH> && gh pr create --title "<commit message>" --body "Automated by /bedrock:sync" --base main
```
4. **Return to main:**
```bash
git -C <VAULT_PATH> checkout main
```
---
**Strategy: `commit-only`**
```bash
git -C <VAULT_PATH> commit -m "<message per convention>"
```
Do not push. Output:
```
Git strategy: commit-only — changes committed locally. Use `git push` manually when ready.
```
---
## Phase 6 — Generate Report
Generate a complete execution report and save as a fleeting note.
### 6.1 Build report content
```markdown
---
type: fleeting
name: "sync-github YYYY-MM-DD"
aliases: ["Sync GitHub YYYY-MM-DD"]
status: "raw"
updated_at: YYYY-MM-DD
updated_by: "sync-github@agent"
tags: [type/fleeting, status/raw]
---
# Sync GitHub — YYYY-MM-DD
> Automatic report generated by sync-github@agent.
## Summary
| Metric | Value |
|---|---|
| Actors found | N |
| Actors synchronized | N |
| Actors ignored (deprecated/no repo) | N |
| Actors with error (MCP) | N |
| PRs collected | N |
| Relevant PRs (post-filter) | N |
| PRs filtered (noise) | N |
| High confidence correlations (processed) | N |
| Medium confidence correlations (for review) | N |
| Actors with activity (no correlation) | N |
## Processed Correlations (High Confidence)
| Actor | PR | Entity | Type | Suggested Status | Evidence |
|---|---|---|---|---|---|
| [[billing-api]] | #42 | [[2026-04-feature-x]] | topic | in-progress | PR implements feature X |
| ... | ... | ... | ... | ... | ... |
## Correlations for Human Review (Medium Confidence)
> [!todo] Review correlations below
> These correlations were detected with medium confidence. Review and apply manually if correct.
| Actor | PR | Entity | Type | Suggested Status | Evidence |
|---|---|---|---|---|---|
| [[notification-service]] | #33 | [[2026-04-bugfix-timeout-notifications]] | topic | in-progress | PR title mentions timeout |
| ... | ... | ... | ... | ... | ... |
## Activity by Actor
| Actor | Relevant PRs | Summary |
|---|---|---|
| [[billing-api]] | #42 merged, #43 open | Feature X completed, refactoring Y in progress |
| [[notification-service]] | #33 merged | Timeout fix |
| ... | ... | ... |
## Updated Actors (Watermark)
| Actor | last_synced_at | last_synced_sha |
|---|---|---|
| [[billing-api]] | YYYY-MM-DD | abc1234 |
| ... | ... | ... |
## Errors
| Actor | Error |
|---|---|
| actor-x | MCP timeout |
| ... | ... |
## Git
- Commit (entities): <hash from /bedrock:preserve or "no entities">
- Commit (watermarks): vault(source): syncs github activity [source: github]
- Push: success / failed (reason)
```
### 6.2 Save report
Save the report to `<VAULT_PATH>/fleeting/YYYY-MM-DD-sync-github.md`.
If the file already exists (duplicate execution on the same day): overwrite with most recent data.
### 6.3 Git commit of report
```bash
git -C <VAULT_PATH> add fleeting/
git -C <VAULT_PATH> diff --cached --quiet && echo "Nothing to commit" && exit 0
```
#### Read git strategy
Read the vault's git strategy from `.bedrock/config.json`:
```bash
cat <VAULT_PATH>/.bedrock/config.json 2>/dev/null
```
Extract the `git.strategy` field. If the file does not exist or has no `git` key, default to `"commit-push"`.
Valid values: `"commit-push"`, `"commit-push-pr"`, `"commit-only"`.
Prepare the commit message:
```
vault(note): creates sync-github-report YYYY-MM-DD [source: github]
```
#### Dispatch by strategy
**Strategy: `commit-push`** (default)
```bash
git -C <VAULT_PATH> commit -m "<message per convention>"
git -C <VAULT_PATH> push origin main
```
If push fails (conflict):
```bash
git -C <VAULT_PATH> pull --rebase origin main
git -C <VAULT_PATH> push origin main
```
If it fails 2x: log the error and continue.
If there is no remote: commit locally and log.
---
**Strategy: `commit-push-pr`**
First, check that `gh` is available:
```bash
which gh 2>/dev/null
```
If `gh` is not found: warn the user and **fall back to `commit-push`** strategy (above).
If `gh` is available:
1. **Create a branch.** Derive the branch name:
`vault/<YYYY-MM-DD>-sync-github-report` (e.g., `vault/2026-04-15-sync-github-report`)
Check for collisions:
```bash
git -C <VAULT_PATH> branch --list "vault/<YYYY-MM-DD>-sync-github-report*"
```
If the branch already exists, append a counter.
```bash
git -C <VAULT_PATH> checkout -b <branch-name>
```
2. **Commit and push the branch:**
```bash
git -C <VAULT_PATH> commit -m "<message per convention>"
git -C <VAULT_PATH> push origin <branch-name>
```
3. **Open a pull request:**
```bash
cd <VAULT_PATH> && gh pr create --title "<commit message>" --body "Automated by /bedrock:sync" --base main
```
4. **Return to main:**
```bash
git -C <VAULT_PATH> checkout main
```
---
**Strategy: `commit-only`**
```bash
git -C <VAULT_PATH> commit -m "<message per convention>"
```
Do not push. Output:
```
Git strategy: commit-only — changes committed locally. Use `git push` manually when ready.
```
---
## Phase 7 — Finalize
Log final message:
```
sync-github@agent completed.
- Actors processed: N
- Correlations processed (high): N
- Correlations for review (medium): N
- Report: fleeting/YYYY-MM-DD-sync-github.md
```
**Execution ends here.** The agent does not wait for user response.
---
## Critical Rules
| # | Rule |
|---|---|
| 1 | **AUTONOMOUS MODE** — do NOT ask for user confirmation in any phase |
| 2 | **NEVER write entities directly** — all writing of topics/projects/actors goes through `/bedrock:preserve` |
| 3 | **NEVER create new topics or projects** — only update existing ones |
| 4 | **NEVER overwrite status** of topics/projects — only add a note with suggestion via `[!info]` callout |
| 5 | **Only HIGH confidence generates action** — medium confidence correlations go only to the report |
| 6 | **Best-effort for GitHub MCP** — never block due to rate limit or inaccessible repo |
| 7 | **MCP in main context** — do NOT use Agent tool for GitHub MCP calls |
| 8 | **Filter noise before matching** — dependabot, version bumps, bots |
| 9 | **Conservative semantic matching** — discard low confidence correlations |
| 10 | **Maximum 2 push attempts** — after that, log and continue |
| 11 | **Sensitive data** — NEVER include credentials, tokens, passwords, PANs, CVVs |
| 12 | **Frontmatter keys in English**, values in the vault's configured language |
| 13 | **Bare wikilinks** — `[[name]]`, never `[[dir/name]]` |
| 14 | **Append-only for topics** — add information, never delete existing content |
| 15 | **Report always generated** — even if no correlations, generate report in `<VAULT_PATH>/fleeting/` |
| 16 | **Vault resolution first** — resolve `VAULT_PATH` before any file operation or git command — never assume CWD is the vault |
| 17 | **All git commands use `git -C <VAULT_PATH>`** — never assume CWD is the vault |
| 18 | **All entity paths use `<VAULT_PATH>/` prefix** — `<VAULT_PATH>/actors/`, not `actors/` |
| 19 | **Pass --vault to /preserve** — ALWAYS include `--vault <VAULT_NAME>` when delegating to `/bedrock:preserve` |
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!