Complete agent-assisted development workflow for Edge repositories: slash skills, companion scripts, coding standards, an autonomous Asana-to-PR orchestration system with deterministic enforcement hooks, a post-hoc eval suite, and meta-tooling for maintaining the workflow itself.
The distributable content lives under .cursor/. This repo is the versioned
home for those skills, rules, scripts, and docs, plus the portable
orchestration trees a second machine bootstraps from.
The .cursor/ path is historical, not a commitment: the system is built to
sync across agent harnesses, not just Cursor. Claude Code is the primary
consumer today (~/.claude/skills symlinks to the canonical tree,
~/.claude/CLAUDE.md is generated from the always-apply rules, and the
enforcement hooks are Claude Code hooks); OpenCode gets generated mirrors via
tool-sync.sh. Other harnesses are untested, but the content is plain
markdown and shell, so most of it should carry in theory; the hook layer is
the Claude-specific part.
The canonical local doc lives at ~/.cursor/README.md. During
/convention-sync, that file is mirrored to edge-dev-agents/README.md, and
the repo copy should not keep a second .cursor/README.md.
Fresh machine (one command): clone this repo and run the bootstrap. It
installs everything (cursor skills/rules, the orchestration system, hook
registrations, workflows, and shared memories) into your home dir, seeds
credentials.json from the example, and links skills + shared memory:
git clone <this-repo> ~/git/edge-dev-agents && cd ~/git/edge-dev-agents && ./bootstrap.sh
# then edit ~/.config/agent-watcher/credentials.json with your real asana_tokenFor incremental onboarding instead of the full bootstrap:
1. Set the required env var in your ~/.zshrc:
export GIT_BRANCH_PREFIX=yourname # e.g. jon, paul, samThis drives branch naming and PR discovery across the workflow.
2. Sync the repo copy into ~/.cursor/:
This repo treats ~/.cursor/ as the canonical working copy. Use
/convention-sync to move local changes into edge-dev-agents, or run the
companion script directly when onboarding:
~/.cursor/skills/convention-sync/scripts/convention-sync.sh \
--repo-to-user --stage3. Verify prerequisites:
ghCLI:gh auth loginjq:brew install jqASANA_TOKENenv var for Asana-backed workflowsclaudeCLI (the orchestration spawns Claude Code sessions; the prose judge also shells out to it)
- Orchestration & memory
- Enforcement hooks
- Prose standards & enforcement
- Testing infrastructure
- Evals
- Distribution (what syncs)
- Architecture
- Skills
- Companion scripts
- Rules
- Design principles
The orchestration system runs Asana tasks to PRs autonomously: a watcher picks up
Pending tasks, spawns one isolated agent session per task, and a watchdog tends
the live sessions. Post-hoc evals grade what each run did.
At a glance:
flowchart TD
A["Asana project (Pending tasks)"] -->|"watcher tick 120s"| B{"guardrail and cap OK?"}
B -- no --> A
B -- yes --> C["allocate slot: worktree + cloned sim + Metro port"]
C --> D["spawn tmux claude-asana-GID (claude --rc --yolo /one-shot)"]
D --> E["/one-shot 7 phases: Planning, Developing, Reviewing, Testing"]
E --> F{"finalize-gate: CI green + bots clean + 0 unresolved threads"}
F -- "not green" --> E
F -- green --> G["agent_status = Complete"]
G -->|"watchdog completion sweep"| H["retire to done-asana-GID; free sim, Metro, slot; claude kept alive"]
H -->|"operator sets Pending (revisit resumes with memory)"| A
H -->|"beyond keep_completed_sessions"| I["reaped"]
agent-watcher/asana-watcher.js, launchd every 120s, polls the project for
agent_status = Pending.
- Tick gate. The tick is skipped when a resource guardrail trips (1-minute
load over
max_load_avg, free RAM undermin_free_ram_gb) or the concurrency cap is full (max_concurrent, default 4). - Provisioning. Each pickable task gets a slot: a git worktree, a cloned
simulator from the pool, and a Metro port. Before recloning the pool the
watcher runs
refresh-master-build.sh: ifdevelopadvanced AND its native side (ios/Podfile.lock) changed, the master sim is rebuilt fromdevelopand the pool recloned, so runs test against a current build. A JS-only advance skips the rebuild (clones bundle JS live from Metro). The check is a cheapgit fetchplus SHA compare; a build failure is non-fatal (provisioning continues on the last-good master). - Fresh vs revisit. A never-run task spawns fresh:
agent_status = Planning, a tmux sessionclaude-asana-(gid)running/one-shot --yolo (task-url). A task with a prior transcript is RESUMED instead, on a fresh slot, viaresume-task. Re-engaging a finished task is therefore one signal: set it back toPending. - Resume memory, and why finalize is gated. A resumed session's memory is
never trusted for scope decisions: long runs auto-compact mid-flight, and
the summary a compaction runs on predates whatever re-armed the task, so
"the newest comment is my own Complete note" recalled from context is
exactly the stale claim that has re-Completed past real followups. (Resumes
themselves answer the resume menu with a FULL resume; the lossier
summary-resume tier is retired, and a transcript past the degradation
threshold fresh-spawns from artifacts instead.) The counter is mechanical:
check-followup-scope.shlive-fetches comments and attachments, lists every operator comment newer than the latestagent-run-report*.mdwatermark, diffs the task's fields against the previous segment's snapshot, fetches GitHub-side scope across parent AND subtask-attached PRs (unresolved review threads, reviewer-bot completeness on owned ready HEADs, unanswered top-level review bodies and PR comments), and writes a marker; therequire-followup-scope-on-complete.shhook blocksCompleteunless that marker exists, still matches the live newest comment, records zero blocking threads and bot gaps, and shows the report attachment as the newest agent-authored event (a π₯-marked agent comment after the report means the watermark is stale and the report must be re-attached). Recalled context never stands in for the fetch. - Per-task model and effort. Both spawn and resume route through
spawn-test-session.sh, which pins the session model and reasoning effort from the task'sagent_model/agent_effortAsana fields (models all 1M-context; effort low through max). Unset falls back to config defaults (currently.watcher.agent_model= Opus 5 1M,.watcher.agent_effort= high). - Version stamp. Every spawn and resume segment records which orch version
governs it:
stamp-orch-version.shappends content digests of the governing trees (skills, rules, watcher, hooks, settings hooks) plus repo head, CLI version, model, and effort toversions/(gid).jsonl, and exportsAGENT_ORCH_VERSIONfor the run report. Evals slice findings by the version actually in force per segment.
/one-shot --yolo, a single agent turn: seven phases with agent_status
advanced via update-status.sh at each boundary. Planning (/asana-plan),
Developing (/im), Testing (/build-and-test, on-sim verification), Reviewing
(/pr-create through the CI + reviewer-bot watch), Complete. Status LEADS the
work: the phase status is set when that kind of work starts, never as a side
effect of a terminal action. The agent runs hands-off: no interactive prompts,
no self-respawn, every wait a bounded blocking in-turn call. The full decision
graph, including followup, concession, landing, and cheese branches, is in the
/one-shot decision flowchart below.
Complete requires the finalize gate: every primary PR CI-green, every
reviewer bot run-and-concluded clean on the ready HEAD, zero unresolved review
threads. At green, landability is decided FIRST: a PR with a human APPROVED
review (or the task's Force Land field) lands via /pr-land and the Build
field is ignored. Only a non-landable green run kicks a cheese build, pinning
the task's own unpublished dep PRs when the deliverable requires them.
agent-watcher/session-watchdog.js, launchd every 120s, tends live sessions:
- RC-bridge revive (only when the bridge is dead)
- completion sweep:
Completeretiresclaude-asana-(gid)todone-asana-(gid), frees sim/Metro/slot, keeps claude alive for re-engagement - blocked = blocked COMPLETION (
Complete --blocked yes): retires via the completion sweep; the shed-on-block branch is a legacy net for stray mid-run blocks - GC: keep newest
keep_completed_sessions/keep_completed_worktrees(currently 20 / 5) - orphan-Metro reap, idle-dirty-sim reclaim, and operator escalation for parked prompts or stuck sessions
It does NOT re-engage finished tasks: that is the watcher's job (Pending resumes), so watchdog and watcher stay decoupled.
Every agent is an INTERACTIVE claude --rc process in a detached tmux
session, never a headless claude -p. The pane is part of the machinery, not
just a viewport.
- Naming is state.
claude-asana-<gid>is a live run; the watchdog's completion sweep renames it todone-asana-<gid>at retirement (resources freed, claude kept alive);chat-<slug>sessions are discussion forks of past transcripts (resume-agent.sh --chat: talk to a finished run from anywhere, no slot, original conversation untouched); long-lived operator anchors keep their own names. Hooks and the watchdog read the name to decide context: the π₯ authorship boundary is "pane name is exactlyclaude-asana-<gid>", so a retired session's text counts as operator instruction. - Spawn.
spawn-test-session.shwrites a wrapper script (slot env baked in: worktree cwd,AGENT_SIM_UDID,AGENT_METRO_PORT, task gid, model and effort flags) and launches it withtmux new-session -d. When claude exits the wrapper prints the exit time and drops to a shell, so the pane and its scrollback survive for diagnosis. - Resume poking.
claude --resumeon a prior transcript shows an interactive menu that would wedge a hands-off session, so the spawner polls the pane and answers it with send-keys (Down+Enter: FULL resume; the summary tier is retired). This is the general pattern: the session is driven through its terminal exactly as an operator would drive it, which means every interactive surface the CLI grows is automatable without new APIs. - Remote-control keepalive. Each session arms a remote-control bridge at
startup, so the operator can steer any run from the desktop or phone app.
Bridge liveness is read from the pane footer (the
/rctoken, or the "Remote Control active" line on older builds). The watchdog revives ONLY a verified-dead bridge on a verified-alive claude: kill the pane's process, confirm death, relaunch in place with--remote-control <name> --resume <live-id>and the preserved flags, under a per-session cooldown. A half-open bridge is left for the operator to reconnect, and the revive never adds a second process (the count goes 1 to 0 to 1). - No self-respawn. A session never kills or relaunches its own pane;
resume-task.shandresume-agent.share watcher/operator tools and refuse to run from inside their target. Theno-self-respawn.shhook enforces the agent side. - The host must not idle-sleep. All of this dies with the machine, so the box runs a keep-awake LaunchAgent (machine-local, not synced; an idle-sleep default once killed every bridge in the fleet after hours of quiet).
Every run ends by attaching ONE structured run report to the Asana task
(agent-run-report-NN-<slug>.md). The report is more than documentation: its
attachment timestamp is the followup-scope WATERMARK. Comments newer than the
newest report are undischarged scope for the next run, so every comment a run
owes posts BEFORE the attach, and a comment that must land later forces a
re-attach so the watermark is last again. The attach boundary is gated (see
require-clean-run-report.sh below): template form, prose lint, traceability
frontmatter, one report doc per segment, and resolvable GitHub citations are
all checked mechanically.
Post-hoc, per run or per cohort. See Evals.
The full decision flow of an orchestrated /one-shot --yolo run, including the
followup, concession, landing, and cheese branches. Rule ids in brackets name
the governing one-shot/cheese/build-and-test rules.
flowchart TD
START["/one-shot --yolo task-url"] --> REFIRE{"re-fire? task already
ran in this session"}
REFIRE -- "no (fresh or resumed-new)" --> PLAN
REFIRE -- "yes, still mid-run" --> CONT["continue current phase
[ignore-refired-one-shot]"]
REFIRE -- "yes, previously FINISHED" --> SCOPE["LIVE scope check:
check-followup-scope.sh
(never from memory)"]
SCOPE -- "operator asks newer
than report watermark" --> FOLLOWUP["deliver the new scope
[followup-scope-is-the-deliverable]"]
FOLLOWUP --> DEV
SCOPE -- "0 newer comments" --> GATE
PLAN["Planning: /asana-plan, plan doc
(confirmation waived in yolo)"] --> DEV
DEV["Developing: /im contract
(lint-warnings, lint-commit,
clean history)"] --> TEST
TEST["Testing: slot-preflight -> obey PLAN/INVOKE
-> build -> drive the REAL action on sim
(log-attempt every drive, pixel-verify proofs)
[preflight-before-build-decisions]"] --> WALL{"hit a wall?"}
WALL -- no --> PR
WALL -- yes --> ATTEMPT["log the attempt
(failed:/blocked:/loss:)"] --> VALID{"concession-validator
verdict [yolo-true-blockers]"}
VALID -- "legitimate: true" --> BLOCKED["blocked completion:
Complete --blocked yes (one-line
comment, report attached as normal)"]
VALID -- "legitimate: false" --> RETRY["do what_to_try, continue"] --> TEST
PR["Reviewing: /pr-create (verify green, clean tree,
template, evidence; Asana attach; multi-repo
subtasks; draft dep PRs excluded from gate)"] --> WATCH
WATCH["watch-pr bounded poll; bots must be
SUCCESS (NEUTRAL = findings -> /bugbot);
fixes via amend + force-with-lease"] --> GATE
GATE{"finalize-gate green:
CI + every bot clean +
0 unresolved bot threads
on EVERY primary PR"}
GATE -- "not green" --> WATCH
GATE -- green --> LAND{"landable? human APPROVED
review (any point in history)
OR Force Land field
[land-on-approval]"}
LAND -- yes --> PRLAND["/pr-land with TASK URL
(dep ordering: merge dep -> publish ->
bump -> gui; npm OTP parks at
operator boundary). Build field IGNORED
-> NO cheese push"] --> REPORT
LAND -- no --> BUILDF{"Build field?
[cheese-build-on-green]"}
BUILDF -- none --> REPORT
BUILDF -- staging --> REPORT
BUILDF -- "cheese (feta/gouda/...)" --> PINQ{"gui deliverable requires
unpublished dep PRs?"}
PINQ -- yes --> PINNED["/cheese --pin each dep
at ITS PR head
[cheese:orch-pins-required]"] --> REPORT
PINQ -- no --> POINTER["/cheese pointer reset
test-branch -> PR head"] --> REPORT
REPORT["RE-READ report template -> write report
-> require-clean-run-report lint at attach
-> set tested field from THIS run's evidence"] --> COMPLETE
COMPLETE["agent_status = Complete
(gated: fresh followup-scope check
required by hook)"]
Status discipline throughout: the phase status is set when that KIND of work starts (status leads the work), never as a side effect of a terminal action.
The hands-off contract and the quality bars are enforced by deterministic
Claude Code hooks, not merely documented. Registrations live in
~/.claude/settings.json and are distributed as the claude-settings/hooks.json
projection (see Distribution); the scripts live in
agent-watcher/hooks/. Hook BODIES are re-read from disk on every fire, so a
script fix reaches every live session immediately; only registration changes
need a session restart or settings reload.
Most gates no-op unless AGENT_TASK_GID is set (orchestrated sessions only).
The prose gates and the Asana authorship marker run everywhere, because
interactive sessions post PRs and Slack messages too.
| Group | Hook | Enforces |
|---|---|---|
| Status gates | require-plan-before-developing.sh |
No Developing until the plan doc exists |
require-concession-validation.sh |
A block or a downgrade-finalize needs a fresh concession-validator verdict bound to the exact reason | |
require-followup-scope-on-complete.sh |
Complete needs a fresh live scope check: no newer operator comments unaddressed, zero blocking threads, reviewer bots concluded, watermark last | |
require-continuation-or-block.sh (Stop) |
A turn may not end except at Complete or a validated block | |
require-tdd-current.sh |
TDD-flagged tasks keep the design doc current before finalize | |
| PR / git gates | git-history-gate.sh |
Commits go through lint-commit.sh; no raw git commit, no --no-verify |
pre-pr-gate.sh |
PR creation needs test evidence (proof frames or a justified blocker note) and runs a duplicate-utility scan | |
require-subtasks-for-multi-repo-pr.sh |
Multi-repo PR sets attach subtask-per-PR, never flat onto the main task | |
require-clean-run-report.sh |
Report attach: template form, prose lint (with judge), traceability frontmatter auto-fill, stable ordinals, one doc per segment, no dead GitHub citations | |
block-raw-thread-resolve.sh |
Review threads resolve through the reply-first scripts, never raw GraphQL | |
block-upfront-conflict-probe.sh |
PR mergeability is a landing-time concern; no upfront probes | |
ensure-tdd-pr-link.sh |
PR bodies carry the TDD link when one is owed | |
no-push-after-complete.sh |
No branch/PR mutation once the task is Complete (post-Complete rework must re-arm) | |
| Sim / testing gates | require-playbook-before-drive.sh |
The sim-testing playbook must be read before the first drive; injects the corePlugins working-set contract once per run |
require-maestro-device.sh |
Drives name an explicit --device (concurrent sims make defaults ambiguous) |
|
block-simctl-booted.sh |
No simctl ... booted in slot sessions |
|
block-coordinate-taps.sh |
No blind coordinate taps; drive by accessibility ids or text | |
block-sim-wipe.sh |
No sim erase/wipe (pooled sims carry funded test accounts) | |
require-bundle-triage.sh |
Stale-bundle symptoms get triaged before deeper debugging | |
| Prose gates | lint-md-on-write.sh |
Mechanical no-slop lint on markdown written outside the internal allowlist (full on Write, fragment on Edit/heredoc) |
slack-prose-gate.sh |
Outbound Slack text passes the shared lint with the judge tier; brevity nudge over ~900 chars | |
| Hygiene / injectors | no-interactive-prompt.sh |
No AskUserQuestion in hands-off runs; pick the defensible default |
no-self-respawn.sh |
No ScheduleWakeup/CronCreate/claude --resume self-respawn |
|
block-piped-watcher-scripts.sh |
Watcher status scripts run bare (pipes silently masked their exit codes); gated-claim commands hard-block instead of rewriting | |
mark-agent-authored-asana.sh |
In-flight-run Asana prose carries the π₯/π authorship markers; operator-context text stays unmarked | |
require-agents-md-skill.sh + mark-agents-md-skill-read.sh |
AGENTS.md edits load the authoring skill first | |
mark-playbook-read.sh |
Records the playbook read the drive gate requires | |
nudge-asana-mcp.sh |
Steers bulk Asana reads to the cheaper script path | |
inject-run-context.sh, inject-no-slop-reminder.sh, inject-no-slop-line.sh |
Session-start run context; no-slop refresh at session start and every prompt | |
| Shared helpers | strip-cmd-mentions.sh |
Blanks quoted/heredoc spans so hooks trigger on commands, not on text that merely mentions them |
cmd-executes.sh |
Command-position matching, so naming a script in a grep never fires the gate that guards executing it |
require-block-validation.sh is an unregistered legacy kept for history; the
concession gate replaced it.
All outward prose (PR bodies and comments, commit messages, Asana text, run reports, Slack, docs that leave the team, and chat replies) follows the /no-slop rules: banned vocabulary, no em dashes, no courtesy enders, no structure announcements, no count-announcement openers, plain copulas. The full pattern list is published with this repo, so review comments can cite it instead of private config paths.
Enforcement is layered, one shared implementation per rule class
(no-slop/scripts/no-slop-lint.sh is the single lint every boundary calls):
- Mechanical tier (regex-decidable): em dashes, banned vocabulary, Claude session links, decorative loudness, count-announcement shapes including trailing count-appositions. Exit 1 on HARD findings.
- Semantic tier (
--semantic): a haiku judge (no-slop-judge.sh) for the rules regexes cannot decide: courtesy enders, forward references, validation preambles. A wide regex net nominates candidate sentences, one batchedclaude -pcall rules on them, verdicts cache by sentence hash, and every infra failure fails open. Calibrated on violation and look-alike corpora before being made blocking. - Fragment mode (
--fragment): position-independent checks only, for Edit fragments and heredoc command text where sentence-shape checks would false-positive.
Where each tier runs:
| Boundary | Tier | Enforced by |
|---|---|---|
| Markdown file writes (outside internal allowlist) | mechanical | lint-md-on-write.sh hook |
| PR body at create | mechanical + judge | pr-create.sh |
| PR replies, mark-addressed, standalone comments | mechanical + judge | pr-address.sh |
| Review submits (top-level + inline bodies) | mechanical + judge | github-pr-review.sh |
| Slack sends, drafts, canvases | mechanical + judge | slack-prose-gate.sh hook |
| Run-report attach | mechanical + judge (em dashes auto-rewritten in place) | require-clean-run-report.sh hook |
| TDD docs | mechanical | tdd-lint.sh |
| Commit messages | scrub (session trailers/URLs stripped) | lint-commit.sh |
The file-write hook exists because posted prose travels as
--body-file/$(cat file) per the file-over-args convention, so a
command-string hook literally cannot see it; the bytes are visible when the
file is written and when the poster script assembles the final body, and both
points are covered. Known residuals: raw gh api posting that bypasses the
scripts is unlinted, and Asana comments get the authorship marker but no prose
lint today.
/build-and-test owns verification. For edge-react-gui and its dependency
repos, verification means driving the REAL user action on a booted simulator
(maestro), not just green unit tests: a dep change is not done until it runs in
the app.
- Sim slots. Each orchestrated run gets a cloned simulator from a pool,
its own Metro port, and a worktree. Pool management, cloning, and
restoration live in
agent-watcher/(ensure-sim-pool.sh,clone-ios-sim.sh,restore-sim-app-container.sh). - The sim-testing playbook
(
build-and-test/references/sim-testing-playbook.md) carries the working knowledge runs need: funded test accounts and rosters, per-provider drive recipes, known gotchas with their continue-workarounds, the corePlugins working-set trim (filter to the assets the task needs, with a funding carve-out: filtering is a convenience, never a constraint), and the fallback gates. The drive gate blocks the first maestro drive until it is read. - Flow library (
build-and-test/maestro/common/): parameterized, reusable maestro flows (login, wallet find, send-to-address, swap pair selection, ramp region/fiat, throwaway-account lifecycle, slider confirm). Task-specific flows an agent writes mid-run stay local and are excluded from sync; recurring sequences get promoted into the library through eval curation. - Attempt log. Every value-moving action and drive logs through
log-attempt.shwith a truthful result; the log is the ground truth evals and the concession gate read. Proof screenshots are pixel-verified, and hack-forced frames must carry the πͺ marker. - Cheese builds (
/cheese): push atest-*branch so testers get a build of the PR head; the orchestration pins unpublished dep PRs when the deliverable needs them. The task's Build field routes this at finalize. - Tested field. Each run sets the task's
testedmulti-select (iOS Sim, Android Sim, Android Device, Unit Tests, Untested) from that run's own evidence viaset-tested.sh.
The eval suite grades finished runs against explicit rubrics, with every BAD finding carrying a citation an auditor can open.
/resolve-runbuilds the evidence manifest per run: transcript path, PRs, Asana state, attempt log, friction block (hook blocks, tool errors, compactions), version stamps, release receipt./agent-evalgrades process compliance and outcome honesty against the agent-behavior rubric (dimensions A1-A32: status hygiene, completion honesty, report discipline, testing depth, tested-field accuracy, deferral validity, and more)./orch-evalgrades infrastructure health (fork storms, memory pressure, liveness, resource accounting, gate coverage) against the O-dims./eval-runorchestrates cohorts through a background multi-agent workflow. Two eval types, never conflated: the default REPORT-EVAL grades what a run claimed (report vs live GitHub/Asana APIs; transcripts never opened; ceiling REPORT_CLEAN), and the heavier TRANSCRIPT-EVAL (the only path to GOLD) runs the full process pass plus orch-eval on named or escalated runs. Every BAD is adversarially re-verified before it lands in a report. Cohort reports end in a typed Actions checklist (re-runs, field corrections, infra fixes, playbook and flow promotions) that the operator approves row by row; nothing executes unapproved.- Coverage ledger (
eval-coverage.sh): which runs have been evaluated under which lens, and which are STALE (new segments since their last eval) or NEVER-evaluated. Default cohort scope comes from this queue, not date guessing. - Rubric drift (
rubric-drift.sh): every rubric row anchors to the rule and script content it grades against, by content digest. A changed anchor means dimensions may grade against stale expectations; CHANGED/UNCOVERED findings block until reconciled, and rubric rows carry dated era notes so runs are graded against the rules in force when they ran. - Friction scorecard (
friction-scorecard.sh): a zero-LLM trend table between cohorts (hook blocks, tool errors, builds, compactions, drives, attempt walls) straight from manifests.
Beyond cursor skills/rules, this repo mirrors portable trees so a second Mac is
reproducible from a single clone + ./bootstrap.sh:
agent-watcher/: the autonomous orchestration system (Asana watcher daemon, session watchdog, worktree/iOS-sim pool helpers, the enforcement hooks, status/attempt/tested scripts). Canonical home is~/.config/agent-watcher. Committed: scripts,*.js,asana-config.json, docs, andcredentials.example.json. Never committed:credentials.json(secret) and machine-local state (pool.json,slots.json,watchdog-state.json,*.state,*.log, forensics).claude-settings/hooks.json: a PROJECTION of the.hookskey of~/.claude/settings.json. Hook scripts ship in the agent-watcher tree; without this projection they would be installed but never fire. User-to-repo sync exports the key; repo-to-user andbootstrap.shmerge it back replacing ONLY.hooks, so model/theme and other machine-local settings stay put, and an unconfigured machine can never blank the canonical registrations.claude-workflows/: multi-agent Workflow scripts installed to~/.claude/workflows(currentlycode-review-sonnet.js, the deep multi-agent PR review harness/pr-reviewlaunches).memory-shared/+bin/link-shared-memory.sh: cross-cutting Claude memory notes that should surface regardless of working directory. Canonical home~/.claude/memory-shared; the link script symlinks them into each per-project auto-memory dir and maintains a managed block in eachMEMORY.md. Claude auto-memory itself is machine-local and intentionally NOT synced. The only officially global Claude file is~/.claude/CLAUDE.md, generated here from always-apply rules..cursor/.syncignore: permanent sync exclusions, read from the REPO copy so every machine honors the same list. Currently: WIP commands, task-specific maestro flows (dev artifacts, not conventions), and the third-partybananaimage-generation skill (local-only by request).
/convention-sync keeps all of the above in sync (home to repo) and hard-blocks
staging when the remote is ahead, the branch is wrong, or the sync would delete
or revert canonical files authored elsewhere (the fix is a repo-to-user pass
first). bootstrap.sh does the reverse (repo to home) on a new machine.
edge-dev-agents/
βββ README.md # Synced copy of ~/.cursor/README.md
βββ bootstrap.sh # Fresh-machine installer (repo -> home)
βββ agent-watcher/ # Orchestration system incl. hooks/ (-> ~/.config/agent-watcher)
βββ claude-settings/ # hooks.json registration projection (-> ~/.claude/settings.json .hooks)
βββ claude-workflows/ # Workflow scripts (-> ~/.claude/workflows)
βββ memory-shared/ # Shared Claude memory notes (-> ~/.claude/memory-shared)
βββ bin/ # link-shared-memory.sh
βββ .cursor/
βββ skills/ # Slash skills (*/SKILL.md) + companion scripts
βββ scripts/ # Shared portability and dashboard scripts
βββ commands/ # Minimal command wrappers
βββ rules/ # Coding and workflow standards (.mdc)
Separation of concerns:
- Skills (
SKILL.md) define workflows, rules, and step ordering. - Companion scripts (
.sh,.js) handle deterministic work like git, GitHub, Asana, and JSON processing. - Hooks enforce the contracts deterministically at tool-call time.
- Rules (
.mdc) provide persistent guidance that gets loaded by context. - Repo docs describe the system and how the distribution copy fits together.
All GitHub API work uses gh CLI. Deterministic git operations should live in
scripts, not be re-described independently across skills.
| Skill | Description |
|---|---|
/one-shot |
End-to-end task flow: plan, implement, test, PR, finalize; the skill orchestrated runs execute |
/asana-plan |
Build an implementation plan from Asana or ad-hoc requirements |
/task-review |
Fetch Asana task context, summarize, and resolve the target repo by code evidence |
/im |
Implement with clean, structured commits (lint-warnings, lint-commit, history discipline) |
/build-and-test |
Build and verify: real on-sim maestro drives for GUI work, playbook + flow library |
/pr-create |
Create a PR with repo-aligned title/body, evidence, and Asana attach |
/bugbot |
Address Cursor Bugbot findings until the PR is actually clean |
/pr-address |
Address PR feedback: fixups, reply-then-resolve, mark-addressed |
/pr-review |
Review a PR: deep multi-agent pass by default, Edge-specific checklist |
/pr-land |
Land approved PRs: prepare, merge, publish, GUI dep bumps, staging cherry-picks, Asana updates |
/staging-cherry-pick |
Cherry-pick landed staging-targeted commits onto staging |
/cheese |
Push a test-branch build, pinning unpublished dep PRs when required |
/changelog |
Update CHANGELOG entries using repo conventions |
/dep-pr |
Create dependent Asana tasks and downstream PR work in another repo |
/tdd |
Write or update a technical design document for shipped work |
| Skill | Description |
|---|---|
/concession-validator |
Judge whether delivering less than the prescribed bar is legitimate; deny-on-sight taxonomy |
/blocker-validator |
Judge whether a proposed block is a true blocker or a premature yield |
/no-slop |
The prose rules; its scripts are the shared lint + judge every boundary calls |
| Skill | Description |
|---|---|
/eval-run |
Orchestrate cohort evals (report-eval default, transcript-eval on escalation) with an operator Actions checklist |
/agent-eval |
Grade one run's process compliance and outcome honesty against the rubric |
/orch-eval |
Grade one run's infrastructure health |
/resolve-run |
Build the per-run evidence manifest evals consume |
/chat-audit |
Audit chat sessions for waste, drift, and workflow gaps |
| Skill | Description |
|---|---|
/asana-task-update |
Generic Asana mutations: attach PR, assign, status, fields |
/asana-task-create |
Create Edge dev tasks on the standard boards with the right fields |
/kanban-categorize |
Sweep a kanban board and populate Category fields |
/convention-sync |
Sync ~/.cursor/ + portable trees with this repo; mirror this README; update the PR description |
/author |
Create, revise, and debug skills, scripts, and rules |
/agents-md |
Write or revise a repo's AGENTS.md agent-context file |
/q |
Answer questions before taking action |
/local-research |
Multi-agent research over the local filesystem with citation-backed reports |
/resume-session |
Find and resume the right past claude session |
/debugger |
Inspect runtime state in a running React Native app |
/fix-eslint |
Apply documented fixes for recurring ESLint warnings |
/coinhub |
Maintain the Coinhub white-label build |
/obsidian |
Manage notes in the local Obsidian vault |
/drunk-claude |
Novelty persona skill |
The banana image-generation skill exists locally but is excluded from sync
(third-party, local-only by request).
Scripts live beside the skill that owns them (<skill>/scripts/); shared
scripts live at skills/ top level. The ones most worth knowing:
| Script | What it does |
|---|---|
pr-create.sh |
Create the PR: verify, template body, prose lint (with judge), evidence, Asana attach |
pr-address.sh |
Fetch unresolved feedback (with an obligation trailer so filtered JSON cannot hide review bodies), reply, resolve, mark addressed; outbound bodies linted |
github-pr-review.sh |
Fetch PR context and submit reviews; review bodies linted at submit |
pr-finalize-fixups.sh |
Finalize fixup commits before the ready flip |
git-branch-ops.sh |
Shared deterministic autosquash and push operations |
| Script | Phase |
|---|---|
pr-land-discover.sh |
Find relevant PRs and approval state |
pr-land-comments.sh |
Detect unresolved inline, review-body, and top-level comments |
pr-land-prepare.sh |
Autosquash, rebase, detect conflicts, verify |
pr-land-merge.sh |
Rebase again, verify, merge sequentially |
pr-land-publish.sh |
Version bump, changelog, commit, tag |
upgrade-dep.sh |
Bump one package on the current branch and commit lockfile updates |
staging-cherry-pick.sh |
Cherry-pick staging-qualified commits |
changelog-union-merge.sh |
Mechanical CHANGELOG conflict resolution at land time |
verify-repo.sh |
Run changelog and code verification |
| Script | What it does |
|---|---|
lint-commit.sh |
Lint-assisted commits; scrubs session trailers/URLs from messages |
lint-warnings.sh |
Auto-fix and summarize remaining TypeScript/ESLint warnings |
no-slop-lint.sh |
The shared prose lint: mechanical tier + --semantic judge + --fragment mode |
no-slop-judge.sh |
The haiku judge stage (cached, fail-open) |
tdd-lint.sh |
TDD form lint (calls the shared prose lint) |
install-deps.sh |
Install dependencies and run project prepare steps |
rubric-drift.sh |
Anchor tracking between eval rubrics and the rules/scripts they grade against |
| Script | What it does |
|---|---|
asana-get-context.sh |
Fetch task details, comments, subtasks, attachments |
asana-task-update.sh |
Reusable Asana mutations (the report-attach path is hook-gated) |
asana-field-value.sh, asana-build-field.sh, asana-force-land.sh |
Live single-field reads the finalize gate consumes |
update-status.sh |
The gated agent_status write every phase transition goes through |
check-followup-scope.sh |
The live followup-scope + watermark check backing the Complete gate |
log-attempt.sh |
Append truthful attempt-log entries |
set-tested.sh |
Set the task's tested field from run evidence |
convention-sync.sh |
Bidirectional sync with cross-machine safety blocks |
generate-claude-md.sh |
Regenerate ~/.claude/CLAUDE.md from always-apply rules |
| Rule | Purpose |
|---|---|
act-autonomously.mdc |
Run it and investigate yourself first; ask only what is genuinely undeterminable |
answer-questions-first.mdc |
Answer user questions before editing or mutating state |
workflow-halt-on-error.mdc |
Stop on skill-script failures; fix the workflow definition before workarounds; slash-command detection |
writing-style.mdc |
Prose destinations and enforcement: em-dash scoping, no-slop, Slack conventions, link discipline |
diagram-escalation.mdc |
One diagram when an explanation covers ordering, races, or state machines |
load-standards-by-filetype.mdc |
Load language standards before editing |
no-format-lint.mdc |
No manual formatting; the commit script owns it |
typescript-standards.mdc |
TypeScript and React editing standards |
review-standards.mdc |
Review-specific bug patterns and conventions |
eslint-warnings.mdc |
Documented fixes for recurring ESLint warnings |
- Scripts over duplicated reasoning. Deterministic git, API, and parsing work belongs in shared scripts.
- Enforcement over prose. A rule that matters gets a deterministic gate (hook, script exit code, lint) at the boundary where violations ship; documentation alone does not survive compaction or a skipped file read.
- One shared implementation per rule class. The prose lint, the commit path, the status write, the trigger-precision helper: every boundary calls the shared implementation, so there is no per-surface copy to drift.
ghover raw GitHub HTTP calls. Use the authenticated CLI.- Rules before edits. Load the relevant standards before editing code or evaluating lint/type failures.
- Workflow fixes before workarounds. If a skill is wrong, fix the skill or script instead of patching around it.
- Canonical local copy.
~/.cursor/is the working source of truth;edge-dev-agentsis the distribution and review copy. - Evals close the loop. Runs are graded against anchored rubrics, findings become gates, and rubric era notes keep old runs graded by the rules in force when they ran.
- Interactive sessions over headless. Every agent runs as an interactive
claudein tmux, neverclaude -p. The pane is an interface: the operator can attach or remote-control any run mid-flight and steer it by typing; the watchdog reads it (bridge footer, parked prompts, menus) and drives it with send-keys; a run survives watcher restarts and stays alive after Complete for followups with full context. It also future-proofs billing: provider terms have singled out headless/programmatic usage for separate metering (announced, so far unenforced), and interactive sessions stay on the plan surface either way.