Skip to content

B4.3R4: Apply manual owner billing adjustments #866

Description

@ScottArbeit

B4.3R4: Apply manual owner billing adjustments

Objective

Add a narrow internal service for deliberate manual owner billing adjustments that remains distinct from automatic
late-usage correction and preserves exact replay semantics.

This gives Grace operators a controlled way to correct an owner's ledger without weakening the immutable initial-charge
or automatic-correction contracts.

Parent and dependency

Product V1 contract

  • Manual posting appends only an Adjustment row to an existing Closed or Corrected owner billing period.
  • The caller supplies a nonempty stable correlation id, reason, actor/provenance, and signed amount under the accepted
    money/rounding constraints.
  • Repeating the exact same correlation and payload returns the existing adjustment.
  • Reusing a correlation with any conflicting scope, amount, reason, actor, or provenance is rejected.
  • Manual provenance is reserved from automatic work provenance; a manual row can never satisfy automatic late-work
    completion or uniqueness.
  • Initial charges and prior corrections/adjustments remain immutable.
  • Database UTC timestamps own persisted posting time.
  • A manual adjustment does not change Closed/Corrected state except that an already Corrected period stays Corrected.

Forbidden implementation shapes

  • No public route, CLI command, SDK surface, OpenAPI operation, or generated client in this leaf.
  • No generic ledger-write primitive exposed to callers.
  • No automatic late-work processing or failed-period settlement.
  • No update/delete path for any ledger row.
  • No synthetic UsageFactId or other automatic-work identity for a manual adjustment.
  • No source-text assertions as the primary database-constraint or replay proof.
  • No compatibility behavior for imagined production rows.

Owned paths

  • src/Grace.Operations/Grace.Operations.Data/** for the internal manual-adjustment service and constraints.
  • src/Grace.Operations/Grace.Operations.Tests/** for real-database proof.
  • Relevant *.fsproj compile items.
  • Worker changes are N/A unless the current architecture requires dependency registration for an internal service; no
    scheduled manual worker is allowed.

Sensitive or excluded paths

  • src/Grace.slnx
  • Usage ingestion, initial close, and automatic correction except consuming their settled types
  • Grace CLI, SDKs, OpenAPI, generated contracts, and unrelated server routes

Minimum detail gate

Positive tests

  • A valid adjustment appends once and preserves the exact correlation and provenance.
  • Exact replay returns the existing row without changing the ledger.
  • Positive and negative amounts follow the accepted money constraints.
  • Closed and Corrected periods accept adjustments while retaining their state.

Negative tests

  • Open and Provisional periods reject manual posting.
  • Empty correlation, reason, actor, or required provenance is rejected.
  • Conflicting correlation reuse is rejected for every payload dimension.
  • A manual correlation cannot collide with or complete automatic late work.
  • Update and delete attempts fail at the database boundary.

Regression and boundary tests

  • Two concurrent exact replays produce one row and the same result.
  • Concurrent conflicting reuse produces one success and one deterministic conflict.
  • Transaction rollback leaves no adjustment or success marker.
  • Maximum accepted amount and overflow boundaries are explicit and deterministic.

High-risk adversarial examples

  • Same correlation reused for another owner, organization, repository, or month is rejected.
  • Same reason and amount with a different actor is a conflict, not an exact replay.
  • Automatic provenance prefixes/identities are rejected for manual input.
  • A negative adjustment cannot be represented as mutation of an earlier charge.

Contract propagation map

  • Shared DTOs/parameters/events: internal service request/result only; public DTOs N/A.
  • Persisted shapes and migrations: required only for manual replay/provenance constraints not already delivered.
  • HTTP routes: N/A.
  • CLI/SDK/OpenAPI/generated artifacts: N/A.
  • Documentation: XML documentation for new F# declarations and nearby Operations notes where needed.
  • Tests: real database constraints, concurrency, replay, and rollback.

Stale-state preflight

  • Source before decision: persisted period state and any ledger row with the supplied correlation.
  • Revalidation point: in the posting transaction before append.
  • Abort state: no ledger mutation; return a deterministic validation or conflict result.
  • Retry/cleanup: exact replay converges on the existing row; rollback leaves nothing to clean up.
  • Proof: concurrent exact or conflicting requests cannot both append.

Validation profile

  • Format touched F#.
  • Build affected Release projects before focused --no-build tests.
  • Run focused real-database tests for replay, conflict, concurrency, constraints, immutability, and rollback.
  • Run git diff --check.
  • GitHub Validate and one fresh current-head review are required before merge.

Epic execution policy

Epic #554's 4+1 worker policy applies. Workers 1-3 are the normal implementation and proof budget. After worker 3
and before worker 4, audit the remaining invariant families; if more than one substantial family remains unproven or
the slice needs scope, architecture, lifecycle, public-contract, durable-state, or primary-invariant expansion,
supersede or split before worker 4. Worker 4 is the completion target and must produce a complete local proof ledger and
reviewable candidate. Worker 5 is reserved only for one finite, grounded repair set from fresh exact-head review or
PR-owned CI on that candidate; it may not finish planned implementation, multiple outstanding proof families,
environment diagnosis, or redesign confirmation. Any valid addressable finding remaining after worker 5 supersedes
automatically; no worker 6. Keep one active implementation/fix worker per PR head, one fresh review per pushed head,
and exact-head GitHub Validate.

Definition of done

  • Manual posting is an internal, immutable, replay-safe adjustment path distinct from automatic correction.
  • Real-database tests prove exact replay, conflicting reuse, concurrency, rollback, and provenance separation.
  • No public adjustment surface or failed-period settlement is introduced.
  • The PR targets the epic integration branch, links this issue, has current-head Validate, and receives a fresh
    no-findings review verdict.

Readiness Refinement After #865

The manual path shares the immutable ledger but not the automatic-work lifecycle. A fresh worker must preserve these
boundaries explicitly.

Identity and transaction contract

  • The replay identity is exact billing scope plus an ordinal, width-validated manual correlation ID. Exact equality is
    ordinal and includes signed amount, currency, reason code/text, initiating principal, and every persisted provenance
    field. No normalization may make two caller payloads equivalent implicitly.
  • The posting transaction resolves the exact period, acquires the shared scope lock, rereads period state and existing
    manual correlation, then appends or returns the existing row. A stale pre-read cannot post into an Open,
    Provisional, or failed period.
  • The ledger row uses a database-generated UTC creation time and a stable kind/source discriminator reserved for manual
    adjustments. It cannot carry UsageFactId, automatic-work ID, automatic reason code, or automatic initiating
    principal values.
  • Manual adjustment changes neither automatic pending/blocked/completed work nor period state. Closed remains Closed;
    Corrected remains Corrected.

Durable enforcement and proof

  • SQL uniqueness and constraints make the exact manual correlation unambiguous within its billing period and keep
    manual provenance disjoint from initial charge and automatic-correction provenance.
  • Runtime model, migration, frozen target, and snapshot propagate any new constraint identically. Migration behavior
    is self-contained and does not call mutable runtime helpers.
  • Real-SQL proof must drive two independent connections through the production internal service for exact replay and
    conflicting reuse. It must prove one row, deterministic conflict, no automatic-work mutation, and unchanged period
    state in both lock orders.
  • Injected failure or cancellation after ledger staging must roll back the row and release the scope lock. Retry then
    posts once.
  • Direct update/delete, forged automatic provenance, wrong period/scope, amount overflow, and every maximum-width plus
    one input must be rejected without partial mutation.

Metadata

Metadata

Assignees

Labels

ready-for-agentIssue-owned implementation task for human or AI agents

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions