Persona-as-code (Git sync)
Connect a Git repository to your workspace; pull, push, or bidirectional sync personas as YAML + markdown with last-write-wins conflict detection.
Persona-as-code closes the loop between Moonborn's product surface and your engineering workflow: personas live in Git, every change shows up as a diff in a pull request, and the workspace stays synced.
Preview status. The domain and application layers are implemented (
@moonborn/integration-gitshipsPullPersonasFromGitUseCase+PushPersonasToGitUseCase); the HTTP API and product onboarding UI are landing in the next release. Until then, this tutorial walks through the configuration shape — early-access customers can wire it up via direct integration.
1. Pick a sync mode
integrations.git.sync_direction controls the flow:
pull— Git is the canonical source. Workspace mirrors the repo.push— Moonborn UI is canonical; Git is the audit trail.bidirectional— both, with conflict resolution.
Most teams start with push for the first month — let the product UI
generate the personas, watch the diffs land in Git, then switch to
bidirectional once the team is editing personas in both places.
2. Configure the connection
await client.config.setItem({
key: 'integrations.git.provider',
value: 'github',
scope: 'workspace',
scopeId: 'ws_...',
});
await client.config.setItem({
key: 'integrations.git.repo_url',
value: 'https://github.com/your-org/persona-bible',
scope: 'workspace',
scopeId: 'ws_...',
});
await client.config.setItem({
key: 'integrations.git.base_path',
value: 'personas/',
scope: 'workspace',
scopeId: 'ws_...',
});
await client.config.setItem({
key: 'integrations.git.sync_direction',
value: 'push',
scope: 'workspace',
scopeId: 'ws_...',
});Supported providers: github, gitlab, bitbucket. Each is
independently tier-gated under integrations.git.providers.* (all
Team+).
3. File layout
Each persona becomes a single file under the configured base path:
---
id: persona_01H...
name: Mert Aksoy
version: 4
layer: Soul
desire: build something the world can't ignore
fear: being seen as ordinary
wound: a parent who measured love in achievement
growth_arc: from approval-seeking to self-trusting
---
Mert spent his twenties in the Istanbul tech scene. His first
company sold to a Turkish telco — a quiet exit that he still
describes as "fine."YAML frontmatter carries the structured layers (Soul, Self, Mask, Surface); the markdown body is the narrative grounding. The serializer emits this exact shape on push; the parser reads it back on pull.
4. Trigger the first sync
In the preview phase, sync runs are triggered via the application layer:
// (preview — direct integration only; HTTP endpoint coming)
import { PushPersonasToGitUseCase } from '@moonborn/integration-git/application';
await runPush({ workspaceId: 'ws_...' });In production, this becomes:
# (planned)
curl -X POST https://api.moonborn.co/v1/integrations/git/push \
-H "Authorization: Bearer $MOONBORN_API_KEY"5. Conflict resolution
Bidirectional syncs use last-write-wins with explicit rejection. The
strategy is set by integrations.git.conflict_resolution:
remote_wins— pulls overwrite local changes on conflict.local_wins— pushes overwrite remote changes on conflict.error— surface the conflict, halt the sync, returnPersonaFileSyncConflictErrorwith the affected persona IDs.
Conflicts are detected by Git blob SHA mismatch — no three-way merge, no CRDT.
6. Audit + commit messages
Commit messages follow engine.pipeline.audit.commit_message_template
(default feat(persona): {{name}} v{{version}}). Each persona change
produces one commit (with integrations.git.commit_strategy = per_change) or batched into one commit per sync run (with
batched).
Git history becomes the de-facto change log. The Moonborn audit log records the sync operations themselves, not the persona deltas.
Tier
Team and up. Each provider toggle independent.
Next
- The use case: Persona-as-code use case.
- Provider details: Git sync integration.
- Integrations API reference.