Skip to content

Classify late settlement from Horizon close_time vs expiry and cancel - #576

Open
woahwhattheheck wants to merge 7 commits into
Quittance-Labs:mainfrom
woahwhattheheck:latch/quittance-507-late-settlement
Open

woahwhattheheck wants to merge 7 commits into
Quittance-Labs:mainfrom
woahwhattheheck:latch/quittance-507-late-settlement

Conversation

@woahwhattheheck

@woahwhattheheck woahwhattheheck commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Closes #507.

Settlement context (ON_TIME / AFTER_EXPIRY / AFTER_CANCEL) is decided only from Horizon transaction close time versus stored expiresAt / cancelledAt. Verify, monitor, receipt, timeline, and PDF share one classifier and warning vocabulary in shared/settlement.ts. Existing policy markdown (docs/LATE_PAYMENT_POLICY.md, docs/EXPIRY-AND-LATE-PAYMENT.md) stays the written spec.

Done when

  • Payment whose close_time is after expiry settles with AFTER_EXPIRY, not wall clock
  • Missing close_time does not mark PAID (503 TRANSACTION_CLOSE_TIME_UNAVAILABLE)
  • Timeline and PDF/receipt show the same warning code (and shared copy) as the API
  • No new overlapping policy doc

Changes

  • Shared classifier (shared/settlement.ts): parse UTC close time, fail closed when missing, classify against expiry/cancel, export warning codes + API/receipt/timeline copy
  • Backend domain re-exports the shared module (no parallel classifier)
  • Monitor passes created_at into verification so close time is not dropped on the auto path
  • Timeline keeps the expired milestone when settlement is AFTER_EXPIRY
  • Receipt / PDF export read late-payment title/body from the shared definitions
  • Tests: documented matrix (including clock-skew ON_TIME after EXPIRED sweep), shared-contract drift guard, handlers fixture for close_time + fail-closed 503, timeline AFTER_EXPIRY sequence

Test plan

  • node --import tsx --test tests/invoice-settlement.test.ts tests/shared-settlement-contract.test.ts tests/invoice-cancel-payment-race.test.ts tests/memory-storage.test.ts tests/invoice-handlers.test.ts tests/invoice-service.test.ts (159 pass)
  • npm run typecheck in backend and frontend
  • node --test tests/invoice-workspace.test.js (13 pass, including AFTER_EXPIRY timeline)

Payout Routing

  • EVM (Base/Arbitrum/Polygon/ETH): 0x18Dc2B9fc890a49e2a9cEBDb94e6c5A5cABa4F0a
  • Stellar: GCUC7ECT3CP5OKA4N6C5LCOYZJKFUVKJT3RBS5JM3PJSG6VPRS5LYHL4

Original contribution and conditional reward claim

/claim #507

As the original contributor to this existing submission, I request review and acceptance of the issue #507 solution and consideration for any eligible GrantFox OSS compensation under the campaign's actual terms. If eligible and accepted, please confirm the allocation, approved reward (if any), payout procedure and expected payment date. The Maybe Rewarded label does not establish a fixed bounty or payment, and no award is claimed as already approved.

Move the late-settlement classifier into shared/settlement.ts so verify,
monitor, receipt, timeline, and PDF share one source for ON_TIME /
AFTER_EXPIRY / AFTER_CANCEL plus warning codes and copy. Fail closed when
close_time is missing, pass created_at through the monitor verify path,
and keep the AFTER_EXPIRY expiry milestone on the seller timeline.

Closes Quittance-Labs#507
@CemAyyildiz

Copy link
Copy Markdown
Collaborator

This branch now has merge conflicts with main. Please rebase. #527 targets the same issue (#507); only one will be merged, so keeping this one up to date gives it the better chance.

Incorporate the upstream storage contract updates while preserving the shared
ledger-time classifier, warning copy, monitor timestamp and proof/timeline
behavior. Resolve the handler fixture overlap through the current payment
helper with an explicit ledger close time.

Validation: 270 focused backend tests and 13 timeline tests pass; backend and
frontend typechecks pass; frontend lint passes with two existing warnings.
Retain the conflict-free tree GitHub computed for PR 576 from main
282c75a and submission
43de9a0. Preserve both histories,
the existing Horizon close-time policy, and the original claim.

Tree matches test-merge f2cada2.
This publication does not rerun or extend earlier runtime evidence.
Keep Horizon close-time parsing independent of the server timezone. Require explicit-zone ISO date-times and round-trip local calendar components before applying the offset; reject locale/date-only input, nonexistent days and normalized 24:00. Date objects, numeric millisecond epochs, offsets, fractional seconds and existing settlement/warning policies are preserved.

Continues PR576 / issue507 on current-main integration ac98b81. Original shared module blob2693b05748b94f719812e4629d45750777557386 was staged byte-exact. Node22.16.0 with installed ts-node transpile-only / TypeScript5.8.3 executed the entire shared module: the same18 focused cases in UTC, America/New_York and Asia/Kolkata failed8 on the original and passed18/18 on the repair in each zone. No network, dependencies installed, full-app/DB/API run or settlement-policy rewrite.

The new backend/tests/settlement-time-input.test.ts is selected by the existing tests/*.test.ts script. Repository replay with its existing loader: cd backend && node --import tsx --test tests/settlement-time-input.test.ts. That tsx command and the full typecheck were not run in the isolated container.
Receipt and timeline helpers accept unknown strings but must only resolve definitions owned by the warning table. Inherited names previously produced undefined warning content instead of the documented null fallback.

The complete settlement module and backend re-export were executed with the maintained contract file plus two regressions on Node 22.16.0 / TypeScript 5.8.3: baseline 3 passed, 2 failed; repaired 5 passed, 0 failed. This is transpiled module execution, not a full typecheck, HTTP, browser, PDF-rendering or hosted-CI run. Existing classification, warning copy, UTC parsing and payment routing are unchanged.
Extend the existing shared settlement contract file with two focused cases. Unknown, empty and inherited names return null; both supported definitions retain their receipt and timeline copy.

The five-case file passed against the complete transpiled production module and backend re-export on Node 22.16.0 / TypeScript 5.8.3. The same file had two failures against pre-fix source bb8ca01. No full application suite, browser/PDF rendering, hosted CI or payment assertion.
@woahwhattheheck

Copy link
Copy Markdown
Contributor Author

@CemAyyildiz The October 1 integration request is addressed on the existing branch. Current head 759f7abea055386903564a1d83d51fdb5ba4d77f includes main@282c75ad933651a93b89a8412ab649b48714c739, and GitHub reports it mergeable.

The non-force integration at ac98b819552a3bd9b88cfe743068f2211cafd1f2 preserves the original #507 contribution and current storage/lifecycle changes. Subsequent commits retain the shared Horizon close-time classifier and canonical receipt/timeline warning copy. The earlier execution evidence remains attached to its original commits; no checks were rerun for this follow-up, and a mergeable branch is not being represented as green CI.

Please re-review the current head and identify any concrete remaining requirement, including the carrier-selection decision alongside #527. I maintain my original #507 contribution claim and request payment. Please confirm acceptance, the compensation amount and settlement route while preserving existing authorship and contributor claims.

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.

Classify late settlement from Horizon close_time vs expiry and cancel

2 participants