Skip to content

feat(projects): add auto-close automation for inactive work items#296

Merged
martian56 merged 1 commit into
Devlaner:mainfrom
cavidelizade:feat/project-auto-close
Jul 11, 2026
Merged

feat(projects): add auto-close automation for inactive work items#296
martian56 merged 1 commit into
Devlaner:mainfrom
cavidelizade:feat/project-auto-close

Conversation

@cavidelizade

@cavidelizade cavidelizade commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Feature summary

The "Auto-close work items" project setting was a non-functional toggle (local React state only). It now persists and drives a real automation that closes inactive work items, mirroring the existing auto-archive feature.

Linked issues / discussion

Closes #193

User-facing behavior

In Project settings → Automations, the "Auto-close work items" toggle now persists. When enabled, you pick a duration (1/3/6/12 months). Work items that are still active (not completed or cancelled) and have been untouched for that long are automatically moved into the project's cancelled ("closed") state by a background sweep.

What changed

API (apps/api/)

  • model/project.go: Project.CloseIn (months; 0 = off). The close_in column already exists in the initial schema, so no migration is needed.
  • store/issue.go: CloseInactiveBefore moves non-archived, non-draft items that are not already in a completed/cancelled state (including items with no state) and were last touched before the cutoff into the project's cancelled state (lowest-sequence cancelled state). Projects with no cancelled state are skipped. Bumps updated_at so a freshly-closed item gets its own clock.
  • store/project.go: ListWithAutoClose (projects with close_in > 0).
  • service/automation.go: RunAutoClose sweeps every opted-in project.
  • cmd/api/main.go: the existing 6h in-process ticker now runs auto-close alongside auto-archive.
  • service/project.go + handler/project.go: close_in threaded through project Update, validated (ErrInvalidCloseIn, 400 on negative).

UI (apps/web/)

  • SettingsPage: the toggle + a months selector read and persist close_in via projectService.update (mirrors the auto-archive control).
  • projectService / types.ts: close_in field.

Database

  • No schema changes (close_in already exists on projects).

Why this design

Auto-close is the direct sibling of the just-merged auto-archive (#194), so it reuses that shape end to end: a per-project months field, a store sweep, an AutomationService method, and the same 6h ticker. The semantic difference is that auto-close targets active (non-terminal) stale items and moves them into the cancelled group, whereas auto-archive targets settled items and sets archived_at.

Test plan

  • go vet ./... and full go test ./... green (testcontainers).
  • New TestAutoClose_ClosesOnlyInactiveStaleIssues, TestAutoClose_DisabledProjectUntouched, TestAutoClose_NoCancelledStateSkips.
  • npm run typecheck, npm run lint, prettier green.
  • Manual, against the running stack: toggled auto-close on (persisted close_in = 3), the months selector appeared, changed it to 6 (close_in = 6), reloaded — the switch stayed on with "6 months" selected.

Out of scope (follow-ups)

  • None.

Rollout notes

None (no migration; the column already exists).

AI assistance

  • AI tools were used — tool(s): Claude Code — and AI-assisted commits include a Co-Authored-By: trailer

Checklist

  • PR title follows Conventional Commits and is <= 100 chars
  • New env vars documented — none
  • No --no-verify bypass on the commit (full suite ran on pre-commit)
  • Acceptance criteria from the linked issue are all met

Summary by CodeRabbit

  • New Features

    • Added an optional auto-close automation for inactive work items.
    • Configure the inactivity period in months from project settings.
    • Inactive items are moved to a cancelled state when the configured period elapses.
    • Auto-close can be disabled by setting the period to zero.
  • Bug Fixes

    • Invalid auto-close settings are now rejected with a clear validation error.

The "Auto-close work items" project setting was a non-functional,
local-state-only toggle. It now persists and drives a real automation,
mirroring the existing auto-archive feature.

Backend: Project.CloseIn (months; 0 = off, column already in the schema);
IssueStore.CloseInactiveBefore moves non-archived, non-draft items that are
not already in a completed/cancelled state and have been untouched past the
cutoff into the project's cancelled ("closed") state, skipping projects with
no cancelled state; AutomationService.RunAutoClose sweeps every opted-in
project; the existing 6h in-process ticker runs it alongside auto-archive.
close_in is threaded through project Update.

Frontend: the settings toggle + a months selector read and persist close_in
via projectService.update.

Closes Devlaner#193

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cavidelizade cavidelizade requested a review from a team as a code owner July 10, 2026 20:23
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds configurable project auto-close automation across the API, scheduled backend processing, issue storage, tests, and project settings UI. Inactive non-terminal items can be moved to a cancelled state after a configured number of months.

Changes

Project Auto-Close

Layer / File(s) Summary
Project auto-close contracts
apps/api/internal/model/project.go, apps/api/internal/service/project.go, apps/api/internal/handler/project.go
Adds the close_in project field, validates non-negative values, and passes the setting through project updates.
Auto-close storage operations
apps/api/internal/store/project.go, apps/api/internal/store/issue.go
Lists enabled projects and bulk-updates eligible inactive issues to the lowest-sequence cancelled state.
Automation execution and validation
apps/api/internal/service/automation.go, apps/api/cmd/api/main.go, apps/api/internal/service/automation_test.go
Runs auto-close on the periodic ticker and tests stale, disabled, terminal, repeat-run, and missing-state cases.
Settings persistence and UI
apps/web/src/api/types.ts, apps/web/src/services/projectService.ts, apps/web/src/pages/SettingsPage.tsx
Adds client types and persists the auto-close toggle and inactivity duration from project settings.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related issues

Suggested labels: enhancement, API, UI

Poem

I’m a bunny with settings to save,
Closing stale tasks to a cancelled cave.
Months tick by, the timer sings,
Active work keeps its little wings.
Toggle, persist, and hop along—
Auto-close now joins the song!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is conventional, concise, and accurately summarizes the new auto-close automation.
Description check ✅ Passed The description covers the feature, linked issue, behavior, implementation, testing, rollout, and migration notes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/api/internal/service/project.go (1)

213-218: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

close_in validation doesn't enforce the documented 1/3/6/12-month enum.

Only negative values are rejected; any positive integer (e.g. 2, 100) is silently accepted, even though the PR objective and UI limit choices to 1, 3, 6, or 12 months. A client bypassing the UI (or a future bug) can persist an unsupported value, and the automation in RunAutoClose will still act on it without error.

♻️ Proposed fix to enforce the allowed set
 	if closeIn != nil {
-		if *closeIn < 0 {
+		if *closeIn != 0 && *closeIn != 1 && *closeIn != 3 && *closeIn != 6 && *closeIn != 12 {
 			return nil, ErrInvalidCloseIn
 		}
 		p.CloseIn = *closeIn
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/internal/service/project.go` around lines 213 - 218, Update the
close_in validation in the relevant project update method to accept only the
documented values 1, 3, 6, or 12; reject zero, negative values, and any other
integers with ErrInvalidCloseIn before assigning p.CloseIn.
apps/web/src/pages/SettingsPage.tsx (1)

285-306: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting a shared helper/component for auto-archive and auto-close.

persistAutoClose (Lines 287-305) is structurally identical to persistAutoArchive (Lines 265-283), and the "Auto-close work items" toggle block (Lines 2671-2719) mirrors the "Auto-archive closed work items" block almost line-for-line. With two now-symmetric automations, extracting a generic persistAutomationField(field: 'archive_in' | 'close_in', enabled, months, ...) helper and a shared <AutomationToggle> subcomponent (icon, label, description, months selector, toggle) would remove the duplication and prevent the two flows from drifting apart as more automations are added.

Also applies to: 417-419, 2671-2719

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/pages/SettingsPage.tsx` around lines 285 - 306, The auto-archive
and auto-close settings duplicate persistence logic and toggle UI. Extract a
shared helper, such as persistAutomationField, parameterized by archive_in or
close_in, and a reusable AutomationToggle component covering the icon, text,
month selector, and toggle; update persistAutoArchive, persistAutoClose, and
both settings blocks to use these shared abstractions while preserving their
distinct labels and descriptions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/api/internal/service/project.go`:
- Around line 213-218: Update the close_in validation in the relevant project
update method to accept only the documented values 1, 3, 6, or 12; reject zero,
negative values, and any other integers with ErrInvalidCloseIn before assigning
p.CloseIn.

In `@apps/web/src/pages/SettingsPage.tsx`:
- Around line 285-306: The auto-archive and auto-close settings duplicate
persistence logic and toggle UI. Extract a shared helper, such as
persistAutomationField, parameterized by archive_in or close_in, and a reusable
AutomationToggle component covering the icon, text, month selector, and toggle;
update persistAutoArchive, persistAutoClose, and both settings blocks to use
these shared abstractions while preserving their distinct labels and
descriptions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 661b362e-b38c-4bde-94c7-6eac474cb9e4

📥 Commits

Reviewing files that changed from the base of the PR and between 68a0e64 and 11a892d.

📒 Files selected for processing (11)
  • apps/api/cmd/api/main.go
  • apps/api/internal/handler/project.go
  • apps/api/internal/model/project.go
  • apps/api/internal/service/automation.go
  • apps/api/internal/service/automation_test.go
  • apps/api/internal/service/project.go
  • apps/api/internal/store/issue.go
  • apps/api/internal/store/project.go
  • apps/web/src/api/types.ts
  • apps/web/src/pages/SettingsPage.tsx
  • apps/web/src/services/projectService.ts

@martian56 martian56 merged commit 6902ac0 into Devlaner:main Jul 11, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] Implement project auto-close automation for inactive work items

2 participants