sync-architecture · v17 · 2026-09-30 · sha256 8ec6f31798cd4fc5
sync-architecture v17A
Immutable. This exact content never changes and is served at /api/v1/blob/8ec6f31798cd4fc5.
---
name: sync-architecture
description: "Use when the user mentions syncing architecture, pulling template updates, updating parcel machinery, 'sync tools', 'pull latest skills/agents', or wants this workspace's .devops machinery refreshed from the template repo. Runs scripts/pull-architecture.ps1 against the current workspace root and reports drift."
version: 17
updated: 2026-10-01
---
# Sync Architecture (`sync-architecture`)
Refresh this satellite workspace's transportable machinery (skills, agents, rules, templates,
scripts, `.vscode`) from the template repo recorded in `.ptp-source`. Thin wrapper over
`scripts/pull-architecture.ps1`, which delegates to the template's `sync-architecture.ps1`.
## Workflow
1. **Identify the workspace root** (cwd). Refuse if no `.devops/` directory exists there —
that is the wrong kind of workspace; tell the user to bootstrap from the template first
(see `.devops/templates/SATELLITE-BOOTSTRAP.md`).
2. **Run the pull script** with the switch matching the user's intent:
| User asks | Command |
|---|---|
| "what's new" / "check" / "is anything outdated" | `powershell -NoProfile -File scripts\pull-architecture.ps1 -Check` |
| "preview" / "show me what would change" | `powershell -NoProfile -File scripts\pull-architecture.ps1 -DryRun` |
| "sync" / "update machinery" / "pull latest" | `powershell -NoProfile -File scripts\pull-architecture.ps1` |
| "verify" / "is my satellite wired up correctly" / "check my bootstrap" | `powershell -NoProfile -File scripts\pull-architecture.ps1 -Verify` |
Never pass `-Source` unless the user explicitly supplies one — `.ptp-source` remembers it.
If no `.ptp-source` exists, relay the script's hint: run once with `-Source <path-or-git-url>`.
2b. **After a real sync — reconcile the authored files with the changed seeds.** The pull
copies `.devops/templates/**`; it never touches the files a satellite authored from them.
For the seeds the pull changed, review each authored counterpart:
- **Detect.** `git status --short -- .devops/templates/`, then `git diff -- .devops/templates/`
— that working-tree change set is what the pull just wrote. If git history is unavailable,
or seeds changed in earlier pulls without this review, compare each seed against its
authored counterpart directly.
- **Map.** The seed→authored table is `.devops/templates/SATELLITE-BOOTSTRAP.md` § 2 — read
it; review only files this workspace actually adopted (the backlog registers are optional).
- **Apply.** Sections the seed marks `<!-- MACHINERY: keep verbatim -->` land verbatim in the
authored file; `CUSTOMIZE` blocks stay satellite-owned. Leave the derived regions the engine
already reconciles (the `opencode.json` `agent` entries, the `skills` key, Model Registry
rows, model strips) to the engine.
- **Report.** Present what changed in the seed and what you applied (or deliberately kept);
ask before anything ambiguous. A declined change is reported, never silently dropped.
3. **Interpret the verdict table** (`-Check` output) in plain language:
| Verdict | Meaning | Action |
|---|---|---|
| `CURRENT` | identical | none |
| `UPGRADE` | newer version upstream | safe to pull |
| `DRIFT` | same version, different bytes | **locally customized copy — do not blindly overwrite; ask the user.** Offer to show the diff before re-running without `-Check`. A `DRIFT` on a portable file whose bytes changed under an unchanged `machinery-version` is **expected while the template's wrap-up window is open** — it closes at the template's own wrap-up — and the pull's diff is the safe check |
| `MISSING` | never installed here | will be created by a sync |
| `SOURCE-ABSENT` | manifest bug in the template | report to the template repo owner |
| `PRUNE` | retired upstream but present in the target | a sync will delete it — **counts as out of sync** |
| `AHEAD` | target's counter is ahead of the source | **halt-and-reconcile** — the `commonest cause` is a satellite-side bump of a counter it does not own (inert: stamped back down on the next pull); the sync refuses to write |
| `MIGRATION` | the two counters crossed lineage shape (integer vs dotted) | **halt-and-reconcile** by hand before pulling; the sync refuses to write |
Summarize counts + the IN SYNC / OUT OF SYNC line. Exit code 1 = out of sync (any
`UPGRADE`/`DRIFT`/`MISSING`/`SOURCE-ABSENT`/`PRUNE`/`AHEAD`/`MIGRATION` verdict). A retired file reports
`PRUNE` on its own line, never a parent-directory `DRIFT`.
3b. **Interpret `-Verify` output** (never writes; exit 0 = `VERIFIED`):
- `[FAIL]` rows name exactly what is missing or mis-wired: `AGENTS.md` machinery
markers, `opencode.json`'s `skills` array (it must be the V2-native **flat array** and
declare `.devops/skills`), `base-context.md`, the wiki anchor
(`.wiki/core/00-system-index.md`), or machinery a sync should have materialised.
Fix the named item, then re-run.
- A shape-broken `opencode.json` gets its own `[FAIL]`, reading
`opencode.json is not valid JSON: …` and naming the defect — a comma left dangling before
a closing brace or bracket, or braces/brackets that do not balance. The same
host-monotone verdict the write-guard uses gates this check, so a config one host's parser
would accept is never reported valid on this one. The engine's own `-SelfTest` asserts the
row against a real config, and its child-failure output shape is stated once in
`scripts/sync-architecture.ps1`'s header contract — cite it, do not restate it.
- `[WARN]` rows are advisory only (e.g. `.ptp-source` not yet recorded) and never fail
the run.
- Machinery gates run check-only (prefix check without `-Sync`, UTF-8, wiki lint).
A gate failure means the machinery itself is broken — report verbatim.
4. **First-time satellite** (many `MISSING` rows): after syncing, tell the user which
repo-specific files still need authoring from the seed templates in `.devops/templates/`:
`AGENTS.md`, `opencode.json`, `.opencode/plans/base-context.md` — then
`scripts\check-parcel-prefix.ps1 -Sync` once parcel agents exist.
## Workflow Step 5 — Report Parcel Feedback Upstream
The reverse direction of the sync: when a satellite agent spots an issue or improvement
meant for the **template** repo mid-session (a machinery defect, a skill gap, a doc drift),
do not hold it in conversation — draft it where the operator can carry it in one action.
1. **Invoke the step** when the finding is template-worthy (it would change a portable
surface: `.devops/**`, rule layers, scripts, templates). Satellite-local app concerns
stay satellite-local — the fold-candidates protocol (hold, do not edit the template
locally) is the *stance*; this outbox is its *transport*.
2. **Draft the item** into a `feedback/` directory at the satellite repo root — one `.md`
file per finding, shaped exactly as this template's parked plan so the hand-carry is
copy-only:
```
feedback/<slug>-feedback.md
---
code: TBD # template assigns the stable T{theme}-E{epic}.{impl} at registration
type: backlog
claim_status: QUEUED # field + enum canon: .devops/rules/plan-lifecycle.md § Claim Front-Matter
# Backlog: <title>
**Discovered:** <date> — one sentence of context.
## Findings
| # | Gap | Evidence | Impact |
## What the fix does
<the operator's/actionable intent, or "open">
## Open question
<what the template side must decide, if anything>
```
3. **Operator carry contract** (human-gated, no automation): at the next template sync,
the operator hand-carries each outbox item into the template repo — copy it as
`.devops/backlog/<code>-<slug>-backlog.md`, register it in the owning theme register
and the Triage Panel via `@backlog`, then delete the outbox item. "Reviewed" = triaged
to a tier (🔴/🟡/🟢/⚪) or committed to a sprint by `@sprint-plan`.
### Feedback rules (step 5)
- **No secrets/credentials in feedback items** — the portable surface is secret-free; a
drafted item travels through operator hands and must carry findings and evidence only.
- **The outbox is a message queue, not a plan queue** — lifecycle states apply only once
the item is landed and claimed in a template repo; a `feedback/` file in the satellite
is debris, and a sync legitimately leaves it untouched (target-only file survives).
- `ponytail:` one shape, no per-item template file shipped — the skill carries the draft
shape inline instead of adding a template artefact, and items carry a placeholder code
rather than inventing satellite-side code space.
## Rules
- `-Check` never writes; it is always safe to run first. Prefer starting any sync request with
`-Check` and presenting findings before mutating.
- A real sync regenerates PREFIX-LOCKED agent prefixes from *this* workspace's `base-context.md`
and runs the verification gates (prefix, UTF-8, wiki lint). Report gate failures verbatim —
never suppress them with `-NoVerify` unless the user insists.
- **Registry propagation, model stripping & config key-shape migration.** A sync reconciles the
target's `base-context.md` registry capability-class rows (inserts rows the target lacks, prunes
rows for retired keys — rows only, prose untouched), **removes every `model` binding it finds and
never stamps one** (T1-E1.04), and migrates the target's `skills` key to the V2-native flat array
— every write JSON-validated and reverted rather than shipped unparsable, and a shape it cannot
rewrite safely is refused with a printed operator remedy. One exception is structural, not a
model: the locked-preset host's `permission.task` allow-list is still stamped from the seed
(see the step-4 comment in `scripts/lib/sync-bindings.ps1`). Full semantics live in
`.devops/rules/agents-and-skills.md` § Sync Protocol — cited here, never restated.
- **Pull semantics are merge-by-name, not mirror and not additive.** The engine copies portable surfaces with `Copy-Item $s\* $t -Recurse -Force`: a file present in **both** repos is **replaced wholesale** (target-side edits to a portable file are lost — this is why the fold review in `@sprint-close` §8 is template-side), while a file present in the **target only** survives untouched (satellite-only residue is never pruned — "sync" can leave local files behind). Neither "mirror" nor "additive" predicts both; read every pull verdict with this model.
- **Authored files are never copied; a changed seed is a review trigger.** The pull materialises `.devops/templates/**` only — the files a satellite authors from it (`AGENTS.md`, `opencode.json`, `.opencode/plans/base-context.md`, the optional backlog registers) are reconciled by the agent per step 2b, never overwritten by the engine. The engine-owned derived regions (registry rows, model strips, the `skills` key) stay the engine's, and the review leaves them alone.
- A retired file inside a portable skill reports `PRUNE`, never a parent-skill `DRIFT`:
declare it in `prune_files` like any other retirement — the parent-dir mask covers skills.
- **CI gates travel as a self-contained workflow file, never `workflow_call`, a composite action, or "documented wiring."** The invariant gates (prefix integrity, UTF-8, wiki lint, coverage, claims drift) must run in every satellite, and a template cannot enforce anything through a **second repo** at runtime or a documented block. Ship them as a plain portable `.github/workflows/machinery-gates.yml` (the `wiki-refresh.yml` precedent) riding `portable_files` — no secret, no wiring step. Its header states the **invariant-everywhere / template-identity** split once, and the template's own superset cites that split instead of restating it. *(process-lessons, 2026-09-25)*
- **Version discipline.** A skill's `version` MUST bump with any `SKILL.md` or reference change.
Without the bump `-Check` reports `DRIFT` ("locally customized?") on stale machinery instead
of `UPGRADE`, and operators learn to overwrite real local edits. After any machinery edit in the template repo itself, the wrap-up discipline bumps per-skill
`version` / `machinery-version`; this skill only consumes those numbers, never edits them.
The counter is **template-owned and sync-stamped — a satellite never bumps it**
(`.devops/rules/plan-lifecycle.md` § Claim Protocol → *Counter Ownership*), and a
satellite-side bump of a counter it does not own is the `commonest cause` of an `AHEAD`
halt — the bump is inert and is stamped back down on the next pull.