Skip to main content

Cycle 381: Issue-Number Cycle IDs

Priority: MEDIUM Status: DRAFT Domain: infra Dependencies: None Product: Flux — AI Hiring Assistant for small businesses Organization: Employ Inc.

Problem

Cycle docs use manually-chosen sequential integers as identifiers. Three collisions have already occurred: cycle213 (feature flags AND deploy hardening), cycle217 (candidate identity AND nginx migration), cycle220 (select for hire AND notification system). The root cause is a global counter with no coordination mechanism — multiple engineers and AI agents independently pick “the next number” and collide.

Objective

Use the GitHub issue number as the cycle number. Issues are auto-incremented by GitHub — zero collision risk, zero new tooling, and the cycle ID doubles as a direct link to the project board.

What This Cycle Does NOT Include

  • Renaming existing merged cycle docs (they stay as cycle202, cycle203, etc.)
  • Renaming in-progress cycle docs in open PRs
  • Changes to the cycle lifecycle process (two-phase Plan PR → Code PR)
  • Changes to the cross-model review requirement

The Convention

How it works

  1. Create a GitHub issue on the Flux project for the new cycle → get #342
  2. Name the cycle doc cycle342-descriptive-name.md with cycle: 342 in frontmatter
  3. Sub-cycles follow the existing convention: cycle342.1-sub-task.md, cycle342.2-next.md
  4. PR body includes Closes #342 → board auto-closes on merge

Why this works

  • Human-friendly — “cycle 342” works in conversation, in Slack, in standups
  • Always unique — GitHub issue numbers are auto-incremented, zero collision risk
  • Zero new tooling — no counter files, no CI validators, no naming conventions to learn
  • Enforces discipline — every cycle must have a tracked project item before the doc is written
  • Traceable — cycle number = issue number, so #342 links directly to the board
  • Sub-cycles preserved342.1, 342.2 etc. work exactly as today
  • Existing automation compatiblecycle-board.yml already parses issue numbers from PR bodies; cycle-sync.yml already reads numeric cycle: values

The rule

No cycle doc may be created without a corresponding GitHub issue. The issue must exist first — its number becomes the cycle number. This is not optional. If no issue exists, create one before writing the doc. This is what makes the system collision-proof: GitHub guarantees unique issue numbers.

What changes

Numbers won’t be sequential (you might have cycles 318, 325, 341). Nobody needs them sequential — just unique and easy to reference.

Process Change

Before (old convention)

  1. Pick the next number after the highest existing cycle doc
  2. Hope nobody else picked the same number
  3. Write the cycle doc
  4. Open plan PR
  5. cycle-sync (if enabled) creates the issue from the doc

After (new convention)

  1. Create a GitHub issue first — title: Cycle: <brief description>, label: cycle
  2. Note the issue number (e.g., #342)
  3. Write the cycle doc: cycle342-descriptive-name.md with cycle: 342
  4. Open plan PR with Closes #342 in the body
  5. Issue already exists on the board — no sync needed
The key inversion: issue first, then doc instead of doc first, then issue.

Frontmatter

No changes to the cycle: field type or required fields. The value is still an integer — it just comes from GitHub instead of manual counting.
---
cycle: 342
title: "Cycle 342: Candidate Screening API"
status: draft
domain: hiring
engineer: null
issue: "#342"
priority: high
created: "2026-04-13"
dependencies: [204, 202]
tags:
  - flux
  - hiring
---
The issue: field (already defined in CONTRIBUTING.md as optional) becomes effectively required for new cycles, since the cycle number IS the issue number.

Branch, PR, and Commit Conventions

No changes to the format — just the number source:
# Branch
cycle342/plan
cycle342/impl

# Plan PR title
docs(cycle342): plan — candidate screening API

# Code PR title
feat(cycle342): candidate screening API

# PR body
Closes #342

# Commit
docs(cycle342): plan — candidate screening API

Transition Rules

CategoryAction
Merged docs (cycle133.1cycle222)Leave as-is. No rename.
In-progress PRsLeave as-is.
New cycles from here forwardCreate GitHub issue first, use issue number as cycle number.
cycle-board.ymlNo changes needed.
cycle-sync.yml.disabledConsider deprecating — issues are created manually first now, not auto-generated from docs.
Issue template (cycle.yml)No changes needed — still works for manually creating cycle issues.

Concrete Example: HC-8 Select for Hire

If HC-8 were started under the new convention:
  1. Create issue: Cycle: Select for Hire — Data Contracts → gets #342
  2. Create issue: Cycle: Select for Hire — Hire Execution → gets #343
  3. Create issue: Cycle: Select for Hire — Hire UX → gets #344
Or use sub-cycles:
  1. Create issue: Cycle: Select for Hire → gets #342
  2. Docs: cycle342-select-for-hire-backend.md, cycle342.1-hire-execution.md, cycle342.2-hire-ux.md
CurrentNew convention
cycle222-select-for-hire-backend.mdcycle342-select-for-hire-backend.md
cycle222.1-select-for-hire-execution.mdcycle342.1-hire-execution.md
cycle222.2-select-for-hire-ux.mdcycle342.2-hire-ux.md

Edge Cases

What about cycles that aren’t on the board yet? — Create the issue first. That’s the point — every cycle gets tracked before work begins. What about AI agents creating cycles autonomously? — Agents use gh issue create to get a number, then write the doc. Same flow, automated. What about sub-cycles? — Same as today: 342.1, 342.2. The parent issue is #342; sub-cycles don’t need their own issues unless they’re independently trackable. What if cycle-sync is re-enabled? — It would need to skip doc-to-issue creation (issue already exists) and only do issue-to-doc sync (update labels/status from frontmatter). Or deprecate it entirely since the manual-first flow is simpler.

Deliverables

1. Update CONTRIBUTING.md

Sections to update (by heading):
  • “Cycle Document Frontmatter” — Add note that cycle: value must be a GitHub issue number for new cycles. Note issue: field should reference the same number.
  • “Phase 1: Cycle Plan PR” — Update the workflow to start with “Create a GitHub issue” before writing the doc.
  • “Check Before You Start” — Already includes gh issue create; add explicit note that the issue number becomes the cycle number.

2. Add CI unique-ID validator

Lightweight check on PRs touching docs/roadmap/cycles/*.md:
  • Scan all cycle docs, extract cycle: value
  • Fail if any two docs share the same cycle: value (catches both legacy and new collisions)
  • Legacy duplicates (213×2, 217×2) need an allowlist until resolved

3. Consider deprecating cycle-sync.yml

With issues created manually first, the doc-to-issue sync is redundant. The workflow could be simplified to only sync labels/status, or removed entirely.

Testing

  • Verify CONTRIBUTING.md changes are clear and examples use issue numbers
  • Run unique-ID validator against all existing docs — confirm it flags known duplicates (213, 217) and passes everything else
  • Verify cycle-board.yml still works with Closes #<issue-number> in PR bodies (already confirmed by PR #310 fix)

Success Criteria

  • CONTRIBUTING.md documents “create issue first, use issue number as cycle number”
  • CONTRIBUTING.md updates Phase 1 workflow to start with issue creation
  • CI unique-ID validator blocks merge on duplicate cycle: values
  • No existing cycle docs modified
  • make quality-gates passes