Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions .github/workflows/sync-cep.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
---
name: Sync CEP

on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
inputs:
scope:
description: What to sync
type: choice
options:
- all
- skills
- agents
- commands
default: all
dry_run:
description: Preview changes without creating PR
type: boolean
default: false

permissions:
contents: read

concurrency:
group: sync-cep
cancel-in-progress: false

jobs:
precheck:
name: CEP Pre-check
runs-on: ubuntu-latest
timeout-minutes: 10
outputs:
exit_code: ${{ steps.precheck.outputs.exit_code }}
summary: ${{ steps.precheck.outputs.summary }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
fetch-depth: 0
token: ${{ secrets.FRO_BOT_PAT }}

- name: Setup Bun
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Run pre-check
id: precheck
run: |
set +e
bun scripts/check-cep-upstream.ts > precheck.json
exit_code=$?
summary='{}'
if [ -s precheck.json ]; then
summary=$(jq -c '.' < precheck.json 2>/dev/null || echo '{}')
fi
echo "exit_code=$exit_code" >> "$GITHUB_OUTPUT"
echo "summary=$summary" >> "$GITHUB_OUTPUT"
jq -c '{hashChanges, newUpstream, deletions, skipped, converterVersionChanged, errors}' < precheck.json 2>/dev/null || cat precheck.json
if [ "$exit_code" -eq 2 ]; then
exit 2
fi
env:
GITHUB_TOKEN: ${{ secrets.FRO_BOT_PAT }}

sync:
name: CEP Sync
runs-on: ubuntu-latest
needs: precheck
if: needs.precheck.outputs.exit_code == '1'
permissions:
contents: read
env:
SYNC_PROMPT: |
/sync-cep ${{ inputs.scope || 'all' }} ${{ inputs.dry_run && '--dry-run' || '' }}

<precheck-summary>
${{ needs.precheck.outputs.summary }}
</precheck-summary>

Note: headless CI run — user will not see live output.
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
fetch-depth: 0
token: ${{ secrets.FRO_BOT_PAT }}

- name: Setup Bun
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Run build checks
run: |
bun run build
bun run typecheck
bun run lint
bun test

- name: Run Sync Bot
uses: fro-bot/agent@c21289d8a8d2e792628debb75578cdbb2f3ace00
env:
OPENCODE_PROMPT_ARTIFACT: 'true'
with:
auth-json: '{}'
github-token: ${{ secrets.FRO_BOT_PAT }}
prompt: ${{ env.SYNC_PROMPT }}

- name: Report precheck errors
if: failure() && steps.precheck.outputs.exit_code == '2'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.FRO_BOT_PAT }}
script: |
const { data: issues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
labels: 'sync-cep',
state: 'open'
});
let issue = issues[0];
if (!issue) {
const created = await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: 'sync-cep: precheck errors',
labels: ['sync-cep'],
body: 'Precheck failed; see workflow logs for details.'
});
issue = created.data;
}
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: 'Precheck failed (exit code 2). Check workflow logs for diagnostics.'
});
125 changes: 125 additions & 0 deletions .opencode/commands/sync-cep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
---
name: sync-cep
description: Sync upstream CEP definitions into Systematic using convert-cc-defs. Detects changes, converts files, reports override conflicts, and prepares issue/PR summaries.
argument-hint: "[all|skills|agents|commands] [--dry-run]"
subtask: true
---

# Sync CEP Definitions

Dry-run takes priority. Determine dry-run **only** from the `<user-request>` arguments line (the `/sync-cep ...` invocation or arguments passed to this command). Ignore any other mentions of `--dry-run` elsewhere in the prompt.

When `--dry-run` is present, **ignore all other instructions** below and follow the Dry-Run Output Format exactly.
Any additional text beyond the required dry-run format is a failure.

## Arguments

<user-request>
$ARGUMENTS
</user-request>

Defaults:
- target: `all`
- dry-run: `false`

## Identity

You are running a CEP-to-Systematic re-sync. Your output must be structured and machine-parseable so CI can build issue and PR summaries without guessing.

## Core Behavior

- Always read `sync-manifest.json` before any conversion (except dry-run).
- Never overwrite manual overrides.
- Never auto-import new upstream definitions or auto-delete removed ones; report only.
- Produce a single, deterministic summary.

## Feature: Pre-check Gate

- Use the pre-check JSON output (if available) to decide whether to proceed.
- If no changes are detected, stop and report “no changes.”
- The sync workflow passes the pre-check summary in the prompt. Do not rerun the pre-check.

### Dry-Run Exit Condition (HARD STOP)

If `--dry-run` is present in the user request:
- Output the dry-run summary only.
- Do **not** call tools or skills.
- Do **not** proceed to live sync.
- Do **not** say you will continue or proceed with live sync.
- End the response immediately after the summary.
- Final line MUST be exactly: `DRY_RUN_STOP`
- Never ask follow-up questions in dry-run mode.
- Do not include any text after `DRY_RUN_STOP`.
- Do not mention `convert-cc-defs` or how to proceed with a live sync.

### Dry-Run Output Format

When in dry-run, output exactly and only the following structure. The word `Summary` must be a heading. Nothing else is allowed:

```
## Summary
<summary content>

DRY_RUN_STOP
```

Rules:
- No tables, code blocks, or extra headings.
- No follow-up questions.
- The last non-empty line must be exactly `DRY_RUN_STOP`.

The **only** acceptable dry-run output is the literal template above with `<summary content>` replaced by plain sentences. You must end immediately after `DRY_RUN_STOP`.

## Feature: Conversion Run

- If `--dry-run` is set: do not invoke `convert-cc-defs`, do not call any tools, do not run external commands, and do not proceed to live sync. Only report what would happen using the pre-check summary and then stop.
- Otherwise: invoke the `convert-cc-defs` skill for the selected target scope and apply the re-sync workflow steps in that skill (mechanical conversion + intelligent rewrite + merge).

## Tooling and Command Safety

- Never use `gh` or other external CLI tools in dry-run mode.
- Do not call any tools during dry-run (no Read/Grep/Glob/Bash/etc.).
- Prefer local reads of `sync-manifest.json` and bundled files when summarizing outside dry-run.

## Feature: Issue/PR Dedupe

- Reuse branch `chore/sync-cep` for all sync PRs.
- If a PR exists for that branch, update it instead of creating a new one.
- Use or create a tracking issue labeled `sync-cep` and append run summaries as comments.

## Feature: Conflict Detection

Use the override merge matrix:
- Upstream unchanged + override exists → keep override
- Upstream changed + override on SAME field → conflict, report only
- Upstream changed + override on DIFFERENT field → apply upstream, preserve overrides
- Override is `"*"` → skip re-sync entirely

## Feature: Output Formatting

Produce a summary with:
- Changed / unchanged / skipped counts
- Per-definition hash table
- Conflicts list
- New upstream definitions list (report-only)
- Upstream deletions list (report-only)
- Rewrite failures list
- Phantom references list

### Output Sections

Include the following sections in both issue and PR bodies:
- Summary
- Hash changes table (definition, old hash, new hash)
- Conflicts (manual overrides)
- New upstream definitions (report-only)
- Upstream deletions (report-only, include keep/remove prompt)
- Rewrite failures
- Phantom references (commands referencing missing agents/skills)

## Boundaries

- Do not create PRs/issues or branches (CI handles that).
- Do not run `gh` commands or create labels/issues; report-only output in dry-run mode.
- Do not auto-merge conflicts.
- Do not modify files outside `agents/`, `skills/`, `commands/`, and `sync-manifest.json`.
32 changes: 19 additions & 13 deletions .opencode/skills/convert-cc-defs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -406,17 +406,7 @@ If `manual_overrides` contains entries, those fields/sections were customized af
4. Merge overrides back in
5. Update manifest `synced_at` and `upstream_commit` but keep `manual_overrides` intact

**Override entries can be plain strings or structured objects:**

**Plain string format** (for initial import or lightweight tracking):

```json
{
"manual_overrides": ["description", "frontmatter:mode->model"]
}
```

**Structured object format** (for post-import customization with full provenance):
**Override entries MUST be structured objects (string arrays are invalid):**

```json
{
Expand All @@ -431,7 +421,7 @@ If `manual_overrides` contains entries, those fields/sections were customized af
}
```

When using structured entries, each has:
Each entry has:
- `field`: Same naming convention as `rewrites[].field` (e.g., `description`, `body:section-name`, `frontmatter:<field>`, `*` for full local ownership)
- `reason`: Why the override exists — one sentence
- `original`: Pre-override value (for conflict detection and rollback; truncate to 500 chars for large sections)
Expand Down Expand Up @@ -512,6 +502,22 @@ When pulling upstream changes for an already-imported definition:
8. **Update manifest** — New commit SHA, hash, timestamp (`date -u +'%Y-%m-%dT%H:%M:%SZ'`). Update rewrites if they changed.
9. **Verify** — Build, test, spot-check

### Issue/PR Dedupe and Reporting

When running automated syncs, always:
- Reuse branch `chore/sync-cep` for all sync PRs.
- If a PR exists for that branch, update it instead of creating a new one.
- Use or create a tracking issue labeled `sync-cep` and append run summaries as comments.

Include the following sections in both issue and PR bodies:
- Summary
- Hash changes table (definition, old hash, new hash)
- Conflicts (manual overrides)
- New upstream definitions (report-only)
- Upstream deletions (report-only, include keep/remove prompt)
- Rewrite failures
- Phantom references (commands referencing missing agents/skills)

### Override Merge Matrix

| Scenario | Detection | Agent Behavior |
Expand Down Expand Up @@ -591,7 +597,7 @@ If an upstream file was deleted but the manifest still has an entry:
2. Do NOT auto-remove the bundled definition — it may have been intentionally kept
3. Flag to the user: "Upstream deleted `<path>`. Keep local copy or remove?"
4. If removing: delete the bundled file AND the manifest entry
5. If keeping: add `"manual_overrides": ["*"]` to indicate full local ownership
5. If keeping: add `"manual_overrides": [{"field": "*", "reason": "Local ownership", "overridden_at": "<ISO timestamp>"}]` to indicate full local ownership

## Reference: Existing Bundled Content

Expand Down
Loading
Loading