Design: dependency-aware queue sequencing
Epic-to-Epic dependencies in pitlab are invisible: 30 Epics sit in the delivery queue with one dependency link between them, so nothing stops work whose prerequisite is unfinished from reaching the top of the list. This design makes those dependencies captured facts — recorded by a human at the moment work is groomed or designed — and makes them sort the queue rather than gate it. No new skill is built; three existing skills gain a capability and one weekly detective watches whether the practice is actually happening.
Prior art — the Layer 0 sweep, and what it changed
Section titled “Prior art — the Layer 0 sweep, and what it changed”Board swept across eleven mechanism-class vocabularies, title and description: roadmap,
dependency, critical path, sequenc, prioriti, graph, keystone, what next, planning,
depmap, link. Corpus swept with two phrasings — dependency-mapping/critical-path, then
forcing-function/reconciler-drift — across designs/, adr/ and standards/.
No existing Epic covers Epic-dependency sequencing, so this design is not a second copy. Four hits changed it materially:
| Prior art | Verdict |
|---|---|
| Epics #2420 “1. claude-config as a governed repo”, #2441 “2. Agent permissions as governed config”, #2436 “3. Blocked-action deferral for /go”, #2251 “4. Delivery verbs — /go replaces /justdoit” | Does not cover the goal, but supplies a precedent. A human already sequences related Epics by hand using an ordinal title prefix, and #2441’s title reads “blocked on 4”. The [N] prefix this design keeps is therefore existing convention, not an invention |
| Design: /groom and its debt detective | Does not cover the goal; supplies the pattern. groom_backlog_debt.py — monthly, Zabbix items plus Prometheus textfile, paging owned by triggers, an age dead-man — is the shape this design’s detective copies rather than reinvents |
| Backlog grooming runbook | Already documented that ChangedDate is unusable for age: “any bulk edit, re-parent or tag fix bumps ChangedDate”. A live measurement this session re-derived that fact independently; it should have been retrieved, not rediscovered |
| Epic #2348, Grill Gate | Adjacent, and the reason a gate was rejected here. It is a fail-closed deploy-time design-authority gate with thirteen resolved decisions and, by its own status note, “no code, no standard, no pipeline wiring”. Building a second design-authority control before the first exists is how controls become bypassable |
| Epic #2260 servicemap, ADR-0359 | Different domain. It maps service topology — what runs on what — not what blocks what. Reusing its renderer would produce one graph answering neither question cleanly |
The measurement that killed the derivation approach
Section titled “The measurement that killed the derivation approach”The design originally proposed deriving dependencies from Epic descriptions. That half was rejected after measurement, not assumed away. All 30 Epics in the Up Next and Grilled columns were pulled and read on 2026-08-17:
| Observation | Value |
|---|---|
| Epics in the queue | 30 |
| Existing dependency links between them | 1 |
| Descriptions effectively empty (<40 chars) | 4 |
| Descriptions containing any dependency phrase | 5 |
| Epic-to-Epic dependencies actually stated in text | 0 |
| Descriptions citing another work item | 18 — none citing another queued Epic |
Every one of the five phrase hits was read in full context and none survived: #1599 (“every other child depends on it”) and #2348 (“start with Issue #2349… must land atomically”) are intra-Epic child ordering; #2412 (“Batch 1 = … then …”) is intra-Epic batching; #1802 was a regex false positive on the word “Precedent”; #2102 (“prerequisite for any Proxmox firewall use”) names a dependency on a future capability that is not a queued Epic.
The knowledge is not in the cards. It exists in the owner’s head at the moment work is designed, which is why capture beats derivation here and why no derivation skill is built. Deriving over the 102 To Do Epics — 5,151 pairs, 92 of them ungroomed — would have produced a complete-looking graph whose majority was authored rather than observed (FC-19).
Decision tree
Section titled “Decision tree”Decisions
Section titled “Decisions”| Decision | Choice made | Rationale | Rejected alternatives |
|---|---|---|---|
| Edge source | Declared by a human at grooming and design time | 92 of 102 To Do Epics are ungroomed, and zero dependencies are stated in the queue’s text — there is nothing to derive | Derive across all 102 (fiction at scale); derive the 30-card queue (measured empty); declare-only with no backfill path |
| Shape | Amend /groom, /grillme, /getready; add one detective script. No new skill | /groom already walks cards with the owner in Q&A — one extra question is nearly free, and capture belongs where a human is already reading | A /depmap derivation skill (nothing to derive); a /depmap capture skill (overlaps /groom); script-only (no backfill path) |
| Forcing function | Dependencies sort, they do not block. An Epic with an unmet predecessor sinks and cannot be marked sequenced | Reuses a mechanism that already exists in /getready for stale cards; nothing to bypass; degrades safely to a merely-unsorted queue | Hard gate at /grillme (duplicates unbuilt Grill Gate #2348); detective-only (board never self-corrects — groomed_pct reached 0 with a healthy detective); artifact-only (relies on remembering to read it) |
| Clarity artifact | None on disk. Board holds the links; the agent reports critical path, root set and keystones in-session; the script emits metrics only | Owner’s ruling. Removes every drift and freshness risk a generated page carries, and matches “I am the interface — never defer him to a source he does not read” | A generated docs page with Mermaid; extending servicemap; a Grafana panel alone; board-native with no reporting path at all |
| Detective signals | Two — queue_unmapped (coverage) and unmet_predecessors (drift), plus cycles and an age dead-man | With one link across 30 Epics a drift-only trigger reads 0 whether the queue is perfect or nobody ever looked — indistinguishable states, FC-03. Same volume-plus-tail logic as the grooming detective | Drift only (green forever); coverage only (goes silent permanently once mapped); a single composite health percentage (hides which of three things moved, so the alert cannot name an action) |
| “Examined and independent” | A DepMapped: YYYY-MM-DD description stamp | Without it, “no dependencies” is indistinguishable from “never looked” — the exact gap the groomed tag closes for grooming. Reuses the Groomed: stamp pattern already live | A fifth Epic tag (vocabulary growth); nothing at all (coverage metric becomes unmeasurable) |
| Queue position | A Sequenced: YYYY-MM-DD stamp, not a ready tag | A dated stamp is self-evidently stale; a bare tag lies silently. Keeps the Epic tag vocabulary closed at four and removes the reconciliation burden a rotting tag would impose | The ready tag with an ADR-0399 amendment; StackRank only (unreadable in any report) |
| Ordinal prefix | Keep [N] in the title; amend ADR-0399’s title clause only | Already live convention on #2420 → #2251. A queue position is a value, and ADR-0399’s own (HC DD-MM-YY) clause establishes that values belong in the title | Drop the prefix (position invisible outside the board); leave it unconformant |
| Cadence | Weekly | Queue drift is actionable on the timescale work is picked up. The grooming detective’s monthly cadence suits debt that moves on weeks; a monthly nudge about this week’s queue is noise | Extend groom_backlog_debt.py (monthly cadence conflicts); daily (nothing changes that fast) |
NFR resolution
Section titled “NFR resolution”| Requirement | Resolution |
|---|---|
| Monitoring | Four Zabbix items — depmap.queue_unmapped, depmap.unmet_predecessors, depmap.cycles, depmap.age — with depmap.age as the dead-man, registered in the cron-heartbeats table. The dead-man matters more than usual here: a mapped queue publishes low numbers, so a collector that died is indistinguishable from a healthy board |
| Observability | Prometheus textfile collector scraped via Alloy, carrying the same four values for trending |
| Alerting | Alertmanager/Zabbix triggers on coverage above threshold, any cycle, any unmet predecessor, and a stale heartbeat. Paging owned by the triggers, never by the script — one alerting path, ADR-0036. Pushover only, priority 0, subject to the 21:00–07:00 quiet-hours mute |
| SBOM / vulnerability coverage | No new image and no new dependency — a stdlib Python script in an existing repo, covered by that repo’s existing Trivy and Dependency-Track posture |
| Documentation as code | This design doc, a governing ADR, the ADR-0399 title-clause amendment, and a runbook, all in the same delivery |
| Config as code | Script in ansible/scripts/ with its deployment task (RULE 6 category 1 — host cron); schedule declared in schedule_config.yml; Zabbix items and triggers deployed by playbook. Nothing manual |
| Software update and patch lifecycle | The repo-versioned script pattern, not the pinned-tag container pattern: no image, no upstream release. Bump trigger is an ordinary code change. Gated before landing by --self-test plus the trigger red-test. Rollback is git revert. Staleness here is not a stale version but a stale board model — a script whose assumptions no longer match how the board is used — detected by the dead-man and by the coverage metric behaving implausibly |
| Planned maintenance | No service disruption; the only live-system change is adding Zabbix items, covered by a maintenance window during deployment |
| Notifications | Pushover via the Zabbix trigger path. No email |
| Tagging | Docs tags from the registered mkdocs.yml vocabulary; Zabbix items carry standard tags; Prometheus metrics carry join keys; ADO fields populated |
| Enterprise pattern | Dependency-aware scheduling with critical-path awareness — the planning half of theory of constraints: sequence around the bottleneck, because the longest dependency chain is the true floor on delivery time and no amount of parallelism beats it |
| DORA / SRE | This is a planning control, not a service, so the Four Keys apply only indirectly: it targets lead time by preventing work from starting blocked. Its SLI is queue coverage — the share of queued Epics examined for dependencies. Error budget is expressed as unmapped-Epic-weeks |
| Minimal decomposition | 2 sub-issues, 1 prompt. Mechanism and governance must land atomically; rollout and the readiness review follow |
Compliance and control mapping
Section titled “Compliance and control mapping”The compliance surface here is genuinely thin, and inflating it would be theatre. This is an internal work-planning control: it touches no data, no identity, no network exposure and no third party. It is recorded honestly rather than mapped to security frameworks it does not serve.
| Control / decision | Framework mapping | How it is tested and evidenced | Residual risk |
|---|---|---|---|
| Dependencies recorded as durable links rather than held in one person’s head | NIST CSF 2.0 Govern (accountability for prioritisation decisions); ISO 27001 A.5.37 documented operating procedures | Coverage metric trends the population; the DepMapped: stamp evidences examination | A captured link can be wrong; a wrong link is read as fact by every later run |
| Detective control with a dead-man behind it | CPS 234 testing of controls, not merely implementation | Triggers proven red against a forced condition before being trusted green; dead-man proven by ageing its stamp | unmet_predecessors has no measured baseline, which ADR-0361 expects for a >0 trigger — it cannot have one until coverage exists |
| Sorting rather than gating | ISO 27001 A.8.32 change management, lightly | The sort rule proven by planting a real link and observing the demotion | A sort is advisory — nothing prevents the owner from picking a blocked Epic deliberately, which is accepted as correct behaviour |
Operational readiness and handover
Section titled “Operational readiness and handover”- Service record. A control within cc-pool agent tooling, tier T3. No host, no hostname, no NetBox asset, no material service provider. Its unavailability delays a sequencing pass and affects no running service.
- Reliability contract. No availability SLO. The operating signal is coverage: unmapped queue
Epics, trending down. When coverage stalls, the response is a
/groompass, not an escalation. - Runbook. Owed at
docs/operations/, covering how capture works inside/groom, what each of the four metrics means, how to correct a wrongly-captured link, and the failure modes. Must be walked cold by a fresh session before acceptance. - Test strategy. Per sub-issue: every trigger proven RED against a forced condition before
being trusted green;
--self-teston the reconciler; the sort rule proven by planting one real Predecessor link and observing the dependent Epic demote and lose itsSequenced:stamp. Notestplan.yamlis owed — the Service Test Plan Standard governs services with a golden path, and this is agent tooling plus a detective whose dead-man is its gate, the same reasoning accepted for/groom. - Day-2 BAU and toil. This design removes planning toil rather than adding it: the recurring
obligation is answering one extra question during a
/groompass that is already happening. The reconciler is automated and declared in the schedule config. - Backup and restore. No datastore. All state is ADO work-item metadata plus version-controlled
scripts; recovery is a
git checkoutand the links persist in ADO. - Standing owner. The weekly reconciler is the detective; its dead-man is the backstop;
/healthsurfaces Zabbix state. Nothing new needs remembering, which is the point. - Lifecycle exit. Retiring this means removing the reconciler, its schedule entry and its Zabbix items, and deciding whether the captured links stay. The links are ordinary ADO relations and survive independently of the tooling.
Delivery plan — 2 sub-issues, 1 prompt
Section titled “Delivery plan — 2 sub-issues, 1 prompt”Issue #2504 — mechanism, governance and the three skill amendments. Must land atomically: the
sort rule is meaningless without capture, and the detective measures a practice that must already
exist. Scope: /groom gains the dependency question, the Depends on: brief line and the
DepMapped: stamp; /grillme captures links at close-out; /getready drops the ready tag for a
Sequenced: stamp and gains the unmet-predecessor sort rule; depmap_reconcile.py with its cron,
schedule slot, four Zabbix items, triggers and Prometheus textfile; the ADR-0399 title-clause
amendment; the governing ADR; the runbook.
Issue #2505 — rollout, backfill and the readiness review. Scope: a real /groom pass over the
30-Epic queue so coverage has data and the drift signal becomes meaningful; the PRR with evidence —
alerts red-then-green through the real trigger, metrics populated, dead-man proven by ageing its
stamp, runbook walked cold; a two-week early-life soak reviewing captured links for wrong edges,
since a persisted wrong link is read as fact by every later run.
Deviations (as-delivered)
Section titled “Deviations (as-delivered)”Filled by /wrapup Check O at Epic close. Empty by design — nothing has been built.