#+TITLE: quotatool — Masterplan
#+AUTHOR: Johan Ekenberg
#+DATE: 2026-03-08 (revised 2026-03-15)

* AI coding - to the human reader
Yes, the rest of this file is mostly maintained by AI (Claude Code).
It's a hard decision how much of development chores to hand over to
AI, and how much to keep doing by hand. I have chosen to embrace
AI for mostly maintaining project notes, todos, research notes,
commit messages and large non-core boilerplate code generation
like the new test framework. For actual bugs and features I have
stayed in the loop, reviewing/coding manually, testing the important
bits by hand. I expect these choices will be challenged continuously
as we move forward, AI capabilities increasing seamingly
exponentially over time.

* Purpose

This document is the single source of truth for the quotatool
modernization effort. It defines the goal, the major milestones
to get there, and tracks which milestone is current.

Each milestone has a separate step-plan file (=steps/NN-name.org=)
created when that milestone becomes active. The step-plan contains
detailed tasks, decisions, and progress for that milestone only.

Background research lives in =research/=. This document references
but does not duplicate that material.

* Goal

Transform quotatool from a multi-platform legacy tool into a focused,
well-tested Linux (+BSD) utility with modern kernel API support.
Establish automated testing that makes ongoing maintenance confident
and low-effort.

* Current State (2026-03-19)

- Working tool, ~4,800 LOC of clean C, no dependencies
- Supports Linux, FreeBSD, OpenBSD
- *Test suite complete* — Linux 25 kernels (ext4+XFS), FreeBSD 14.4, OpenBSD 7.8
- *M2 DONE*: BSD testing, platform drops, code cleanup
- Bugs fixed: Q_SYNC hang, #36/#37 grace, 5 audit bugs, memory leak, uninit fields
- Dropped: macOS, AIX, Solaris, NetBSD (see PLATFORM-CHANGES.md)
- Missing support for newer kernel APIs (quotactl_fd, project quotas)
- Packaged in Debian, Ubuntu, Fedora, EPEL, Gentoo, openSUSE, Homebrew
- *v1.7.0 released* (March 2026): bug fixes + test framework
- *v1.7.1 released*: macOS install fix (#30)
- Ready for v1.8.0 release

* Strategy

*Revised 2026-03-15*: Original plan was "all scaffolding, then all
building." Revised to ship bug fixes earlier — the Linux test
framework is ready, users benefit from fixes now. BSD scaffolding
and platform drops are a separate, later release.

Order of work:

1. *M1 (DONE)*: Linux test framework — the foundation
2. *M3 (DONE)*: Bug fixes + cleanup → *released as v1.7.0*
3. *M2*: BSD testing + platform drops (+possibly M4/M5) → *release v1.8.0*
4. *M4/M5*: New features (if not bundled with M2)

The key change: M3 before M2. Rationale:
- Bug fixes benefit users immediately (grace -r affects 7 kernels)
- The test framework to verify fixes is ready (M1)
- BSD testing is unbounded effort for low user base
- Platform drops deserve their own release with advance notice
- v1.7.0 as a "quality release" (fixes + tests) is a positive message;
  "we deleted your platform" is not

* Milestones

** DONE M1: Linux Test Framework [step-plan: steps/01-testing.org]

The critical enabler for everything else. Without this, no change
can be made with confidence. *Complete as of 2026-03-15.*

Delivered:
- Clone repo → =run-tests --setup= → =run-tests --all= → full results across 25 kernels
- Validated on multiple hosts
- 16 kernel-matrix tests × 2 filesystems × 25 kernels
- Host-side test checks (arg validation, unit parsing, timespan, dump format)
- =--quick= (no VM, instant), =--host-only= (one VM), full matrix
- Alpine rootfs for RHEL kernels (musl-based, no host dependency)
- Dependency audit complete (dep stack lean, grep -oP → POSIX sed)
- All test assertions reviewed for false positives by 4 audit agents
- All failures are known quotatool bugs, not framework issues

Branch: =m1-testing= (merged to =master= as part of v1.7.0)

Inputs: =research/testing-infrastructure.org=,
        =research/multi-kernel-testing.org=

-----

*→ Release v1.7.0 after M3*

-----

** DONE M3: Bug Fixes & Codebase Cleanup [step-plan: steps/03-cleanup.org]
/Status: COMPLETE — released as v1.7.0/

Bug fixes and cleanup, verified by the full test suite.
This is the first milestone that touches =src/=.

*** Priority fixes

- *Grace period restart (-r)*: broken on kernels ≤4.12 — sets grace
  to 0 instead of restarting. Confirmed quotatool bug, 7 kernels
  affected. Fix and verify with the multi-kernel matrix.
- *+0M clears quota*: =parse_size()= returns 0 when =count==0=,
  ignoring =orig= and =op=. So =+0M= applied to 100M → 0 instead
  of being a no-op. Discovered and confirmed during M1 testing.
- *XFS grace period display*: =-d= reports =block_grace=0= after
  setting grace on XFS. Possibly a display bug or =-t= not reaching
  XFS properly (Q5 in =steps/01-testing.org=).
- *-d grace field operator precedence*: Linux code path in =main.c=
  line 109/123 has wrong C operator precedence on the cast.
  BSD code path (lines 101-107) is correct. The cast applies only
  to =block_time=, not to the ternary result.
- *"5m" timespan ambiguity*: =parse_timespan()= returns -1 for bare
  "m" (ambiguous: minutes or months), but =main.c= doesn't check
  the return value. The -1 reaches =quotactl()= unchecked.

*** Cleanup

- Remove dead code, commented debug blocks
- Static analysis (cppcheck, scan-build)
- Fix known issues (e.g. mixed indentation)
- Evaluate whether shipping custom kernel quota headers is still needed
- Ensure clean compile with =-Wall -Wextra= on gcc and clang
- Remove unused =#include <sys/cdefs.h>= from =src/linux/linux_quota.h=

*** UX: run-tests CLI review (DONE)

- Renamed =run-tests.sh= → =run-tests= (no extension)
- No-args shows help
- =--all= required for full matrix

-----

*v1.7.0 released. v1.7.1 released (macOS install fix).*

-----

** DONE M2: Platform Testing & Cleanup [step-plans: steps/02a-bsd-testing.org, steps/02b-platform-docs.org, steps/02c-platform-drops.org]
/Status: COMPLETE. M2a (BSD testing), M2b (platform docs), M2c (code drops) all done./

*** M2a: BSD Test Coverage (scaffolding)

Before dropping any platform, establish test coverage for the
platforms we're *keeping*. You don't delete code you can't test.

FreeBSD and OpenBSD share =src/bsd/quota.c= (181 LOC) — completely
different code path from Linux. Uses =quotactl()= on FreeBSD,
UFS filesystem, =quota.user=/=quota.group= files. None of this is
currently tested.

Note: there is no =src/openbsd/= — FreeBSD, OpenBSD, and NetBSD
all use =src/bsd/= via the =ANY_BSD= macro in =quotatool.h=.
=configure.ac= maps all three to =PLATFORM=bsd=.

Tasks:
- Fix BSD expired-grace display bug (#36, found during M3 #33 fix)
- Research BSD quota APIs, filesystem setup, VM options
- FreeBSD test path: QEMU + FreeBSD image, or Cirrus CI (free)
- OpenBSD test path: QEMU + OpenBSD image, or Cirrus CI
- Adapt test scripts: UFS quota setup, BSD-compatible commands
- Reuse existing test logic where possible (limits, grace, etc.)
- Verify: quotatool builds and passes basic tests on FreeBSD + OpenBSD

Result: same confidence level for BSD as we have for Linux.

*** M2b: Document Platform Decisions

Before removing code, document *why* each platform is dropped.
quotatool is packaged in Homebrew and MacPorts — dropping macOS
needs a clear public explanation, not just a commit message.

Write =PLATFORM-CHANGES.md= (or section in README) covering:
- *macOS*: APFS replaced HFS+ as default (2017). APFS has no
  traditional quota support. The quotactl() interface quotatool
  uses doesn't work on APFS. No path forward — not a temporary gap.
- *AIX*: no accessible test environment, no known users, IBM's
  proprietary JFS2 quota API has diverged.
- *Solaris/illumos*: ZFS is the standard filesystem. ZFS has its own
  quota system (=zfs set quota==) that doesn't use quotactl().
- *NetBSD*: broke their quotactl API (=__quotactl=) in a way that's
  incompatible with quotatool's approach. Would need a full rewrite
  of the NetBSD backend.
- *FreeBSD, OpenBSD*: kept. Active projects, standard UFS quotas,
  quotatool works.

Note: v1.7.0's ROADMAP.md announces these planned drops.
Notify Homebrew/MacPorts stakeholders before v1.8.0 release.

*** M2c: Drop Dead Platforms

With test coverage on the survivors and rationale documented,
safely remove the dead.

For each drop:
1. Remove =src/<platform>/= code
2. Update =configure.ac=, =Makefile.am=
3. Run full test suite (Linux + BSD) — verify no regressions
4. Commit per platform (atomic, revertible)

Result: smaller, honest codebase. Updated README, configure, Makefile.

Inputs: =research/other-os-relevance.org=

*** Possibly: bundle M4/M5 into v1.8.0

Decision deferred until after v1.7.0 release. Options:
- *v1.8.0 = M2 + M4 + M5*: one big release with platform changes
  AND new features. Gives distro maintainers a reason to repackage
  (quotactl_fd + project quotas are real value-adds).
- *v1.8.0 = M2 only, v1.9.0 = M4+M5*: smaller releases in quick
  succession. Less risk per release but more packaging churn.

The distro-maintainer argument favors bundling: they won't rush
to package a release that only removes platforms. Adding quotactl_fd
(tmpfs/bcachefs quotas) and project quotas gives them actual value.

-----

*→ Release v1.8.0 here*

-----

** TODO M4: quotactl_fd() Support [step-plan: steps/04-quotactl-fd.org]
/Status: NOT STARTED — possibly bundled with M2 in v1.8.0/

Add support for the quotactl_fd() syscall (kernel 5.14+). This
enables quota management on tmpfs (kernel 6.6+) and bcachefs —
filesystems that have no block device path and thus can't use
the original quotactl().

Inputs: =research/linux-kernel-quota-api.org=

** TODO M5: Project Quota Support [step-plan: steps/05-project-quotas.org]
/Status: NOT STARTED — possibly bundled with M2 in v1.8.0/

Add =-p= flag for project quotas (quotas on directory trees).
Supported by XFS (always) and ext4 (since kernel 4.4). Useful for
container storage, web hosting docroots, CI build directories.

Inputs: =research/linux-kernel-quota-api.org=

** M6: Release Mechanics [step-plan: steps/06-release.org]
/Status: NOT STARTED — applied at each release point/

Ongoing release process. Applied at each release point, not a
one-time milestone. Covers:
- Tagging, changelog, version bump
- Man page updates
- README updates (tested kernel matrix, platform support)
- Notify downstream packagers (Debian, Fedora, Homebrew, etc.)

* Roadmap

** v1.7.0 — Quality Release (M1 + M3)

The "everything works better" release. Bug fixes verified by a
comprehensive new test suite. *No platform changes* — all existing
platforms continue to be supported. Non-destructive upgrade from 1.6.5.

What's in it:
- Multi-kernel test framework (25 Linux kernels, ext4 + XFS)
- Bug fixes: grace -r, +0M, XFS grace display, operator precedence
- Codebase cleanup: dead code removal, clean -Wall -Wextra compile
- Tested kernel matrix documented in README
- ROADMAP section announcing v1.8.0 platform changes (advance notice)

What's NOT in it:
- No platform drops (that's v1.8.0)
- No new features (quotactl_fd, project quotas — that's v1.8.0+)
- No CI (local testing is the workflow for a one-person project)

** v1.7.x — Maintenance

=master= IS the v1.7.x line. Bug fixes go directly to master,
tag, release. Simple — no separate release branch needed.

Purpose: macOS users on older OS versions (pre-APFS) and anyone
who can't upgrade to v1.8.0 (which drops platforms) can still
receive bug fixes via v1.7.x patches.

** v1.8.0 — Platform Focus (M2, possibly +M4/M5)

The "honest codebase" release. Drops unsupported platforms, adds
BSD test coverage, possibly adds new features.

What's in it:
- BSD test coverage (FreeBSD, OpenBSD)
- Drop macOS, AIX, Solaris, NetBSD (with documented rationale)
- *Possibly*: quotactl_fd() support (M4) — tmpfs/bcachefs quotas
- *Possibly*: project quotas =-p= flag (M5)
- Updated kernel test matrix

Decision on M4/M5 inclusion made during M2 development.

Branch strategy:
- M2 work on =m2-1.8-development= branch (long-lived, off master)
- =master= stays stable (v1.7.x) during M2 development
- When v1.8.0 is ready: merge =m2-1.8-development= → =master=, tag
- Notify Homebrew/MacPorts maintainers *before* release (they got
  advance notice in v1.7.0's ROADMAP section)

** v1.9.0 or v2.0.0 — Feature Release (if M4/M5 not in v1.8.0)

Only needed if M4/M5 are not bundled with v1.8.0.

* Principles

- *Tests first*: No code change lands without test coverage.
- *Files over memory*: All state lives in tracked files, never in
  LLM context alone. Update files at every checkpoint.
- *No duplication*: One source of truth per topic. Reference, don't copy.
- *Milestone focus*: Work on one milestone at a time. Don't mix.
- *Masterplan is mutable*: Milestones can be reordered, split, merged,
  or added as understanding grows. But changes are deliberate, not drift.
- *Release early, release often*: Ship improvements to users. Don't
  block fixes behind unrelated work. Separate positive changes
  (bug fixes) from disruptive changes (platform drops).
