Skip to content

· Synx Data Labs

The Monday IPM, In Depth

How Synx Data Labs commits a week of engineering work with ccxp-skills: one queue-ordered commit list sized by judgment instead of a budget formula, the IPM file as the week's single source of truth, and the hard gate that keeps the backlog from quietly stranding work.

At Synx Data

Last week’s post laid out the shape of an engineering week run on ccxp-skills — three fixed rituals and a continuous build loop underneath them. This post goes deep on the first of those rituals: the Monday Iteration Planning Meeting, run by the /ipm skill in ccxp-skills, which is public — everything below is something you can go read in ipm/SKILL.md yourself.

An IPM’s job is narrow but important: decide, once a week, exactly what gets built, and write that decision down somewhere both a human and an unattended cron loop can check it against. The output is a single file, dev/JOURNAL/YYYY-MM-DD-ipm-weekly.md, that the daily standup grades progress against all week and the Friday retro grades final on.

One list, in queue order

Everything an IPM commits lands in a single list, walked straight down dev/TODO/queue.md’s existing priority order — there’s no separate scoring pass and no fixed partition between “already running” and “new” work. What varies per row is how it got there:

  • Auto-carried. Any task already In Progress or in Review is automatically in this week’s commit — no re-litigating work that’s already underway. The same rule extends to any task with a non-empty claim, even if its status is still Design or Open: a live session (this clone, or a peer) already committed to it, so it carries too. Skipping that check would mean a claimed-but-not-yet-coded task is invisible to the IPM, never gets its scheduled: date advanced, and silently drifts off the current iteration on the board.
  • New picks. Fresh work, pulled from /todo next’s flat top-to-bottom walk of queue.md — there’s no separate scoring; the queue’s position is the priority — plus anything staged mid-week via /stage.

There’s a guard against carry-over becoming a place where work quietly never ships: if a task has carried in the last two committed IPM files and would be a third, it gets flagged “Bumped 2x — reassess” and forced to an explicit call — re-commit with a stated reason, mark it won’t-fix, or defer it to a later Monday. An unattended run never blocks on that decision; it auto-defers and posts one line to Slack so a human can override later. The point isn’t to punish slow tasks, it’s to stop a comfortable “we’ll get to it” line from absorbing accountability without ever shipping.

The pre-IPM design pass — human-only, by design

Before a new pick counts toward the budget, it needs a design: what the fix actually is, roughly how big it is. That pass is /incept — a short, timeboxed grilling (10-15 minutes) that interviews whoever’s running the IPM, confirms or revises the task’s story-point estimate, and writes the result into the task file.

This step never runs unattended. When CCXP_CRON_MODE=1 (the nightly cron invocation), it’s skipped outright, and the eligibility filter tightens to match: only candidates already at status: Design with an estimate on file are eligible for that week’s commit — a task still sitting at Open hasn’t had its design pass yet, and the cron has no one to interview. If that leaves the pool thinner than the budget could otherwise absorb, the IPM says so in its ## Notes section rather than quietly reaching into Open tasks to fill the gap. It’s one of the clearest examples of the interactive/unattended split running through the whole system: bookkeeping runs the same in both modes, but a judgment call about whether a task is actually ready waits for a human.

No budget — just judgment, walking the queue

An earlier version of this skill tried to formalize that trade-off: a weekly ~20-hour capacity budget, split into three explicit lines (~12 hours product, ~4 hours infrastructure/tooling, ~4 hours slack), so carry-over-versus-new-work wasn’t an invisible call. It never actually ran — not once, in this skill’s own history. And once task estimation moved from duration buckets to XP story points, there was nothing left to sum against an hours-denominated line anyway.

So it’s gone. Carry-over commits unconditionally, same as always. Everything else — new picks from step 2, plus carry-over candidates from step 1.5 — gets walked top-down in queue.md’s existing order, and the person running the IPM decides, by judgment, how much to take on. A task’s story-point total can inform that call, but there’s no categorical product/infrastructure/slack split and no numeric ceiling the skill itself enforces — pick what you can realistically land, and stop there. Infrastructure and tooling work doesn’t need a protected line to get picked, either: if it’s next in the queue and worth doing now, it’s just the next row in the commit list, with a “why this now” reason like anything else.

The one thing that doesn’t go away: every carry-over candidate a prior IPM deliberately deferred still needs an explicit disposition this time around — accepted into the commit list, or re-cut with a stated reason. Nothing is silently dropped just because there’s no longer a budget line to blame the drop on.

One file, one source of truth

Every decision the IPM makes — what carried, what got cut and why — lands in dev/JOURNAL/YYYY-MM-DD-ipm-weekly.md. Nothing about the week’s commitment lives anywhere else: not on an external board, not in a Slack thread. Moving a task between iterations is a one-line scheduled: frontmatter edit in the task file itself — there’s no separate system to keep in sync.

A task that gets cut isn’t just dropped — its scheduled: date advances to next Monday and it’s pre-appended to next week’s IPM stub under ## Candidates, so a deliberately-deferred task resurfaces on its own instead of needing someone to remember it. scheduled: only ever moves forward; the field’s own git history is the record of every iteration a task has touched.

The hard gate: nothing strands on a closed iteration

The riskiest failure mode isn’t a task that gets explicitly cut — those are handled. It’s a task that’s still Open or Design, has no claim on it, was scheduled into the iteration that’s now ending, and nobody touched it: not carried (it’s not in-flight or claimed), not cut (nobody looked at it this week). Silently, it strands on a now-closed iteration and the board quietly lies about what’s active.

The IPM closes that gap with a hard gate, ipm-iteration-drain-check.sh, run after the week’s picks are written and before the IPM is considered committed. It reads dev/TODO/*.md frontmatter directly — no external board, no network call — for anything still scheduled: into the previous iteration with a non-terminal status, and the commit simply isn’t final until it exits clean.

That’s the Monday ritual end to end: carry what’s in flight, pick and design what’s new by walking the queue on judgment instead of a formula, write it down in one file, and refuse to call it done while anything from last week is left stranded. Next week’s post covers the daily ritual that checks progress against this file every single day — the standup, and the unattended cron loop that runs it when nobody’s at the keyboard.