agentleFS
the-vibey-projectthe-vibey-project/vibey/.claude/skills/vibey-domain-model/SKILL.md

vibey-domain-model

Skill3 starsChanged 25 days agoMITView on GitHub

Phases, jobs, ledger, handoff, rotation, and the no-loss gate — the core domain types and invariants in vibey.

We split this skill into the rules it gives an agent

8 rules. About 0 are common, 0 are distinctive, and 8 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 8

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Read docs/plans/domain-model.md before touching domain/.

    line 9
  2. Very specific:

    Bounded ladders park, they do not loop: When build.implement or build.verify exhausts its effort ladder (or a spend cap), the job parks …

    line 73
  3. Very specific:

    Idempotency: every job is idempotent under replay via idempotency_key — a deterministic hash of (project_id, cycle, kind, subject).

    line 81
  4. Very specific:

    Digest: every event carries a digest (SHA-256 of canonical JSON payload).

    line 115
  5. Very specific:

    When an engine hits CreditsExhausted, vibey:

    line 123
  6. Very specific:

    If the gate passes, writes the full ledger to <worktree>/.vibey/handoff/ledger.jsonl and seeds the next engine.

    line 127
  7. Very specific:

    If the gate fails, regenerates (up to 3 attempts), then escalates to full_transcript mode, then raises a human gate.

    line 129
  8. Very specific:

    No starvation: over sum(effective_weight) consecutive selections, every candidate with effective_weight > 0 is selected at least once.

    line 165

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

vibey domain model

Read docs/plans/domain-model.md before touching domain/. This skill is a compressed orientation; the full spec lives in that doc.

Phases

Six numbered phases plus optional visual interstitial:

INTAKE → ① DESIGN → [optional VISUAL_DESIGN] → ② BUILD ⇄ ③ REVIEW
         ▲──────────────────────────┘               │
                                                     │
③ ── user declines deployment ──────────────────────→ DONE (local)
  │ user opts into deployment
  ▼
④ DEPLOY_DESIGN → ⑤ DEPLOY_EXECUTE → ⑥ DEPLOY_REVIEW → DONE (deployed)

Interactive phases (talk to the user): ①, ③, ④, ⑥, VISUAL_DESIGN. Autonomous phases (unattended): ②, ⑤.

Loop-backs:

  • BUILD → DESIGN when blocked on ambiguity (spec insufficient).
  • REVIEW → DESIGN (default) or REVIEW → BUILD (fast path when every finding is unambiguous). See ADR-0010.
  • DEPLOY_REVIEW can route to ④, ⑤, or back to ①/②/③ depending on the failure classification.

Opt-in gates:

  • Visual design: after ① accepts a buildable spec, vibey asks whether to enter the visual-design interstitial. Declining takes the DESIGN → BUILD edge directly.
  • Deployment: after ③ has no open product findings, vibey asks whether to work on deployment. Declining records completion_mode = "local" and transitions to DONE without enqueueing any Azure job.

See ADR-0014 (optional visual-design and deployment opt-in).

Jobs

Job kinds are strings registered against handlers in bootstrap.py (25 kinds as of 2026-09-15):

  • DESIGN: design.interview, design.research, design.synthesize, design.spec
  • VISUAL_DESIGN: visual.inventory, visual.plan
  • BUILD: build.decompose (alias build.plan), build.implement, build.verify, build.integrate
  • REVIEW: review.demo, review.collect, review.triage, review.deployment_choice
  • DEPLOY_DESIGN: deploy.design, deploy.interview, deploy.synthesize, deploy.spec (alias deploy.accept)
  • DEPLOY_EXECUTE: deploy.execute (alias deploy.graph)
  • DEPLOY_REVIEW: deploy.demo, deploy.triage, deploy.route

docs/plans/phase-protocols.md also names kinds that are not registered (media.generate.*, visual.review, deploy.discover/plan/validate/apply/ release/verify/recover/collect); treat those as planned. Handoff is not a job kind: it runs inside the engine-driven job through application/wind_down.py and application/handoff_orchestration.py.

Job lifecycle (domain/job.py::JobState): ready → leased → succeeded | failed | awaiting_human | awaiting_capacity | cancelled.

Failure classes (domain/job.py::FailureClass): capacity (opens the circuit), engine (opens after 3), work (the code is wrong; circuit untouched), vibey (our bug; circuit untouched).

Bounded ladders park, they do not loop. When build.implement or build.verify exhausts its effort ladder (or a spend cap), the job parks behind a human gate. The answer can grant a bound (max_attempts for implement attempts, max_rounds for verify repair rounds, max_dollars/max_turns for spend); further attempts run at the ladder's top effort until that grant is used up, then park again (ADR-0024; granted_limit/granted_amount in application/build_verify_handler.py).

Idempotency: every job is idempotent under replay via idempotency_key — a deterministic hash of (project_id, cycle, kind, subject). Re-enqueueing the same logical work is a no-op. Handlers also guard their own side effects.

Parallel vs serial: some jobs run in parallel (e.g., build.implement — each work item in its own git worktree). Others are serial (e.g., build.integrate — one integration branch, serialised per project and cycle by a Postgres advisory lock in infrastructure/db/advisory_lock.py, ADR-0029).

See domain/job.py (JobState, FailureClass, backoff(), idempotency_key()) and the handlers in application/*_handler.py.

Ledger

The append-only event log in Postgres, written in a vendor-neutral schema. Vendor transcripts (~/.claude/projects/**/*.jsonl, codex rollouts) are copied in as attachments referenced by events — they are evidence, not state.

Event kinds (26 total, domain/ledger.py::EventKind): SessionSeeded, TurnRequested, TurnCompleted, ToolInvoked, TranscriptRecorded, FileEdited, VerdictRendered, CapacityRejected, QuestionAsked, AnswerGiven, DecisionRecorded, AssumptionStated, FindingRaised, FindingResolved, ArtifactProduced, SavePointCreated, HandoffInitiated, HandoffAccepted, PhaseTransitioned, BudgetSpent, and the ADR-0014 opt-in gate events VisualDesignOptedIn, VisualDesignDeclined, VisualDesignAccepted, VisualDesignWaived, DeploymentOptedIn, DeploymentDeclined.

Readers are forward compatible, writers strict (vibey#275): a stored kind this vibey does not know is read as UnrecognizedEventKind (so LedgerEvent.kind is EventKind | UnrecognizedEventKind), kept in every range, full ledger and digest, skipped by every projection, and never written. Match kinds with is EventKind.X and narrow with isinstance(kind, EventKind) before using a member-only attribute.

Closable events: QuestionAsked, DecisionRecorded, AssumptionStated, FindingRaised — these are the events the no-loss gate checks.

Digest: every event carries a digest (SHA-256 of canonical JSON payload). digest_range() computes an order-sensitive Merkle-ish fold of a sequence of events — this is what R6 (range integrity) checks.

See domain/ledger.py and docs/plans/handoff-protocol.md.

Handoff and the no-loss gate

When an engine hits CreditsExhausted, vibey:

  1. Produces a HandoffBrief (the outgoing engine writes it, or the incoming engine synthesizes it if the outgoing is dead).
  2. Verifies it against the no-loss gate (domain/noloss.py::verify()).
  3. If the gate passes, writes the full ledger to <worktree>/.vibey/handoff/ledger.jsonl and seeds the next engine.
  4. If the gate fails, regenerates (up to 3 attempts), then escalates to full_transcript mode, then raises a human gate. In that mode the gate waives R1–R5, R7, R9; the design inlines the whole range, but as built the range is only the ledger.jsonl file named in the seed prompt — do not describe it as inlined.

The 10 rules (R1–R10):

  • R1: remaining-work closure
  • R2: open-question closure
  • R3: decision closure
  • R4: assumption closure
  • R5: finding closure
  • R6: range integrity (digest match)
  • R7: artifact closure
  • R8: budget carry
  • R9: constraint closure
  • R10: containment (nothing in the brief contradicts the spec)

The gate is pure and deterministic — no model call. This is the reason rotation is safe.

See ADR-0004 (no-loss gate on handoff) and domain/noloss.py.

Rotation

Smooth weighted round robin (nginx-style SWRR), not naive modulo. Engines are selected from the eligible set via domain/rotation.py::select().

Eligibility: installed, authenticated (doctor passed within TTL), circuit not open, capability requirements met, and per-phase allow-list permits it.

When rotation happens: at boundaries only — never mid-turn. Triggers: new work item starts, capacity rejection (forced, exclude the rejecting engine), effort escalation, phase transition, operator-requested handoff.

Properties (property-tested):

  • No starvation: over sum(effective_weight) consecutive selections, every candidate with effective_weight > 0 is selected at least once.
  • Weight fidelity: selection counts converge to weight ratios within ±1 over any full period.
  • Smoothness: no candidate is selected twice consecutively while another with effective_weight > 0 has gone unselected longer.
  • Determinism: identical candidate state produces an identical selection.

Wiring: application/engine_selector.py::EngineSelector calls select() for engine-driven BUILD jobs, through SelectingEngineProvider in bootstrap.py. Candidates come from engine_health rows, so an engine without a recorded vibey doctor --conformance --record pass is never eligible. A local engine joins only while its switch is on — gptossloop unless switched off, qwenloop and claudeloop-local only when switched on — and is preferred first (ADR-0038, ADR-0064).

See ADR-0005 (smooth weighted round robin), ADR-0007 (rotate at boundaries), and domain/rotation.py.

Effort ladder

Vibey speaks a normalized 5-level ladder: TRIVIAL, LOW, STANDARD, HIGH, MAX. Each engine descriptor provides a projection onto native flags, which may saturate.

Phase base effort:

  • ① DESIGN: HIGH
  • VISUAL_DESIGN: HIGH
  • ② BUILD: LOW (auto-escalating to STANDARD → HIGH after failures)
  • ③ REVIEW: HIGH
  • ④ DEPLOY_DESIGN: HIGH
  • ⑤ DEPLOY_EXECUTE: LOW (auto-escalating)
  • ⑥ DEPLOY_REVIEW: HIGH

Phase also carries DEPLOY (a legacy single-phase bridge, base LOW) and ABANDONED; the phase diagram omits them deliberately.

See ADR-0006 (normalized effort ladder) and domain/effort.py.

Capacity states

Available | WindowExhausted | CreditsExhausted | AuthenticationFailed

The one rule that matters: CreditsExhausted has no resets_at field and can never acquire one. This is enforced at three independent layers:

  1. Type-level: domain/capacity.py's dataclass has no such attribute.
  2. Property-tested: schedule_probe(CreditsExhausted(...)) can only return a BackoffProbe, never a DeadlineProbe.
  3. Database-level: the engine_health table has a CHECK constraint credits_never_have_a_deadline that rejects any INSERT/UPDATE with capacity_state = 'CreditsExhausted' and non-null resets_at.

See domain/capacity.py, domain/circuit.py, the constraint in migrations/0007_engine_health_rotation.sql, and the CLAUDE.md non-negotiable "A capacity rejection always outranks a completion claim" (it has no ADR of its own).

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.