Filter and Sort Semantics for /projects

Context

Handlers expose the /projects list endpoint with several query parameters used by frontends to filter and sort project lists. The repository provides a generic in-memory adapter that centralizes filtering, search and sorting. The behavior must be explicit so adapters and frontend developers have a stable contract.

Decision

  • customer_id query param accepts either a single id or a comma-separated list of ids. When multiple ids are provided the adapter treats them as an OR for customer membership: a project matches if it belongs to any of the listed customers.
  • Different query parameters (for example customer_id and person_id) are combined using AND semantics: a project must satisfy every provided filter to be returned.
  • The /projects endpoint supports sort=progress in addition to name and priority. When sort=progress is used projects are ordered numerically by their clamped progress value (ascending).
  • progress values returned by adapters are clamped to the inclusive range [0,100] by the generic in-memory adapter during Refresh.

This ADR documents and formalizes the behavior already implemented in memory.GenericAdapter and described in SPEC.md.

Consequences

  • Adapter implementors must map upstream DTOs into the normalized model.* shapes and may provide raw progress values; the generic adapter will clamp them on Refresh.
  • Frontends relying on multi-customer filtering must pass comma-separated ids in customer_id when they want the OR semantics.
  • Clients that want OR semantics across different fields must issue multiple requests or this API must be extended; the current decision is AND across fields.

Implementation

  • The memory.GenericAdapter implements the decision. Handlers populate QueryOptions from URL params (including comma-splitting customer_id) and call ListProjects(ctx, opts).
  • SPEC.md has been updated to record these semantics in the canonical spec.

Alternatives considered

  • OR semantics across different fields (e.g., projects matching either person OR customer) — rejected because it complicates frontends and backend filtering logic; explicit AND semantics are simpler and match typical UI filter behavior.
  • SPEC.md — updated to document these semantics.
  • internal/adapter/memory/memory.go — contains the implementation of the filtering, sorting and clamping behavior.