From d3c1b8bee36e0493d6a18b88cc14c277514a7cd5 Mon Sep 17 00:00:00 2001 From: Oriol Torrent Florensa Date: Fri, 3 Jul 2026 12:32:53 +0200 Subject: [PATCH 1/3] feat(unic-archon-dlc): add /cleanup operational janitor (redesign step 11) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ships the last off-line box: /cleanup, a repo-global operational janitor that reports (and, on explicit opt-in, prunes) the debris an Archon-driven lifecycle accumulates — merged/stale worktrees, stale branches/PRs, and stale workflows// artifact dirs. Why a command, not an Archon workflow: /cleanup mutates sibling worktrees, branches, and PRs, so it cannot run inside an isolated worktree (ADR-0017). It composes archon isolation/complete for worktree/branch lifecycle and the configured tracker (MCP-first, CLI-fallback) for PR/branch state — no tracker-adapter lib (ADR-0016/0018). Why report-first: destructive actions honour the repo's never-auto-delete and never-touch-LICENSE norms. Pruning requires --apply plus a per-category confirmation; cleanup.dry_run defaults true. A slug dir is prunable only if its PR/branch is merged or closed (cleanup.prune_slug_dirs defaults false). Also retires the legacy unic-dlc-cleanup.yaml + command stub, whose arch-review + ADR-consolidation content moved to /improve-architecture in v0.9.0 (ADR-0027 deferred the disposal to this step). - new commands/cleanup.md - cleanup config block (stale_days/dry_run/prune_slug_dirs) + tests - ADR-0028 + index row; AGENTS/CONTEXT/redesign-README sweep - bump 0.10.0 + CHANGELOG Co-Authored-By: Claude Opus 4.8 --- .../.archon/commands/unic-dlc-cleanup.md | 53 ----- .../.archon/workflows/unic-dlc-cleanup.yaml | 136 ------------ .../.claude-plugin/marketplace.json | 2 +- .../.claude-plugin/plugin.json | 2 +- apps/claude-code/unic-archon-dlc/AGENTS.md | 1 + apps/claude-code/unic-archon-dlc/CHANGELOG.md | 15 ++ apps/claude-code/unic-archon-dlc/CONTEXT.md | 16 ++ .../unic-archon-dlc/commands/cleanup.md | 208 ++++++++++++++++++ .../adr/0028-cleanup-operational-janitor.md | 74 +++++++ .../unic-archon-dlc/docs/adr/README.md | 1 + .../unic-archon-dlc/docs/redesign/README.md | 32 +-- .../unic-archon-dlc/lib/config-schema.mjs | 1 + apps/claude-code/unic-archon-dlc/package.json | 2 +- .../test/config-schema.test.mjs | 27 +++ 14 files changed, 362 insertions(+), 208 deletions(-) delete mode 100644 apps/claude-code/unic-archon-dlc/.archon/commands/unic-dlc-cleanup.md delete mode 100644 apps/claude-code/unic-archon-dlc/.archon/workflows/unic-dlc-cleanup.yaml create mode 100644 apps/claude-code/unic-archon-dlc/commands/cleanup.md create mode 100644 apps/claude-code/unic-archon-dlc/docs/adr/0028-cleanup-operational-janitor.md diff --git a/apps/claude-code/unic-archon-dlc/.archon/commands/unic-dlc-cleanup.md b/apps/claude-code/unic-archon-dlc/.archon/commands/unic-dlc-cleanup.md deleted file mode 100644 index ea971d7f..00000000 --- a/apps/claude-code/unic-archon-dlc/.archon/commands/unic-dlc-cleanup.md +++ /dev/null @@ -1,53 +0,0 @@ -# /unic-dlc-cleanup - -Post-merge cleanup for a planning session: architecture review and ADR consolidation. - -## Usage - -``` -/unic-dlc-cleanup -``` - -Where `` is the session identifier used throughout the plan → build → qa cycle. - -## What this command does - -1. **`arch-review` node** — reads `docs/workflow//PRD.md` (intent) and - `docs/workflow//report.md` (technical outcome) alongside the changed code. - Detects three categories of drift: - - - **Technical drift**: too-shallow modules, tight coupling, leaky abstractions. - - **Intent drift**: delivered behaviour that diverges from the PRD; silently dropped - acceptance criteria; scope creep added during build. - - **Deepening opportunities**: modules that could hide more complexity behind their - current interface. - - Output: `docs/workflow//arch-review.md` - -2. **`adr-consolidation` interactive node** — presents each proposed ADR individually for - human approval. Sources: - - - "Decisions Made" section of `report.md` - - "Accept as ADR" items from `arch-review.md` - - Each ADR is shown with Context, Decision, and Consequences. The user accepts (A), - rejects (R), or edits (E) each candidate. Only accepted ADRs are written to `docs/adr/`. - -## Prerequisites - -- The build and QA cycles for `` must be complete (build PR merged, QA approved) -- `docs/workflow//PRD.md` and `docs/workflow//report.md` must exist -- `.archon/unic-dlc.config.json` must be present - -## Outputs - -| File | Description | -| ------------------------------------- | ----------------------------------------------- | -| `docs/workflow//arch-review.md` | Architecture review with drift findings | -| `docs/adr/NNNN-*.md` | Accepted ADRs (one file per accepted candidate) | - -## Runs - -``` -archon workflow run unic-dlc-cleanup --input slug= -``` diff --git a/apps/claude-code/unic-archon-dlc/.archon/workflows/unic-dlc-cleanup.yaml b/apps/claude-code/unic-archon-dlc/.archon/workflows/unic-dlc-cleanup.yaml deleted file mode 100644 index 0de01632..00000000 --- a/apps/claude-code/unic-archon-dlc/.archon/workflows/unic-dlc-cleanup.yaml +++ /dev/null @@ -1,136 +0,0 @@ -name: unic-dlc-cleanup -description: > - Post-merge cleanup: architecture review and ADR consolidation. - Catches technical and intent drift and locks architectural decisions. - -inputs: - slug: - description: Planning session identifier — matches the slug from the completed build cycle. - required: true - -nodes: - - id: arch-review - name: Architecture Review — Drift Detection - type: prompt - depends_on: [] - prompt: | - You are running the unic-archon-dlc cleanup workflow — arch-review node. - - Goal: compare what was intended (PRD) against what was shipped (report + code) and - surface any drift before the project moves on. - - ## Step 1 — Load context - - Read the following files in full: - - docs/workflow/{{ inputs.slug }}/PRD.md (intent) - - docs/workflow/{{ inputs.slug }}/report.md (technical outcome) - - docs/workflow/{{ inputs.slug }}/issues.json (issue breakdown) - - Also read the list of changed files from the build: - Run: git diff --name-only HEAD~1 HEAD - (or adapt the range if HEAD~1 is not the merge commit) - - For each changed source file, read its content. - - ## Step 2 — Detect technical drift - - Review the shipped code against architectural best practices: - - - **Too-shallow modules**: functions or classes that are mere pass-throughs with no - real logic (a deep module hides complexity behind a small interface — the inverse is - the anti-pattern here). - - **Tight coupling**: direct imports of internal implementation details across module - boundaries; callers that know about the internals of their dependencies. - - **Leaky abstractions**: functions that expose internal data structures, require - callers to know about implementation order, or have error modes that only make sense - if you understand the internals. - - For each issue found, note: file path, line(s), problem description, suggested fix. - - ## Step 3 — Detect intent drift - - Compare the PRD's user stories and acceptance criteria against the implementation: - - - Delivered behaviour that diverges from what the PRD describes (not the same as tech - debt — this is wrong behaviour, not merely imperfect code). - - Acceptance criteria from the PRD that were silently dropped or narrowed during build. - - Features added during build that were not in the PRD (scope creep). - - For each issue found, note: PRD section referenced, what was described vs what was - delivered, recommended action (fix now / create follow-up issue / accept as ADR). - - ## Step 4 — Identify deepening opportunities - - List at least one module that could be deepened to hide more complexity behind its - current interface — or explicitly state "No deepening opportunities identified." - - ## Step 5 — Write arch-review.md - - Write docs/workflow/{{ inputs.slug }}/arch-review.md with the following sections: - - ## Architecture Review — {{ inputs.slug }} - - ### Technical Drift - - - ### Intent Drift - - - ### Deepening Opportunities - - - ### Summary - CLEAN | ISSUES FOUND (with count) - - Print: "arch-review written to docs/workflow/{{ inputs.slug }}/arch-review.md" - - - id: adr-consolidation - name: ADR Consolidation — Per-ADR Approval Gate - type: interactive - fresh_context: true - depends_on: [arch-review] - prompt: | - You are running the unic-archon-dlc cleanup workflow — adr-consolidation node. - - Goal: review every architectural decision recorded during the build cycle and get - explicit human approval before writing any ADR to docs/adr/. - - ## Step 1 — Collect ADR candidates - - Gather decisions from: - 1. `docs/workflow/{{ inputs.slug }}/report.md` — "Decisions Made" section - 2. `docs/workflow/{{ inputs.slug }}/arch-review.md` — any decisions surfaced during review - (e.g. "accept as ADR" items from intent drift findings) - 3. Any inline ADR drafts written by the build workflow nodes - - For each candidate, present it in this format: - - --- - ADR Candidate N of M - Proposed file: docs/adr/NNNN-.md - Status: [Proposed] - Context: - Decision: - Consequences: - --- - - Accept (A) / Reject (R) / Edit (E)? - - ## Step 2 — Gate each ADR individually - - Wait for the user to respond with A, R, or E for each candidate: - - **A (Accept)**: write the ADR to docs/adr/NNNN-.md using the next - available NNNN (check existing files in docs/adr/). - Print: "ADR written: docs/adr/NNNN-.md" - - **R (Reject)**: skip this candidate. - Print: "ADR skipped: " - - **E (Edit)**: show the ADR draft again and allow the user to revise the - Context, Decision, or Consequences fields. Re-present for A/R after edits. - - ## Step 3 — Summary - - After all candidates are processed, print: - - adr-consolidation complete. - Accepted: N | Rejected: M | Edited then accepted: K - Files written: (list of docs/adr/NNNN-*.md paths, or "none") diff --git a/apps/claude-code/unic-archon-dlc/.claude-plugin/marketplace.json b/apps/claude-code/unic-archon-dlc/.claude-plugin/marketplace.json index fca5779f..dcd3c1a9 100644 --- a/apps/claude-code/unic-archon-dlc/.claude-plugin/marketplace.json +++ b/apps/claude-code/unic-archon-dlc/.claude-plugin/marketplace.json @@ -21,7 +21,7 @@ "name": "unic-archon-dlc", "source": "./", "tags": ["productivity", "workflow", "ai-development"], - "version": "0.9.0" + "version": "0.10.0" } ] } diff --git a/apps/claude-code/unic-archon-dlc/.claude-plugin/plugin.json b/apps/claude-code/unic-archon-dlc/.claude-plugin/plugin.json index 9d14e3d5..1f3e4e93 100644 --- a/apps/claude-code/unic-archon-dlc/.claude-plugin/plugin.json +++ b/apps/claude-code/unic-archon-dlc/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "unic-archon-dlc", - "version": "0.9.0", + "version": "0.10.0", "description": "A complete Archon-powered AI development lifecycle as an installable DLC pack — explore, specs, tickets, build, pr-review, qa, triage, improve-architecture, and cleanup workflows.", "author": { "name": "Unic AG", diff --git a/apps/claude-code/unic-archon-dlc/AGENTS.md b/apps/claude-code/unic-archon-dlc/AGENTS.md index c7bdf7f9..d81ea3f1 100644 --- a/apps/claude-code/unic-archon-dlc/AGENTS.md +++ b/apps/claude-code/unic-archon-dlc/AGENTS.md @@ -65,6 +65,7 @@ Load-bearing invariants. These either originate in a Plugin ADR or are policy de - **`/qa` gates on `gates.qa` and fail-closes the merge.** The QA Archon pipeline (e2e → coverage → UAT → verify-pr-base → merge) has two `approval:` gates (UAT + merge), both HITL by default and skipped in AFK; downstream nodes use `trigger_rule: all_done` so AFK auto-merges a clean build, while the merge node's fail-closed `when` blocks auto-merging a red e2e/coverage or a wrong PR base. `/qa` is also an **issue-producing on-ramp**: a UAT rejection files each defect directly as a `ready-for-agent` tracker issue (composing the configured tracker + `classification.labels`, Matt's `qa` brief shape, AI disclaimer) that feeds `/tickets` — it does not just halt. Never `lib/tracker-adapter.mjs` (dissolved). See [ADR-0025](docs/adr/0025-qa-pipeline-onramp.md). - **`/pr-review` is a self-contained fan-out Archon review that harvests, never depends.** The PR-review Archon pipeline (bootstrap → guard → prep → seven parallel aspect nodes → synthesize → reconcile → review-gate → post) reviews the open PR and posts a summary comment + inline comments via the configured tracker (MCP-first, CLI-fallback). It **harvests `unic-pr-review`'s review learnings** (confidence→severity rubric, structured summary, hidden-marker idempotency, conditional spawn table, two-surface posting) with **no ADO code and no runtime dependency** on that plugin. `prep` composes **one Intent Brief** (linked work items + Confluence/MD docs + PR description + `PRD.md`) that is **injected into every aspect** (the quality lever) and surfaces cross-source contradictions; intent-check is skipped only when no source resolves. Re-review is first-class — a dedicated `reconcile` node keyed on the `<!-- unic-dlc-pr-review:iteration=N -->` marker (never author identity) classifies findings new / still-present / fixed / regressed. The `review-gate` confirms before posting `when gates.pr-review == hitl` (AFK posts directly); posting is advisory — the real merge gate is `/qa`. Never `lib/tracker-adapter.mjs` (dissolved). See [ADR-0026](docs/adr/0026-pr-review-generic-archon-harvest.md). - **`/improve-architecture` is a skill that composes Matt's method and owns ADR superseding.** The off-line, on-demand arch-health box is a **command/skill** (its core is a design grilling loop that needs the live conversation — [ADR-0017](docs/adr/0017-container-follows-structural-need.md)), not an Archon workflow. It **composes `improve-codebase-architecture` verbatim** (technical drift + deepening HTML report + `/grilling` loop, with `/codebase-design` vocabulary + `/domain-modeling`) and **earns its place** ([ADR-0021](docs/adr/0021-earns-its-place-compose-verbatim.md)) by adding three DLC layers the raw skill lacks: an **intent-drift** pass (PRD stories/ACs vs shipped), a **durable `arch-review.md`** artifact under `<artifacts_dir>/` ([ADR-0015](docs/adr/0015-workflows-slug-artifact-home.md)), and an **ADR-consolidation gate with superseding**. Two modes: `<slug>` → intent-grounded against that build session; no-arg → repo-wide sweep (dated artifact, intent-drift skipped). Superseding works across **both ADR homes** (plugin-local + repo-root), never deletes an ADR (old status → `Superseded by ADR-NNNN`, index updated in the matching `README.md`). Config load is **lenient** (off-line, touches no tracker); **no new config key**, no `lib/` change, no auto-hook (PLAN #8). It does **not** touch the legacy `unic-dlc-cleanup.yaml` — that is `/cleanup`'s (step 11) scope. See [ADR-0027](docs/adr/0027-improve-architecture-skill-superseding.md). +- **`/cleanup` is the repo-global operational janitor — a report-first command, not an Archon workflow.** It prunes the debris a lifecycle accumulates: merged/stale worktrees, stale branches/PRs, and stale `<artifacts_dir>/<slug>/` dirs. It **composes** Archon's own `archon isolation list` / `archon isolation cleanup [days] [--merged] [--include-closed]` / `archon complete <branch>` for worktree/branch lifecycle and the configured tracker (`tracker.access`, MCP-first/CLI-fallback) for PR/branch state — never a `tracker-adapter` lib (dissolved). It is a **command** because it mutates sibling worktrees/branches/PRs and so cannot run inside an isolated worktree ([ADR-0017](docs/adr/0017-container-follows-structural-need.md)). It **defaults to a report-only dry-run and never auto-deletes**: pruning needs `--apply` plus an explicit **per-category** confirmation, and `cleanup.dry_run: true` (the shipped default) keeps even `--apply` in report mode until overridden. A slug dir is prunable **only if** its PR/branch is merged or closed (`cleanup.prune_slug_dirs` defaults `false`); slug-dir pruning skips any dir containing a `LICENSE` (repo policy). It reads the new `cleanup` config block (`stale_days`/`dry_run`/`prune_slug_dirs`); config load is lenient (off-line). This step **retired** the legacy `unic-dlc-cleanup.yaml` + command stub, whose arch-review + ADR-consolidation content moved to `/improve-architecture` ([ADR-0027](docs/adr/0027-improve-architecture-skill-superseding.md)). See [ADR-0028](docs/adr/0028-cleanup-operational-janitor.md). - **Dogfooding note.** This monorepo has had Setup run against it; the generated artefacts live under [`docs/agents/`](../../../docs/agents/) at the repo root and are managed by the marker-delimited block in the root [`AGENTS.md`](../../../AGENTS.md). Those files describe the target two-axis architecture (see [ADR-0016](docs/adr/0016-dlc-thin-process-layer.md)–[ADR-0018](docs/adr/0018-generic-core-config-compose.md)) with the tracker as the single source of truth, and should be treated as the canonical agent guidance for this repo, not as a current-practice snapshot. ## External dependencies diff --git a/apps/claude-code/unic-archon-dlc/CHANGELOG.md b/apps/claude-code/unic-archon-dlc/CHANGELOG.md index 034a42aa..781843e4 100644 --- a/apps/claude-code/unic-archon-dlc/CHANGELOG.md +++ b/apps/claude-code/unic-archon-dlc/CHANGELOG.md @@ -11,6 +11,21 @@ ### Fixed - (none) +## [0.10.0] — 2026-07-03 + +### Breaking +- (none) + +### Added +- **`/cleanup` repo-global operational janitor command** (ADR-0028) — reports (and, on explicit opt-in, prunes) the debris an Archon-driven lifecycle accumulates: **merged/stale worktrees**, **stale branches/PRs**, and **stale `<artifacts_dir>/<slug>/` dirs**. It is a **Claude Code command, not an Archon workflow** (it mutates sibling worktrees/branches/PRs, so it cannot run inside an isolated worktree — ADR-0017), **composing** Archon's own `archon isolation list` / `archon isolation cleanup [days] [--merged] [--include-closed]` / `archon complete <branch>` for worktree/branch lifecycle and the configured tracker (`tracker.access`, MCP-first/CLI-fallback) for PR/branch state — no `tracker-adapter` lib. **Report-first and never auto-deletes:** pruning requires `--apply` **plus** an explicit **per-category** confirmation, and `cleanup.dry_run: true` (the shipped default) keeps even `--apply` in report mode until overridden. A slug dir is prunable **only if** its PR/branch is merged or closed (`cleanup.prune_slug_dirs` defaults `false`); slug-dir pruning skips any dir containing a `LICENSE` (repo policy). Config load is lenient (off-line); degrades to defaults when config or the tracker is absent. +- **`cleanup` config block** in `.archon/unic-dlc.config.yaml` — `stale_days` (default 7), `dry_run` (default true), `prune_slug_dirs` (default false). Added to `defaultConfig()` with merge/validate test coverage; **not** a mandatory path, so existing configs stay valid and auto-fill the block on next merge. + +### Removed +- **Retired the legacy `unic-dlc-cleanup` Archon workflow + command stub** (`.archon/workflows/unic-dlc-cleanup.yaml`, `.archon/commands/unic-dlc-cleanup.md`). Its arch-review + ADR-consolidation content was harvested into `/improve-architecture` in v0.9.0 (ADR-0027); the `cleanup` name now belongs to the operational janitor (ADR-0028). + +### Fixed +- (none) + ## [0.9.0] — 2026-07-03 ### Breaking diff --git a/apps/claude-code/unic-archon-dlc/CONTEXT.md b/apps/claude-code/unic-archon-dlc/CONTEXT.md index 2dd53fb6..dc45d98d 100644 --- a/apps/claude-code/unic-archon-dlc/CONTEXT.md +++ b/apps/claude-code/unic-archon-dlc/CONTEXT.md @@ -154,6 +154,22 @@ Architecture Decision Record. Written to `docs/adr/NNNN-*.md` only after explici approval in the `/improve-architecture` command/skill (which also supersedes stale ADRs). _Avoid_: decision doc, architecture note +### Operational hygiene + +**Operational cleanup**: +The git/Archon hygiene the `/cleanup` command performs — pruning merged/stale worktrees, stale +branches/PRs, and stale `workflows/<slug>/` artifact dirs. It composes `archon isolation` / +`archon complete` + the configured tracker; it is report-first and never auto-deletes. Distinct from +**arch-review** (the `/improve-architecture` code-health sense of "cleanup"): operational cleanup +touches no code and writes no ADRs. See `docs/adr/0028-cleanup-operational-janitor.md`. +_Avoid_: cleanup (bare — ambiguous with arch-review), garbage collection + +**cleanup config block**: +The `.archon/unic-dlc.config.yaml` `cleanup` keys the `/cleanup` command reads: `stale_days` +(default 7), `dry_run` (default true), `prune_slug_dirs` (default false). Off-line and non-mandatory +— the command degrades to these defaults when config or the tracker is absent. +_Avoid_: cleanup settings, janitor config + ### PR-review artifacts **Review comment**: diff --git a/apps/claude-code/unic-archon-dlc/commands/cleanup.md b/apps/claude-code/unic-archon-dlc/commands/cleanup.md new file mode 100644 index 00000000..58b42d47 --- /dev/null +++ b/apps/claude-code/unic-archon-dlc/commands/cleanup.md @@ -0,0 +1,208 @@ +--- +argument-hint: '[--apply | (empty = report-only dry-run)]' +description: 'Repo-global operational janitor: report (and, on explicit opt-in, prune) the merged/stale worktrees, stale branches/PRs, and stale workflows/<slug>/ artifact dirs an Archon-driven lifecycle accumulates. Composes archon isolation/complete + the configured tracker; report-first, never auto-deletes.' +--- + +# unic-archon-dlc:cleanup + +> Design rationale: [ADR-0028 — `/cleanup` is the repo-global operational janitor](docs/adr/0028-cleanup-operational-janitor.md) (container per [ADR-0017](docs/adr/0017-container-follows-structural-need.md); compose-don't-reimplement per [ADR-0016](docs/adr/0016-dlc-thin-process-layer.md); tracker is the single source of truth per [ADR-0013](docs/adr/0013-tracker-single-source-of-truth.md); artifact home per [ADR-0015](docs/adr/0015-workflows-slug-artifact-home.md)). + +**Arguments:** "$ARGUMENTS" + +`/cleanup` is the **off-line operational janitor**: it prunes the debris an Archon-driven lifecycle +accumulates — **merged/stale worktrees**, **stale branches/PRs**, and **stale `workflows/<slug>/` +artifact dirs**. It is a **repo-global** box: it inspects and mutates sibling worktrees, branches, +and PRs, so it **cannot** run inside an isolated worktree — that is exactly why it is a **Claude +Code command, not an Archon workflow** ([ADR-0017](docs/adr/0017-container-follows-structural-need.md)). + +It is a **thin composing wrapper**: it **owns the _what_** — deciding what is prunable and enforcing +the report-first / per-category-confirm posture — and **composes the _how_**: Archon's own +`archon isolation` / `archon complete` commands for worktree/branch lifecycle, and the configured +tracker (`TRACKER.access`, MCP-first / CLI-fallback `gh` / `az` / `jira`) for PR and branch state. +Compose those tools — never reimplement them, and never introduce a `tracker-adapter` lib +([ADR-0016](docs/adr/0016-dlc-thin-process-layer.md) / [ADR-0018](docs/adr/0018-generic-core-config-compose.md)). + +> **This is not `/improve-architecture`.** The name `cleanup` was repointed: the old arch-review + +> ADR-consolidation content moved to `/improve-architecture` ([ADR-0027](docs/adr/0027-improve-architecture-skill-superseding.md)). +> `/cleanup` is git/Archon hygiene only — it touches no code, no ADRs, and no `arch-review.md`. + +**Destructive-action posture (load-bearing).** `/cleanup` **defaults to a report-only dry-run** and +**never auto-deletes**. Deletion happens only when invoked with `--apply` **and** confirmed by the +user **per category**. Never create, copy, or delete a `LICENSE` file (repo policy) — slug-dir +pruning explicitly skips them. + +Follow these steps in order. Do not skip any step. + +> **Shell requirement**: Step 1 uses `<<'EOJS'` heredoc syntax, which requires a POSIX-compatible +> shell. On Windows, run inside WSL2 or Git Bash; cmd.exe and PowerShell do not support heredocs. All +> filesystem work uses Node's `node:fs`/`node:path`, so paths are cross-platform. + +## Step 1 — Load config (lenient; tracker read-only) + +`/cleanup` reads (never writes) `.archon/unic-dlc.config.yaml`. Like `/improve-architecture` it is an +**off-line** box, so a missing or incomplete config is **non-blocking** — it degrades to defaults and +continues. It does compose the tracker **read-only** (to check PR/branch state); if no tracker +resolves, the PR/branch-state and slug-dir categories degrade with a warning rather than halting. Run: + +```bash +node --input-type=module <<'EOJS' +let output +try { + const { pathToFileURL } = await import('node:url') + const mod = await import(pathToFileURL(`${process.env.CLAUDE_PLUGIN_ROOT}/lib/config-schema.mjs`).href) + const { existsSync } = await import('node:fs') + const { join } = await import('node:path') + const cwd = process.cwd() + + const pick = (config) => ({ + artifacts_dir: config.artifacts_dir, + tracker: config.tracker, + cleanup: config.cleanup, + project: config.project, + }) + + const yamlPath = join(cwd, '.archon', 'unic-dlc.config.yaml') + if (!existsSync(yamlPath)) { + // Off-line box: no config is fine — fall back to defaults and continue. + output = { ok: true, degraded: true, reason: 'no-config', ...pick(mod.mergeConfig()) } + } else { + const r = mod.loadConfig(yamlPath) + if ('error' in r) { + output = { ok: true, degraded: true, reason: `config-unreadable: ${r.message}`, ...pick(mod.mergeConfig()) } + } else { + output = { ok: true, degraded: false, ...pick(mod.mergeConfig(r.config)) } + } + } +} catch (err) { + // Even a plugin load error should not stop an off-line janitor — default and warn. + output = { + ok: true, + degraded: true, + reason: `plugin-load: ${err?.message ?? String(err)}`, + artifacts_dir: 'workflows', + tracker: null, + cleanup: { stale_days: 7, dry_run: true, prune_slug_dirs: false }, + project: null, + } +} +process.stdout.write(JSON.stringify(output) + '\n') +EOJS +``` + +Parse the JSON. Keep `ARTIFACTS_DIR` (default `workflows`), `TRACKER` (`.type`/`.access`/`.coords`, +may be `null`), `CLEANUP` (`.stale_days` default `7`, `.dry_run` default `true`, `.prune_slug_dirs` +default `false`), and `PROJECT` (`.branching`/`.pr_strategy`, for the main branch). If `degraded` is +`true`, print a one-line warning naming `reason` and note the fallbacks, then continue. If `TRACKER` +is `null` or its `type` is unset, warn that PR/branch-state detection and slug-dir pruning will be +skipped (they need the tracker), then continue. + +## Step 2 — Determine mode + +Read `$ARGUMENTS`: + +- **Empty (or anything other than `--apply`) → report-only dry-run.** Enumerate and report every + prunable item; delete nothing. This is the default and the safe path. +- **`--apply` → apply mode.** After reporting, offer to prune **per category** with an explicit + confirmation for each. Even here, if `CLEANUP.dry_run` is `true` (the shipped default), treat the + run as report-only unless the user explicitly confirms they want to override the configured + dry-run default for this run. + +State the resolved mode to the user before continuing (e.g. `Mode: report-only dry-run` or +`Mode: --apply (config dry_run=true → confirm to override)`). + +## Step 3 — Enumerate worktrees & branches (compose `archon isolation`) + +Run `archon isolation list` and parse the output (branch name, worktree path, workflow type, +platform, last-activity age). If the command is unavailable, warn that Archon isolation is not +installed and skip this category (non-blocking). + +Classify each environment: + +- **merged** — its branch is merged into the main branch (`PROJECT.branching` decides the main + line: `gitflow` → `develop`/`main`, `github-flow` → `main`). Archon's `--merged` uses a union of + ancestry (`git branch --merged`), patch-equivalence (`git cherry`), and PR state, which safely + catches squash-merges — rely on that signal rather than re-deriving it. +- **stale** — last activity older than `CLEANUP.stale_days` days, not yet merged. +- **active** — recent and unmerged; never prunable. Report it as retained, with its age. + +## Step 4 — Detect stale PRs & branches (compose the tracker) + +Only if `TRACKER` resolved. Compose `TRACKER.access` (MCP-first, CLI-fallback `gh` / `az` / `jira`, +or the `azure-devops-cli` skill) against `TRACKER.coords` to list open PRs and their branches — +**never hardcode `gh`**. Flag as candidates: + +- PRs whose branch has already been merged (a leftover open PR), and +- PRs with no activity for longer than `CLEANUP.stale_days`. + +Report these read-only; closing them is opt-in in Step 7. An already-merged PR is normally closed by +the merge itself — surface only genuine leftovers. + +## Step 5 — Detect prunable slug artifact dirs + +Only if `TRACKER` resolved (a slug dir's disposition depends on its PR/branch state). Using +`node:fs`, list the immediate child directories of `<ARTIFACTS_DIR>/` (default `workflows/`). Each +child dir name is a Slug ([ADR-0015](docs/adr/0015-workflows-slug-artifact-home.md)). For each: + +- Resolve its PR/branch via the tracker + the isolation list from Step 3. +- A slug dir is **prunable only if** its PR/branch is **merged or closed**. A slug with an **open + PR or an in-flight (active) branch is never prunable** — report it as retained. +- If `CLEANUP.prune_slug_dirs` is `false` (the shipped default), **report** prunable slug dirs but do + **not** offer to delete them in Step 7 (note that pruning is disabled by config). + +Never treat `<ARTIFACTS_DIR>/arch-review-*.md` or any loose file as a slug dir — only directories. + +## Step 6 — Report + +Print a per-category summary the user can scan. One block per category, each row with the reason it +is prunable (or retained): + +``` +/cleanup report — mode: <report-only dry-run | --apply> + Worktrees: <N prunable> (merged: A, stale >Dd: B) · <M retained active> + - <branch> <path> <merged | stale 12d> + Branches: <N prunable> + - <branch> <merged, worktree gone> + PRs: <N leftover/stale> (tracker: <type>) + - #<id> <title> <branch merged | stale 30d> + Slug dirs: <N prunable> (<workflows/<slug>/>) [pruning <enabled | disabled by config>] + - <slug> <PR #id merged | PR #id closed> + Retained: <items intentionally kept, with why> +``` + +In report-only mode this is the final output — stop here after printing the "next step" hint +(`re-run with --apply to prune`). + +## Step 7 — Apply (only with `--apply`; per-category confirmation) + +Never auto-delete. For each category with prunable items, show the concrete command and the exact +items, then ask the user to confirm **that category** (yes/no). On confirmation, compose: + +- **Merged worktrees/branches** → `archon isolation cleanup --merged` (this also removes the merged + remote branches). Add `--include-closed` **only** if the user separately opts in to also pruning + worktrees whose PRs were **closed without merging**. +- **Stale worktrees** → `archon isolation cleanup <CLEANUP.stale_days>`. +- **A specific branch's full lifecycle** (worktree + local/remote branch) → `archon complete <branch>`. + Use this for a targeted removal the bulk `isolation cleanup` did not cover. +- **Stale/leftover PRs** → close via the composed `TRACKER.access` (e.g. the tracker MCP close tool, + or `gh pr close` / `az repos pr update --status abandoned`), never a hardcoded CLI. Opt-in only. +- **Prunable slug dirs** (only if `CLEANUP.prune_slug_dirs` is `true`) → remove the directory with + Node's `node:fs` (`rm` recursive). **Before deleting, scan the dir for any `LICENSE` file; if one + is present, skip that dir and warn the maintainer to handle it manually** (repo LICENSE policy). + +Report what each confirmed action did (or the error, surfaced — never swallowed). A declined +category is left fully intact. + +## Step 8 — Summary + +Print a concise summary: + +``` +/cleanup complete — mode: <report-only dry-run | --apply> + worktrees: <pruned N | reported N, none deleted> + branches: <pruned N | reported N> + PRs: <closed N | reported N, none closed> + slug dirs: <pruned N | reported N | pruning disabled by config> + skipped: <LICENSE-guarded dirs / declined categories, or none> + next: run /cleanup again after the next batch of merges; + cadence is off-line / on-demand — there is no auto-hook. +``` diff --git a/apps/claude-code/unic-archon-dlc/docs/adr/0028-cleanup-operational-janitor.md b/apps/claude-code/unic-archon-dlc/docs/adr/0028-cleanup-operational-janitor.md new file mode 100644 index 00000000..29dd1376 --- /dev/null +++ b/apps/claude-code/unic-archon-dlc/docs/adr/0028-cleanup-operational-janitor.md @@ -0,0 +1,74 @@ +# 0028. `/cleanup` is the repo-global operational janitor + +**Status:** Accepted (2026-07-03) + +## Context + +`/cleanup` is the last off-line box in the box set ([ADR-0014](0014-workflow-per-box-decomposition.md)). +The name was **repointed** by the two-axis redesign: the shipped `unic-dlc-cleanup` Archon workflow +held arch-review + ADR-consolidation content, which was harvested into `/improve-architecture` in +step 10 ([ADR-0027](0027-improve-architecture-skill-superseding.md)). ADR-0027 explicitly deferred +disposing of the dormant `unic-dlc-cleanup.yaml` + its command stub to this step. + +The maintainer's original intent for the name is git/Archon **hygiene**: an Archon-driven lifecycle +accumulates debris — merged/stale worktrees, leftover branches, stale open PRs, and stale +`workflows/<slug>/` artifact dirs ([ADR-0015](0015-workflows-slug-artifact-home.md)) — that no other +box removes. There is no Matt Pocock analog for this operational concern. + +Three questions were grilled with the maintainer (2026-07-03): + +1. **Container** — Archon workflow or Claude Code command? +2. **Config surface** — hardcode staleness thresholds or add a config block? +3. **Destructive posture** — how aggressive by default, and how are slug dirs judged? + +## Decision + +### 1. A repo-global Claude Code command, not an Archon workflow + +`/cleanup` inspects and mutates **sibling** worktrees, branches, and PRs. An Archon workflow runs +inside an isolated worktree and cannot safely prune the very worktrees around it. By the +[ADR-0017](0017-container-follows-structural-need.md) litmus (repo-global state → command/skill), it +is a **Claude Code command**. It **composes the _how_** and owns only the _what_ +([ADR-0016](0016-dlc-thin-process-layer.md)): Archon's own `archon isolation list` / +`archon isolation cleanup [days] [--merged] [--include-closed]` / `archon complete <branch>` for +worktree/branch lifecycle, and the configured tracker (`tracker.access`, MCP-first / CLI-fallback +`gh` / `az` / `jira`) for PR and branch state. No `tracker-adapter` lib +([ADR-0018](0018-generic-core-config-compose.md)). + +### 2. A `cleanup` config block + +`.archon/unic-dlc.config.yaml` gains a `cleanup` block: `stale_days` (default `7`, matching Archon's +own default), `dry_run` (default `true`), and `prune_slug_dirs` (default `false`). Staleness is a +legitimate per-project tunable, so — unlike `/improve-architecture` +([ADR-0027](0027-improve-architecture-skill-superseding.md)) — this box earns a small config surface. +No key is added to `MANDATORY_PATHS`: the box is off-line and degrades to these defaults when config +is missing or the tracker is unresolved. + +### 3. Report-first, per-category opt-in; slug dirs judged by PR state + +`/cleanup` **defaults to a report-only dry-run** and **never auto-deletes**. Deletion happens only +under `--apply` **and** an explicit **per-category** confirmation; `dry_run: true` (the shipped +default) keeps even `--apply` in report mode until the user overrides for that run. This honours the +repo's destructive-action norms and the never-touch-`LICENSE` policy (slug-dir pruning skips any dir +containing a `LICENSE`). A slug dir is **prunable only if** its PR/branch is **merged or closed** — +an open PR or in-flight branch is never prunable, regardless of age. + +### 4. Retire the legacy `unic-dlc-cleanup` files here + +This step deletes `.archon/workflows/unic-dlc-cleanup.yaml` and +`.archon/commands/unic-dlc-cleanup.md`. Their content lives on in `/improve-architecture`; leaving a +dormant `cleanup`-named Archon workflow whose behaviour contradicts the new operational-janitor +meaning would be confusing. + +## Consequences + +- **Operational hygiene is now a first-class, safe box.** Consumers run `/cleanup` on a cadence + (after a batch of merges); it reports by default and prunes only on explicit opt-in. +- **One new config key group (`cleanup`)** with a `defaultConfig` default and merge/validate tests; + no new mandatory path, so existing configs remain valid and auto-fill the block on next merge. +- **The legacy `unic-dlc-cleanup` workflow + stub are removed**, not left dormant — `/improve-architecture` + is now the sole home of the arch-review + ADR-consolidation content. +- **No `lib/` beyond the config default; no `tracker-adapter`.** All worktree/branch/PR access is + composed (Archon CLI + configured tracker), keeping the tested lib tracker-agnostic. +- **Manual follow-up:** an end-to-end prune against real merged worktrees and a stale slug dir is not + asserted by CI; it is exercised on demand by consumers, per the report-first posture. diff --git a/apps/claude-code/unic-archon-dlc/docs/adr/README.md b/apps/claude-code/unic-archon-dlc/docs/adr/README.md index 20174864..8bfb0c17 100644 --- a/apps/claude-code/unic-archon-dlc/docs/adr/README.md +++ b/apps/claude-code/unic-archon-dlc/docs/adr/README.md @@ -33,3 +33,4 @@ Plugin-scoped architectural decisions. Repo-wide decisions live in `docs/adr/` a | 0025 | `/qa` is an Archon pipeline with two config-gated approvals + an issue-producing on-ramp | Accepted | | 0026 | `/pr-review` is a generic fan-out Archon workflow harvesting unic-pr-review's learnings | Accepted | | 0027 | `/improve-architecture` is a skill composing Matt's method + owns ADR superseding | Accepted | +| 0028 | `/cleanup` is the repo-global operational janitor; retires the legacy cleanup workflow | Accepted | diff --git a/apps/claude-code/unic-archon-dlc/docs/redesign/README.md b/apps/claude-code/unic-archon-dlc/docs/redesign/README.md index 7a0f038e..c7ca360e 100644 --- a/apps/claude-code/unic-archon-dlc/docs/redesign/README.md +++ b/apps/claude-code/unic-archon-dlc/docs/redesign/README.md @@ -10,22 +10,22 @@ This directory drives the refactor of `unic-archon-dlc` into a **thin, Matt-alig > > Legend: ✅ done · ⏭️ skip (not a build) · ▶️ next · ⬜ todo -| # | Step | Container | Status | PR / notes | -| --- | ----------------------- | ----------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 00 | Archon schema pre-work | — | ✅ done | ADR-0011 (#262) | -| 01 | Foundations | — | ✅ done | ADRs 0012–0021 + AGENTS/CONTEXT sweep (#262) | -| 02 | `/handoff` | Matt's, ref | ⏭️ skip | referenced verbatim, not shipped; dependency declared in `/setup` | -| 03 | `/setup` | skill | ✅ done | conversational skill + `config-schema` lib; rich YAML config (#263) | -| 04 | `/specs` | skill | ✅ done | branch-on-input command; template → `templates.prd`; `specs.{discuss_mode,gate}` (#264) | -| 05 | `/tickets` | skill | ✅ done | command; stops at build-ready `issues.json` (no DAG gen); `tickets` config; ADR-0022 (#265) | -| 06 | `/build` | Archon | ✅ done | keystone — ported to key-discriminated schema as one generic red/green/refactor loop over `issues.json`; dag-builder dissolved; ADR-0023 (PR #266) | -| 07 | `/triage` | skill | ✅ done | thin wrapper over Matt's `triage` method; single-source label binding (DLC config); retires old triage workflow; ADR-0024 (PR #267) | -| 08 | `/qa` | Archon | ✅ done | ported to key-discriminated schema; e2e → coverage → UAT + merge gates (`gates.qa`, `all_done` AFK auto-merge, fail-closed merge); UAT-reject files agent-ready issues; `qa` config block; ADR-0025 (PR #268) | -| 09 | `/pr-review` | Archon | ✅ done | new generic fan-out workflow (7 intent-grounded aspects → synthesize → reconcile → gate → post); harvests unic-pr-review learnings, no dependency; iteration-aware re-review; `pr-review` config; ADR-0026 (PR #269) | -| 10 | `/improve-architecture` | skill | ✅ done | command/skill composing Matt's `improve-codebase-architecture` verbatim + DLC layers (intent-drift, durable `arch-review.md`, ADR-superseding gate across both homes); two modes (per-slug / repo-wide sweep); ADR-0027 (PR #270) | -| 11 | `/cleanup` | command | ▶️ next | new — repo-global janitor | -| 12 | `/explore` | Archon | ⬜ todo | `/prototype` NOT built (Matt's referenced skill) | -| 13 | Finalize | — | ⬜ todo | reduced — `Unic-dlc.mmd` diagram + dogfood dry-run only (sweep done) | +| # | Step | Container | Status | PR / notes | +| --- | ----------------------- | ----------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 00 | Archon schema pre-work | — | ✅ done | ADR-0011 (#262) | +| 01 | Foundations | — | ✅ done | ADRs 0012–0021 + AGENTS/CONTEXT sweep (#262) | +| 02 | `/handoff` | Matt's, ref | ⏭️ skip | referenced verbatim, not shipped; dependency declared in `/setup` | +| 03 | `/setup` | skill | ✅ done | conversational skill + `config-schema` lib; rich YAML config (#263) | +| 04 | `/specs` | skill | ✅ done | branch-on-input command; template → `templates.prd`; `specs.{discuss_mode,gate}` (#264) | +| 05 | `/tickets` | skill | ✅ done | command; stops at build-ready `issues.json` (no DAG gen); `tickets` config; ADR-0022 (#265) | +| 06 | `/build` | Archon | ✅ done | keystone — ported to key-discriminated schema as one generic red/green/refactor loop over `issues.json`; dag-builder dissolved; ADR-0023 (PR #266) | +| 07 | `/triage` | skill | ✅ done | thin wrapper over Matt's `triage` method; single-source label binding (DLC config); retires old triage workflow; ADR-0024 (PR #267) | +| 08 | `/qa` | Archon | ✅ done | ported to key-discriminated schema; e2e → coverage → UAT + merge gates (`gates.qa`, `all_done` AFK auto-merge, fail-closed merge); UAT-reject files agent-ready issues; `qa` config block; ADR-0025 (PR #268) | +| 09 | `/pr-review` | Archon | ✅ done | new generic fan-out workflow (7 intent-grounded aspects → synthesize → reconcile → gate → post); harvests unic-pr-review learnings, no dependency; iteration-aware re-review; `pr-review` config; ADR-0026 (PR #269) | +| 10 | `/improve-architecture` | skill | ✅ done | command/skill composing Matt's `improve-codebase-architecture` verbatim + DLC layers (intent-drift, durable `arch-review.md`, ADR-superseding gate across both homes); two modes (per-slug / repo-wide sweep); ADR-0027 (PR #270) | +| 11 | `/cleanup` | command | ✅ done | new repo-global operational janitor (report-first; composes `archon isolation`/`complete` + tracker); `cleanup` config block; retired legacy `unic-dlc-cleanup.yaml` + stub (content now in `/improve-architecture`); ADR-0028 (PR #TBD) | +| 12 | `/explore` | Archon | ▶️ next | `/prototype` NOT built (Matt's referenced skill) | +| 13 | Finalize | — | ⬜ todo | reduced — `Unic-dlc.mmd` diagram + dogfood dry-run only (sweep done) | ## How to run a step diff --git a/apps/claude-code/unic-archon-dlc/lib/config-schema.mjs b/apps/claude-code/unic-archon-dlc/lib/config-schema.mjs index 9e440687..ca78540d 100644 --- a/apps/claude-code/unic-archon-dlc/lib/config-schema.mjs +++ b/apps/claude-code/unic-archon-dlc/lib/config-schema.mjs @@ -85,6 +85,7 @@ export function defaultConfig() { triage: { out_of_scope_dir: '.out-of-scope', external_prs: 'auto' }, qa: { e2e_command: null, coverage_threshold: null }, 'pr-review': { confidence_threshold: 60, inline_comments: true }, + cleanup: { stale_days: 7, dry_run: true, prune_slug_dirs: false }, gates: { build: 'hitl', qa: 'hitl', 'pr-review': 'hitl', explore: 'hitl' }, build: { fresh_context_red_green: true, diff --git a/apps/claude-code/unic-archon-dlc/package.json b/apps/claude-code/unic-archon-dlc/package.json index 102b618f..47877214 100644 --- a/apps/claude-code/unic-archon-dlc/package.json +++ b/apps/claude-code/unic-archon-dlc/package.json @@ -1,6 +1,6 @@ { "name": "unic-archon-dlc", - "version": "0.9.0", + "version": "0.10.0", "private": true, "license": "LGPL-3.0-or-later", "type": "module", diff --git a/apps/claude-code/unic-archon-dlc/test/config-schema.test.mjs b/apps/claude-code/unic-archon-dlc/test/config-schema.test.mjs index fa6fa961..de259221 100644 --- a/apps/claude-code/unic-archon-dlc/test/config-schema.test.mjs +++ b/apps/claude-code/unic-archon-dlc/test/config-schema.test.mjs @@ -253,6 +253,33 @@ test('mergeConfig preserves a team override of pr-review, filling untouched sub- assert.equal(prReview.inline_comments, true, 'untouched sub-key filled from default') }) +test('defaultConfig ships cleanup defaults: stale_days=7, dry_run=true, prune_slug_dirs=false', () => { + const cleanup = /** @type {any} */ (defaultConfig().cleanup) + assert.deepEqual(cleanup, { stale_days: 7, dry_run: true, prune_slug_dirs: false }) +}) + +test('mergeConfig auto-fills the cleanup block for an existing config that predates it', () => { + // A config written before the cleanup block existed (e.g. the pr-review-era dogfood config). + const merged = mergeConfig( + { tracker: { type: 'github' }, project: { branching: 'gitflow', pr_strategy: 'merge' } }, + {} + ) + const cleanup = /** @type {any} */ (merged.cleanup) + assert.deepEqual( + cleanup, + { stale_days: 7, dry_run: true, prune_slug_dirs: false }, + 'cleanup block filled from default' + ) +}) + +test('mergeConfig preserves a team override of cleanup, filling untouched sub-keys', () => { + const merged = mergeConfig({ cleanup: { stale_days: 14 } }, {}) + const cleanup = /** @type {any} */ (merged.cleanup) + assert.equal(cleanup.stale_days, 14, 'existing override wins') + assert.equal(cleanup.dry_run, true, 'untouched sub-key filled from default') + assert.equal(cleanup.prune_slug_dirs, false, 'untouched sub-key filled from default') +}) + test('mergeConfig preserves a team override of triage, filling untouched sub-keys', () => { const merged = mergeConfig({ triage: { external_prs: 'never' } }, {}) const triage = /** @type {any} */ (merged.triage) From c2978acdfeae709e212c3b6e8cc40855ba8ae838 Mon Sep 17 00:00:00 2001 From: Oriol Torrent Florensa <oriol.torrent@unic.com> Date: Fri, 3 Jul 2026 12:33:31 +0200 Subject: [PATCH 2/3] docs(unic-archon-dlc): record PR #271 in the redesign progress table Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --- apps/claude-code/unic-archon-dlc/docs/redesign/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/claude-code/unic-archon-dlc/docs/redesign/README.md b/apps/claude-code/unic-archon-dlc/docs/redesign/README.md index c7ca360e..3ab69de4 100644 --- a/apps/claude-code/unic-archon-dlc/docs/redesign/README.md +++ b/apps/claude-code/unic-archon-dlc/docs/redesign/README.md @@ -23,7 +23,7 @@ This directory drives the refactor of `unic-archon-dlc` into a **thin, Matt-alig | 08 | `/qa` | Archon | ✅ done | ported to key-discriminated schema; e2e → coverage → UAT + merge gates (`gates.qa`, `all_done` AFK auto-merge, fail-closed merge); UAT-reject files agent-ready issues; `qa` config block; ADR-0025 (PR #268) | | 09 | `/pr-review` | Archon | ✅ done | new generic fan-out workflow (7 intent-grounded aspects → synthesize → reconcile → gate → post); harvests unic-pr-review learnings, no dependency; iteration-aware re-review; `pr-review` config; ADR-0026 (PR #269) | | 10 | `/improve-architecture` | skill | ✅ done | command/skill composing Matt's `improve-codebase-architecture` verbatim + DLC layers (intent-drift, durable `arch-review.md`, ADR-superseding gate across both homes); two modes (per-slug / repo-wide sweep); ADR-0027 (PR #270) | -| 11 | `/cleanup` | command | ✅ done | new repo-global operational janitor (report-first; composes `archon isolation`/`complete` + tracker); `cleanup` config block; retired legacy `unic-dlc-cleanup.yaml` + stub (content now in `/improve-architecture`); ADR-0028 (PR #TBD) | +| 11 | `/cleanup` | command | ✅ done | new repo-global operational janitor (report-first; composes `archon isolation`/`complete` + tracker); `cleanup` config block; retired legacy `unic-dlc-cleanup.yaml` + stub (content now in `/improve-architecture`); ADR-0028 (PR #271) | | 12 | `/explore` | Archon | ▶️ next | `/prototype` NOT built (Matt's referenced skill) | | 13 | Finalize | — | ⬜ todo | reduced — `Unic-dlc.mmd` diagram + dogfood dry-run only (sweep done) | From f8b24f67532ca4a9f0f922fd93a2405cc98f531e Mon Sep 17 00:00:00 2001 From: Oriol Torrent Florensa <oriol.torrent@unic.com> Date: Fri, 3 Jul 2026 12:40:26 +0200 Subject: [PATCH 3/3] docs(unic-archon-dlc): handle unset PROJECT in /cleanup merged detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copilot review (PR #271): the degraded/plugin-load config paths can leave PROJECT null, but Step 3 assumed PROJECT.branching to pick the main branch — internally inconsistent. Clarify that merged classification relies on Archon's own --merged signal (not PROJECT.branching), make branching a reporting-only hint with a git-derived fallback (git symbolic-ref → main), and add a Step 1 warning for the PROJECT-unset case. Non-blocking either way. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --- .../unic-archon-dlc/commands/cleanup.md | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/apps/claude-code/unic-archon-dlc/commands/cleanup.md b/apps/claude-code/unic-archon-dlc/commands/cleanup.md index 58b42d47..406ebc00 100644 --- a/apps/claude-code/unic-archon-dlc/commands/cleanup.md +++ b/apps/claude-code/unic-archon-dlc/commands/cleanup.md @@ -91,10 +91,13 @@ EOJS Parse the JSON. Keep `ARTIFACTS_DIR` (default `workflows`), `TRACKER` (`.type`/`.access`/`.coords`, may be `null`), `CLEANUP` (`.stale_days` default `7`, `.dry_run` default `true`, `.prune_slug_dirs` -default `false`), and `PROJECT` (`.branching`/`.pr_strategy`, for the main branch). If `degraded` is -`true`, print a one-line warning naming `reason` and note the fallbacks, then continue. If `TRACKER` -is `null` or its `type` is unset, warn that PR/branch-state detection and slug-dir pruning will be -skipped (they need the tracker), then continue. +default `false`), and `PROJECT` (`.branching`/`.pr_strategy`; a **hint** for the main branch — it may +be `null`). If `degraded` is `true`, print a one-line warning naming `reason` and note the fallbacks, +then continue. If `TRACKER` is `null` or its `type` is unset, warn that PR/branch-state detection and +slug-dir pruning will be skipped (they need the tracker), then continue. If `PROJECT` is `null` or +`PROJECT.branching` is unset (the plugin-load and no-config fallbacks leave it so), warn that the main +branch will be derived from git rather than config — merged detection still works (Step 3), so this is +non-blocking. ## Step 2 — Determine mode @@ -118,10 +121,13 @@ installed and skip this category (non-blocking). Classify each environment: -- **merged** — its branch is merged into the main branch (`PROJECT.branching` decides the main - line: `gitflow` → `develop`/`main`, `github-flow` → `main`). Archon's `--merged` uses a union of - ancestry (`git branch --merged`), patch-equivalence (`git cherry`), and PR state, which safely - catches squash-merges — rely on that signal rather than re-deriving it. +- **merged** — its branch is merged into the main branch. Do **not** depend on `PROJECT.branching` + for correctness: Archon's `--merged` computes merged-into-main itself via a union of ancestry + (`git branch --merged`), patch-equivalence (`git cherry`), and PR state, which safely catches + squash-merges — rely on that signal rather than re-deriving it. `PROJECT.branching` is only a + **reporting hint** for which line is "main" (`gitflow` → `develop`/`main`, `github-flow` → `main`); + when it is unset (per Step 1), derive the main branch from git instead + (`git symbolic-ref --short refs/remotes/origin/HEAD`, falling back to `main`) — never block on it. - **stale** — last activity older than `CLEANUP.stale_days` days, not yet merged. - **active** — recent and unmerged; never prunable. Report it as retained, with its age.