agentleFS
ryanmacleanryanmaclean/skills/jobcard-system/SKILL.md

jobcard-system

Skill1 starsChanged 50 days agoMITView on GitHub

Use when working in the jobcard repository, dispatching agents, writing specs, creating cards, or understanding the card lifecycle. This is a multi-agent task orchestration system — NOT the transit data standard.

The file

We split this skill into the rules it gives an agent

12 rules. About 0 are common, 3 are distinctive, and 9 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 9

    Very specific

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

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Every file has exactly one job

    line 51
  2. Very specific:

    Use filename prefix for poker visibility while keeping meta.id canonical.

    line 98
  3. Very specific:

    Keep meta.id stable as feat-auth (CLI queries stay stable)

    line 102
  4. Very specific:

    Only rename outside running/ (prefer pending/.

    line 106
  5. Very specific:

    If poker consensus finalized a glyph, write meta.glyph in the same critical section.

    line 109
  6. Very specific:

    When a stage finishes (exit 0), dispatcher reads stage_chain, creates next-stage child card in pending/ inheriting spec + glyph + prior …

    line 123
  7. Very specific:

    All four fields are on the Meta struct (landed 2026-03-01).

    line 126
  8. Distinctive:

    Every token earns its slot.

    line 145
  9. Very specific:

    Keep default atomic replacement behavior.

    line 184
  10. Very specific:

    All zsh scripts use ROOT=${0:A:h:h}.

    line 272
  11. Very specific:

    Never use done as a variable (zsh reserved — use n_done).

    line 273
  12. Distinctive:

    Every dispatched agent is shaped by (inner wins):

    line 286

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 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

Jobcard System (bop)

Naming — COMPLETE (2026-03-01)

RoleName
CLI binarybop
Quick Look bundlesh.bop.ql
Host app bundlesh.bop
Card UTIsh.bop.card
launchd labelssh.bop.dispatcher, sh.bop.merge-gate
bop new implement feat-auth      # deal a card into pending/
bop status                       # card counts by state
bop dispatcher --once            # process one pending card
bop merge-gate --once            # process one done card
bop inspect <id>                 # show meta + spec + log tail
bop approve <id>                 # clear decision_required gate
bop kill <id>                    # terminate running agent
bop logs <id>                    # colorized log tail (tailspin-style)
bop poker open <id>              # start estimation round
bop poker submit <id> [--glyph 🂻] [--name alice]
bop poker reveal <id>            # reveal all estimates
bop poker consensus <id> --glyph 🂻  # lock in final estimate
bop factory install              # generate + load launchd plists
bop factory status               # check if services are running
bop factory start                # start both services
bop factory stop                 # stop both services
bop factory uninstall            # unload + remove plists
bop icons                        # FSEvents watcher: auto-updates Finder icons on state change
bop gantt                        # ANSI Gantt timeline in terminal (auto-fits pane)
bop gantt -w 80                  # force width (Zellij panes)
bop gantt --html                 # write .cards/bop-gantt.html
bop gantt -o                     # write HTML + open in browser
bop events                       # OpenLineage event stream
bop events --card <id>           # filter events by card

bop factory install also sets up the icon watcher alongside dispatcher and merge-gate.

MICROCLAW Principle

Fewer files. Sharper precision. Minimum viable factory.

  • Every file has exactly one job
  • Agents can participate with just mkdir + echo '{}' > meta.json
  • The FORMAT is the product; the CLI is one thin reference impl
  • Like Apache Iceberg (table format, any engine reads/writes)

Core Framing

The filesystem IS the state machine. Playing cards ARE the identity system. The FORMAT is the product.

# Minimum viable card — no CLI needed
mkdir -p .cards/pending/my-task.jobcard
echo '{"id":"my-task","stage":"implement","created":"2026-03-01T00:00:00Z"}' \
  > .cards/pending/my-task.jobcard/meta.json
echo "# My Task\nDo X" > .cards/pending/my-task.jobcard/spec.md

Architecture

.cards/
  stages/           ← stage instruction files (spec.md, plan.md, implement.md, qa.md)
  templates/        ← factory recipes (implement, full, cheap, qa-only)
  system_context.md ← prepended to every agent prompt
  <team>/<state>/<id>.jobcard/
    meta.json       ← card identity, stage pipeline, glyph, labels, subtasks
    spec.md         ← what to build
    prompt.md       ← rendered from template, sent to agent
    logs/           ← stdout.log, stderr.log, pid, lease.json
    output/         ← result.md, cards.yaml (child card seeds)
    QuickLook/      ← Thumbnail.png for Finder preview

State Machine

pending/ → running/ → done/ → merged/
                    ↓
                 failed/

Transitions are atomic fs::rename. Dispatcher owns pending/→running/. Merge-gate owns done/→merged/ (runs acceptance_criteria first).

Filename Glyph Lifecycle (Poker)

Use filename prefix for poker visibility while keeping meta.id canonical.

  • Before consensus: 🂠-feat-auth.jobcard
  • After consensus: 🂻-feat-auth.jobcard
  • Keep meta.id stable as feat-auth (CLI queries stay stable)

Safe rename flow (filesystem-first, same volume):

  1. Only rename outside running/ (prefer pending/; allow done//failed/ for cleanup).
  2. Hold card lock / ownership gate; assert destination path does not exist.
  3. Apply atomic rename (fs::rename).
  4. If poker consensus finalized a glyph, write meta.glyph in the same critical section.
  5. Refresh visual artifacts (QuickLook/Thumbnail.png + Finder icon).

Stage Pipeline (Factory Engine)

Templates define the stage pipeline via these meta.json fields:

FieldTypeExample
stage_chainVec<String>["implement", "qa"]
stage_modelsMap<String,String>{"implement":"opus","qa":"sonnet"}
stage_providersMap<String,String>{"implement":"claude","qa":"codex"}
stage_budgetsMap<String,u64>{"implement":32000,"qa":8000}

When a stage finishes (exit 0), dispatcher reads stage_chain, creates next-stage child card in pending/ inheriting spec + glyph + prior output. Final stage → done/.

All four fields are on the Meta struct (landed 2026-03-01). render_prompt substitutes {{stage_instructions}}, {{stage_index}}, {{stage_count}}, {{prior_stage_output}}. Dispatcher auto-advances stages via maybe_advance_stage().

Stage Instruction Files

.cards/stages/ — one markdown file per stage, ~50 tokens each:

FileAgent's job
spec.mdWrite a spec under 500 words. No code.
plan.mdRead spec, produce ordered steps under 800 words.
implement.mdWrite code, run tests, exit 0 when green.
qa.mdReview as a different agent. Be skeptical.

Custom stages: create .cards/stages/<name>.md, add to template's stage_chain.

Context Window Architecture

Every token earns its slot. Dispatcher builds prompts by layering:

system_context.md        ~150 tokens   domain fix
stages/<stage>.md        ~50 tokens    stage behavior
card header              ~20 tokens    id + glyph + stage index
spec.md                  variable      the work
prior_stage_output       variable      previous stage result
acceptance_criteria      ~10 tokens    shell gates

Template variables in prompt.md:

  • {{system_context}} — .cards/system_context.md (WORKS)
  • {{stage_instructions}} — .cards/stages/<stage>.md (WORKS)
  • {{stage_index}} / {{stage_count}} — position in chain (WORKS)
  • {{prior_stage_output}} — previous stage's output/result.md (WORKS)
  • {{spec}}, {{plan}}, {{acceptance_criteria}}, {{memory}} — WORK

Templates

Live in .cards/templates/. Four shipping templates:

TemplateStagesModelsCostUse When
implementimplement → qaopus → sonnet$$Clear requirements
fullspec → plan → implement → qasonnet → sonnet → opus → sonnet$$$Complex/ambiguous
cheapimplementollama-localfreeSmall fix, local model
qa-onlyqasonnet$Code review, audit

Create your own: cp -c -R .cards/templates/implement.jobcard .cards/templates/mine.jobcard then edit meta.json (stage_chain, stage_models, stage_providers, stage_budgets). See plan.md §8 for annotated meta.json reference.

COW-clone: cp -c (macOS APFS) or cp --reflink=auto (Linux).

APFS Copy, Dedupe, Compression (macOS)

  • Fast template clone: cp -c -R (clonefile/COW on APFS).
  • Metadata-safe bundle copy: ditto --clone --extattr --acl --qtn --preserveHFSCompression <src> <dst>.
  • Keep default atomic replacement behavior; avoid ditto --nonAtomicCopies for card bundles.
  • Cold-card storage option: ditto --hfsCompression (or preserve existing compression with default --preserveHFSCompression).
  • For archive export/import, use ditto -c -k / ditto -x -k; prefer staging + atomic move into .cards/.

Filename Tooling Compatibility (macOS 26.3 validation)

With 🂠-feat-auth.jobcard on APFS, these worked in tests:

  • rg, tail, eza, bat
  • bop inspect feat-auth
  • bop logs feat-auth
  • bop logs -f feat-auth when the card is in running/

exa is no longer a Homebrew formula; standardize on eza.

Filesystem Hardening

  1. Dispatcher lock: .cards/.locks/dispatcher.lock/owner.json
    • Acquire with atomic mkdir; reject if live owner PID exists
    • Reclaim stale lock (dead PID) and continue
  2. Run lease per card: logs/lease.json
    • Fields: run_id, pid, pid_start_time, started_at, heartbeat_at, host
    • Dispatcher heartbeats every 5s; stale floor is 30s
  3. Orphan reaping: reap when PID dead OR lease stale
    • Normalize stale stage metadata (running → pending/failed)
  4. Idempotent controls:
    • bop kill <id> succeeds even with stale PID
    • bop retry <id> clears failure reason + normalizes stage

Meta.json Fields

FieldMeaning
glyphSMP playing card (suit=team, rank=priority)
tokenBMP symbol for terminals/filenames
stageCurrent stage: spec / plan / implement / qa
stage_chainFull pipeline: ["implement","qa"]
stage_modelsModel per stage: {"implement":"opus"}
stage_providersAdapter per stage: {"implement":"claude"}
stage_budgetsToken budget per stage: {"implement":32000}
stages.<s>.statuspending / running / done / blocked / failed
zellij_sessionZellij session name for running card
zellij_paneZellij pane ID for running card
worktree_branchGit branch — never touch main
acceptance_criteriaShell commands merge-gate runs
decision_requiredtrue = merge-gate holds; bop approve clears
provider_chainFailover order: ["claude","codex","ollama-local"]
timeout_secondsKill agent after this many seconds
estimatesPlanning poker: {"alice":"🂻","bob":"🂺"}
poker_round"open" / "revealed" / absent
labels[{"name":"High Impact","kind":"effort"}]
subtasks[{"id":"s1","title":"Do X","done":false}]
progress0–100 overall completion %

Adapter Contract

adapter.zsh <workdir> <prompt_file> <stdout_log> <stderr_log> [timeout]
ExitMeaningAction
0Success→ done/
75Rate-limited→ pending/, rotate provider
otherFailure→ failed/

Available: claude.zsh, codex.zsh, ollama-local.zsh, goose.zsh, aider.zsh, opencode.zsh, mock.zsh.

Planning Poker

Suit → perspective: ♠=complexity ♥=effort ♦=risk ♣=value. Rank → magnitude: Ace=1, 2-10=face, Jack=13, Queen=21, King=40. Joker (🃟/🃏) → needs breakdown, blocks consensus.

Scripts

ScriptPurpose
scripts/land_safe.zshJJ/git safe-landing with gate checks
scripts/bop_bop.zshGoal→card→Zellij session bootstrap
scripts/bop_focus.zshNavigate Zellij panes by card id
scripts/macos_cards_maintenance.zshThumbnail refresh, cold card compression
scripts/render_card_thumbnail.swiftGenerate QuickLook/Thumbnail.png
scripts/set_card_icon.swiftStage-coloured Finder icons for .jobcard bundles

All zsh scripts use ROOT=${0:A:h:h}. setopt NULL_GLOB for .jobcard globs. Never use done as a variable (zsh reserved — use n_done).

Quick Look / macOS

  • Xcode project: macos/macos.xcodeproj
  • Extension: macos/bop/PreviewViewController.swift
  • Host app: macos/JobCardHost/ (registers bop:// URL scheme)
  • Build: Xcode → Product → Build
  • Register: lsregister -f ~/Applications/JobCardHost.app
  • Bundle IDs: sh.bop (host), sh.bop.ql (extension), sh.bop.card (UTI)

Three Steering Layers

Every dispatched agent is shaped by (inner wins):

  1. Skills (~/.claude/skills/) — HOW to approach work
  2. CLAUDE.md — project constraints, shell rules, architecture
  3. system_context.md — runtime domain fix ("NOT GTFS transit data")

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.