Docs
How We Work

Issue Management

How Metalyde tracks, triages, and sequences work across the org using roxabi-live, native GitHub relations, and label-driven triage.

How we track work

Every issue in the Metalyde repo flows into a single, queryable corpus. Triage happens in GitHub — labels, native relations, issue types. Status is derived, not set. The ops cockpit (roxabi-live) reads the full graph and surfaces what is blocked, what is ready, and what depends on what.

No Projects V2 board. No manual status columns. Labels + native relations only.


roxabi-live — the ops cockpit

"Operations cockpit for the Roxabi GitHub org — issue status, dependency graphs, and real-time sync."

Live at live.roxabi.dev.

Stack: a single Cloudflare Worker (TypeScript, Hono) + Cloudflare D1 (serverless SQLite) + a nightly Cron Trigger + a real-time GitHub org webhook.

Why it exists: GitHub's default issue list and Projects give no cross-repo dependency view — no milestone x lane pivot, no dependency graph, no single queryable corpus across repos. roxabi-live fills that gap.

Three views

ViewWhat it shows
Pivot matrixMilestones x lanes — every open issue placed at its intersection
Flat listFull corpus, filterable and searchable
SVG dependency graphNodes = issues; edges = parent/child + blocker relations; color = derived status

Filters

Multi-select on repo, milestone, priority, and status. Full-text search. Light/dark theme.

Per-issue fields tracked in D1

FieldNotes
keyGlobally unique — org/repo#number
repo, number, title, state, urlDirect from GitHub
milestone, lane, priority, sizeFrom labels
statusDerived from the dependency graph, not stored as a label
has_active_branch, is_stubLifecycle signals

How data flows

Two paths keep D1 current:

Loading diagram...
  • Nightly cron: full reconcile of all org issues.
  • Org webhook: incremental upsert on every issue event within seconds.
  • Auth: GitHub App installation tokens, AES-GCM encrypted in D1 (no PAT).

The issue lifecycle

Create → Triage (labels + relations) → Cockpit picks it up (webhook or cron) → Work via /dev #N → Close
  1. Create — open the issue with a clear title and acceptance criteria.
  2. Triage — set priority, size, lane, type, and any relations using roxabi-issues:issue-triage.
  3. Cockpit sync — the org webhook upserts the issue into D1 within seconds; the nightly cron reconciles the rest.
  4. Work — pick up ready issues via /dev #N. The dev process drives tier (S / F-lite / F-full) and the full impl lifecycle.
  5. Close — closing the GitHub issue marks it done in the graph.

Labels & types

Priority (one per issue)

LabelMeaning
P0-criticalBlocking — act immediately
P1-highImportant for current milestone
P2-mediumPlan for next cycle
P3-lowBacklog

Size / tier (one per issue)

LabelTierComplexity (kappa)
size:SS1–3
size:F-liteF-lite4–6
size:F-fullF-full7–10

Complexity score: kappa = round(files×0.20 + risk×0.25 + arch×0.25 + unknowns×0.15 + domains×0.15). Legacy aliases (XS→S, M→F-lite, L/XL→F-full) are accepted on input.

Lane (one per issue)

graph:lane/{lane} where {lane} is one of a1, a2, a3, b, c1, c2, c3, d..o, or standalone.

Native issue types (org-level, not labels)

feat | fix | docs | test | chore | ci | perf | refactor | epic | research

Status — derived, not set

Status is computed from the dependency graph at read time:

  • Closed issue → done
  • Open issue with at least one open blocker → blocked
  • Open issue with no open blockers → ready

Status labels (status:Backlog, status:In Progress, etc.) exist in the taxonomy but are not set by triage. The graph is the source of truth.


Dependencies & hierarchy

Use GitHub native relations — not markdown checklists.

RelationMutationTooling
Parent / child (sub-issues)addSubIssue GraphQL mutationroxabi-issues:issue-triage
Blocked-byaddBlockedBy GraphQL mutationroxabi-issues:issue-triage

Deferred follow-up rule

When a follow-up B is deferred from issue A: B becomes a sibling under the shared parent (not a child of A), and B is marked blocked-by A for traceability.

Using roxabi-issues:issue-triage

The roxabi-issues:issue-triage skill owns all label and relation mutations. Auth via GITHUB_TOKEN or gh auth token fallback.

# List all issues; show untriaged
/issue-triage list
/issue-triage list --untriaged

# Triage an existing issue
/issue-triage set 42 --size F-lite --priority P1-high --lane a1 --type feat

# Add relations
/issue-triage set 91 --blocked-by 117
/issue-triage set 164 --parent 163
/issue-triage set 163 --add-child 164,165

# Remove relations
/issue-triage set 91 --rm-blocked-by 117
/issue-triage set 164 --rm-parent

# Create a fully triaged issue in one shot
/issue-triage create \
  --title "feat: account billing dashboard" \
  --body "Surface current plan, usage, and upcoming invoice." \
  --size F-lite --priority P1-high --lane b --type feat \
  --parent 200 --blocked-by 180

Key flags: --size --priority --lane --type --blocked-by --blocks --rm-blocked-by --rm-blocks --parent --add-child --rm-parent --rm-child. For create: also --title (required), --body (optional), --label.


The dependency graph

roxabi-live stores edges in a D1 edges table keyed (src_key, dst_key, kind). Two kinds: parent and blocker.

The graph endpoint (/api/graph) serves JSON nodes + edges. The cockpit renders it client-side as an SVG.

Status computation per node:

ConditionStatus
Issue is closeddone
Issue is open AND has at least one open blocker edgeblocked
Issue is open AND no open blocker edgesready

This means sequencing is automatic: close the blocker and the dependent issue flips from blocked to ready in the next webhook upsert.


Why it matters

One source of truth for every Metalyde issue. Dependency-aware sequencing without manual status management. Cross-repo visibility in a single dashboard. No Projects V2 board to maintain.

"Labels + native relations only — no Projects V2 board."

On this page