Skip to content

feat(profile): single-source-of-truth sin-profile.md + per-agent renderers (Claude/Codex/Gemini/Cursor/Windsurf/Cline/Copilot) + CI sync #175

Description

@Delqhi

Summary

Generate the per-agent profile files (.claude-plugin/, .codex/, AGENTS.md, GEMINI.md, .cursor/rules/, .windsurf/rules/, .clinerules/, .github/copilot-instructions.md, …) for SIN-Code and for any project that uses SIN-Code, from a single source of truth (docs/agent-profiles/sin-profile.md). Inspired by JuliusBrussee/caveman's src/rules/caveman-activate.md + the per-agent install code in bin/install.js:

We removed the agent-specific dotdir mirrors at the repo root (.cursor/, .windsurf/, .clinerules/, .github/copilot-instructions.md, root caveman/SKILL.md). They were never read by the installer — only used to self-apply caveman to this repo when a maintainer opened it in Cursor/Windsurf/Cline. ... src/rules/caveman-activate.md is the single source of truth. ... Don't re-add per-agent mirrors.

For SIN-Code, we currently have only AGENTS.md at the repo root. CLAUDE.md, GEMINI.md, .claude-plugin/manifest.json, .cursor/, .windsurf/, .clinerules/, .github/copilot-instructions.md are all absent. This issue generates all of them from one template.

Motivation

Caveman's distribution insight is that per-agent profile files are all variations of the same content. The README frames this as a maintenance burden:

A handful of dotdir leftovers (.junie/, .kiro/, .roo/, .agents/) still hold a stale cavecrew/SKILL.md mirror from before the cleanup. They aren't read by anything in the current install path; remove on sight, no migration needed.

The fix is the caveman-activate.md single source of truth + a per-agent render step. From the CLAUDE.md:

Auto-generated / auto-synced — do not edit directly

File Synced from
plugins/caveman/skills/caveman/SKILL.md skills/caveman/SKILL.md
agents/cavecrew-*.md agents/cavecrew-*.md
dist/caveman.skill ZIP of skills/caveman/ directory (gitignored; rebuilt by CI on release)

The CI workflow (.github/workflows/sync-skill.yml) does the actual syncing on every push to main. The "single source of truth" pattern is enforced by tooling, not by convention.

For SIN-Code, the use case is:

  1. Self-hosting: when a maintainer opens the SIN-Code repo in Cursor/Windsurf/Cline, the agent should know the project's rules. Currently, only AGENTS.md is read (by Codex and opencode); the other agents read nothing.
  2. Per-project: a project that uses SIN-Code as an MCP server should have its own AGENTS.md / CLAUDE.md that tells the agent how to use SIN-Code. We currently have no command that generates this.

This issue builds:

  • A single template (docs/agent-profiles/sin-profile.md).
  • A sin-code profile render command that emits the per-agent files.
  • A CI workflow (.github/workflows/agent-profile-sync.yml) that auto-syncs on push to main.

Current State in SIN-Code

Area File Lines What is there
AGENTS.md AGENTS.md 1+ The single source of truth for the SIN-Code repo. Read by Codex and opencode.
CLAUDE.md — — Does not exist.
GEMINI.md — — Does not exist.
.claude-plugin/manifest.json — — Does not exist.
.cursor/ — — Does not exist.
.windsurf/ — — Does not exist.
.clinerules/ — — Does not exist.
.github/copilot-instructions.md — — Does not exist.
profiles/ profiles/fireworks.toml, profiles/qwen-relay.toml — LLM agent profiles (model + base URL), not agent-target profiles.
scripts/ scripts/ci-precheck.sh, scripts/validate_skill.py, scripts/org-cleanup.sh, scripts/promote-to-sin-code.sh — Helper scripts; not for profile rendering.
CI workflows .github/workflows/ — ceo-audit.yml, sin-code-release.yml, ecosystem-sync.yml. No agent-profile-sync.yml.
AGENTS.md §10 (Naming and stability rules) AGENTS.md:359+ — Already documents the bundled-skill naming convention.

What this tells us: SIN-Code has one profile file. It does not have the render step, the template, or the CI sync.

What caveman does (precise)

Caveman's src/rules/caveman-activate.md is the single source of truth for the "always-on" rule content. The per-agent installers (in bin/install.js) read this file and render the agent-specific output:

Agent Output path Render strategy
Claude Code Plugin distribution in plugins/caveman/skills/<name>/SKILL.md Direct copy + frontmatter merge (version, always: true)
Codex .codex/hooks.json + .codex/config.toml Generated by bin/install.js
Gemini GEMINI.md context file Generated by the extension
opencode ~/.config/opencode/plugins/caveman/plugin.js ESM Bun module
OpenClaw ~/.openclaw/workspace/skills/caveman/SKILL.md + marker-fenced block in SOUL.md Generated by bin/lib/openclaw.js
Cursor .cursor/rules/caveman.mdc Generated by src/tools/caveman-init.js
Windsurf .windsurf/rules/caveman.md Same
Cline .clinerules/caveman.md Same
Copilot .github/copilot-instructions.md + AGENTS.md Same

The CI workflow (.github/workflows/sync-skill.yml) does the auto-syncing. The maintainer never edits the per-agent files directly.

Detailed Implementation Plan

Phase 1 — The template

  1. Create docs/agent-profiles/sin-profile.md (new file). This is the single source of truth. It contains the project's rules in a vendor-neutral Markdown that can be rendered to every agent format. The structure is:

    <!-- sin-code:profile v1 -->
    # SIN-Code Project Profile
    
    ## Identity
    <name, version, repo>
    
    ## Hard mandates
    - Single binary (M2)
    - Verification gate (M3)
    - Permission engine (M4)
    - Module path (M5)
    ...
    
    ## Architecture
    <subsystem list, command list>
    
    ## Conventions
    <commit format, branch format, CI gates>
    
    ## Bundled skills
    <table of 34 skills, categories>
    
    ## Lifecycle hooks
    <24 events>
  2. Define a frontmatter schema for the template:

    ---
    sin-code-profile: 1
    name: sin-code
    ---

    The version is bumped when the template changes in a breaking way.

Phase 2 — Per-agent renderers

  1. Create cmd/sin-code/internal/profile/render.go (new package). Public surface:

    type Target struct {
        ID        string // "claude-code" | "codex" | "gemini" | "opencode" | "cursor" | ...
        Path      string // target file or dir
        Format    string // "rule" | "context" | "manifest" | "plugin"
        Frontmatter map[string]string // optional, e.g. {"always": "true", "version": "v3.17.0"}
    }
    var Targets = []Target{ ... }
    func Render(template []byte, t Target) ([]byte, error)
    func RenderAll(template []byte) (map[string][]byte, error)
  2. Implement the 10 renderers:

    Target Renderer Output
    claude-code RenderClaudePlugin .claude-plugin/manifest.json + CLAUDE.md
    codex RenderCodex .codex/config.toml + AGENTS.md (already exists)
    gemini RenderGemini GEMINI.md
    opencode RenderOpencode AGENTS.md (already exists) + ~/.config/opencode/plugin/ entries
    cursor RenderCursor .cursor/rules/sin-code.mdc
    windsurf RenderWindsurf .windsurf/rules/sin-code.md
    cline RenderCline .clinerules/sin-code.md
    copilot RenderCopilot .github/copilot-instructions.md (append, marker-fenced)
    agents (generic) RenderGeneric AGENTS.md (already exists)
    openclaw RenderOpenClaw ~/.openclaw/workspace/skills/sin-code/SKILL.md + marker-fenced block in SOUL.md
  3. For the Cursor .mdc format, the renderer wraps the template in a --- frontmatter with the rule's metadata (description, globs, alwaysApply). This is the format Cursor reads.

  4. For the Copilot context file, the renderer appends a marker-fenced block:

    <!-- sin-code:profile v1 begin -->
    <rendered profile>
    <!-- sin-code:profile v1 end -->
    

    The marker is the unit of idempotency (mirrors caveman's marker-fence pattern).

  5. For the Claude plugin manifest, the renderer writes a manifest.json with the plugin's name, version, and a skills array pointing at the bundled skills.

Phase 3 — CLI

  1. Add sin-code profile subcommand in cmd/sin-code/profile_cmd.go:

    • sin-code profile render --target <id> — render one target.
    • sin-code profile render --all — render every target.
    • sin-code profile render --target cursor --project /path/to/repo — render into a different repo (for projects that use SIN-Code).
    • sin-code profile render --dry-run — print to stdout instead of writing.
    • sin-code profile list — list the supported targets.
    • sin-code profile check — verify that all the per-agent files are in sync with the template (CI gate).
  2. Add sin-code profile init that creates a fresh docs/agent-profiles/sin-profile.md in the current project (for projects that want to use SIN-Code).

  3. Wire the renderer into the sin-code install command (issue feat(install): add sin-code install subcommand + 30-line curl|bash shim + PowerShell equivalent #170). The one-line installer should also render the per-agent profile files.

Phase 4 — CI

  1. Add .github/workflows/agent-profile-sync.yml (new file). On every push to main when docs/agent-profiles/sin-profile.md changes:

    • Run sin-code profile render --all --dry-run.
    • Compare the rendered output to the committed per-agent files.
    • If they differ, push a follow-up commit to main with [skip ci].
  2. Add a profile-sync check to the ci-precheck script (already exists at scripts/ci-precheck.sh). The script runs locally and fails if the profile is out of sync.

Phase 5 — Tests

  1. Unit test for each renderer with a fixture template:

    • Output contains the marker fence.
    • Output preserves code, URLs, paths.
    • Idempotent re-render produces byte-identical output.
  2. Integration test for sin-code profile check:

    • Render once, commit the output.
    • Modify the template, run check, assert it fails.
    • Re-render, run check, assert it passes.
  3. Race test: go test -race -count=1 ./cmd/sin-code/internal/profile/... must pass.

Phase 6 — Docs

  1. docs/agent-profiles.md: the template, the targets, the CI sync, the per-project init.
  2. Update AGENTS.md §6 to list the new internal/profile/ package and the sin-code profile command.
  3. Update README.md to mention the new command.
  4. Update CHANGELOG.md Unreleased section.

Acceptance Criteria

  • docs/agent-profiles/sin-profile.md exists with the v1 schema.
  • cmd/sin-code/internal/profile/render.go exists with 10 renderers.
  • sin-code profile render --all writes the per-agent files into the right paths.
  • sin-code profile check fails when the template is out of sync.
  • .github/workflows/agent-profile-sync.yml is committed and the CI step is green.
  • sin-code profile init creates a fresh template in a new project.
  • docs/agent-profiles.md exists.
  • All unit and integration tests pass with -race.
  • golangci-lint, govulncheck, gosec (SARIF) green.

Risk and Rollback

  • Risk: the per-agent files diverge from the template because a developer edits one directly. Mitigation: the profile check and the CI sync enforce it; the AGENTS.md rule "do not edit the per-agent files" is documented in the template itself.
  • Risk: Cursor's .mdc format is unstable and may change. Mitigation: the renderer reads the frontmatter schema from a separate cmd/sin-code/internal/profile/cursor.go file; if Cursor changes, we update one file.
  • Risk: a project's AGENTS.md is generated automatically and the user wants to edit it. Mitigation: the renderer is opt-in (sin-code profile render); committed files are not auto-overwritten unless the user re-runs.
  • Rollback: revert the PR. The per-agent files are new artifacts; deleting them is harmless.

References

Activity

  1. Delqhi commented on Jun 16, 2026

    @Delqhi
    CollaboratorAuthor

    Ergänzung: ponytail-Inspiration (v4.7.0)

    docs/agent-profiles/sin-profile.md sollte ponytail's AGENTS.md-Pattern übernehmen:

    ponytail's AGENTS.md ist 31 Zeilen lang und enthält:

    • Persona ("You are a lazy senior developer")
    • Die 6-stufige Leiter (YAGNI, stdlib, native, dep, one-line, minimum)
    • 8 kurze Regeln (No abstractions, No new dep, No boilerplate, Deletion > Addition, etc.)
    • Hard Constraints ("Not lazy about: input validation, error handling, security, accessibility, calibration")

    Das ist die perfekte Vorlage für unseren sin-profile.md. Aktuell ist AGENTS.md (im SIN-Code-Root) sehr lang (379 Zeilen), weil es die gesamte Repository-Dokumentation enthält. ponytail zeigt, dass eine fokussierte Datei besser funktioniert.

    Konkrete Empfehlung für docs/agent-profiles/sin-profile.md:

    Gliederung (maximal 100 Zeilen):

    <!-- sin-code:profile v1 -->
    
    # SIN-Code Agent Profile
    
    ## Identity
    SIN-Code: verification-first coding agent. One Go binary, 44+ MCP tools,
    34 bundled skills, hard mandate on verification.
    
    ## Hard Mandates (NEVER violate)
    - M1: CI runs only on n8n, never on GH Actions
    - M2: single static Go binary, CGO_ENABLED=0
    - M3: verification gate before any "done"
    - M4: permission engine gates everything destructive
    - M5: module path github.com/OpenSIN-Code/SIN-Code
    - M6: SIN tools over naive built-ins
    - M7: race-free concurrency
    
    ## Working Style
    - Read the file before editing
    - Match existing patterns
    - Test what you break
    - Document only what changed
    
    ## Code Quality (sin-debt: convention)
    Mark shortcuts with // sin-debt: <ceiling>, upgrade: <trigger>
    (see skill-code-lazy for the full lazy-senior-dev ruleset)
    
    ## Subagent Contracts
    - Critic/Reviewer: L<line>: <tag> <problem>. <fix>.
    - Adversary: <path>:<line> — <attack-kind> — <hypothesis>
    - Governor: <path>:<line> — <rule-id> — <note>
    - Debt scanner: <path>:<line> — <ceiling> — <upgrade>
    
    ## Output Style
    Default: normal (the spec). On `verbosity: terse` or `ponytail: full`:
    - Drop pleasantries
    - Fragments OK
    - Code first, then 1-3 lines of context
    - Never essay-length

    Per-Agent-Adapter für ponytail's 13 Agenten (siehe #169):

    ponytail's docs/agent-portability.md:5-50 listet 13 Adapter. Wir können diese 1:1 für unseren sin-profile.md übernehmen:

    adapter_targets:
      claude-code:        CLAUDE.md  # full markdown
      codex:              AGENTS.md  # same as claude but no frontmatter
      opencode:           AGENTS.md  # via opencode.json plugin
      gemini-cli:         GEMINI.md  # via gemini-extension.json
      cursor:             .cursor/rules/sin-code.mdc
      windsurf:           .windsurf/rules/sin-code.md
      cline:              .clinerules/sin-code.md
      copilot-editor:     .github/copilot-instructions.md
      copilot-cli:        .github/copilot-instructions.md
      kiro:               .kiro/steering/sin-code.md
      aider:              CONVENTIONS.md
      openclaw:           ~/.openclaw/workspace/SOUL.md  # marker-fenced
      pi:                 ~/.pi/SOUL.md
      vscode-codex:       AGENTS.md

    node scripts/check-rule-copies.js als Vorbild für sin-code profile check:

    ponytail hat scripts/check-rule-copies.js, das prüft, ob die per-Agent-Kopien mit der Source synchron sind. Wir bauen dasselbe in Go (Phase 4, Schritt 12 dieses Issues):

    func (r Registry) Check() ([]Drift, error) {
        source, err := os.ReadFile(r.SourcePath)
        if err != nil { return nil, err }
        for _, target := range r.Targets {
            rendered, err := r.Render(source, target)
            if err != nil { return nil, err }
            current, err := os.ReadFile(target.Path)
            if err != nil { continue } // missing = drift
            if !bytes.Equal(rendered, current) {
                drift = append(drift, Drift{Path: target.Path, ...})
            }
        }
        return drift, nil
    }

    Plugin-Manifest-Kompatibilität:

    ponytail's .claude-plugin/plugin.json:

    {"name":"ponytail","version":"4.7.0","description":"...","author":{"name":"Dietrich Gebert",...}}

    Unser internal/skilldist/ kann das gleiche Schema verwenden, mit zusätzlichen Feldern für SIN-Code-spezifische Properties.

    Wichtigste Erkenntnis:

    ponytail zeigt, dass eine gute AGENTS.md kurz sein muss. 379 Zeilen für unser AGENTS.md ist zu lang. Wir sollten das in drei Dateien aufteilen:

    1. AGENTS.md (50 Zeilen) – Quick-Reference + Hard Mandates
    2. docs/agent-profiles/sin-profile.md (100 Zeilen) – Detail-Profile
    3. docs/agent-profiles/instinct-rules.md (variabel) – Gelernte Instinkte

    Quellen:

    • https://github.com/DietrichGebert/ponytail/blob/main/AGENTS.md:1-31
    • https://github.com/DietrichGebert/ponytail/blob/main/docs/agent-portability.md:1-50
    • https://github.com/DietrichGebert/ponytail/blob/main/.claude-plugin/plugin.json
  2. Delqhi commented on Jun 16, 2026

    @Delqhi
    CollaboratorAuthor

    Closed: implementation in PR #202 (feat(profile): single-source-of-truth sin-profile.md + per-agent renderers + CI sync). When PR #202 merges, the feature is live.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions