Skip to content

feat(#1186): PR3 — Granite ClubEG charge-through backfill (read-only triage + DRY backfill)#1206

Merged
Systemsaholic merged 2 commits into
mainfrom
feature/issue-1186-charge-through-backfill
Jun 26, 2026
Merged

feat(#1186): PR3 — Granite ClubEG charge-through backfill (read-only triage + DRY backfill)#1206
Systemsaholic merged 2 commits into
mainfrom
feature/issue-1186-charge-through-backfill

Conversation

@Systemsaholic

@Systemsaholic Systemsaholic commented Jun 26, 2026

Copy link
Copy Markdown
Owner

#1186 PR3 — Granite ClubEG charge-through backfill (final piece of the #805/#817 cluster)

Models the $25,370.25 ClubEG golf-program funds (currently one orphan commission_check_items row a31b9896…, both link cols NULL → invisible) as a per-sub-trip charge-through (pass-through) activity on each of the 24 active Granite sub-trips, and excludes the inbound orphan. Does not create the #761 outbound payout — Al runs that manually later once ClubEG is a provisioned payee.

The Granite group is informal (trip_group_id NULL) and no auditable list of the 24 active ClubEG sub-trips existed — only approximate prose in the cluster plan ("~24 ClubEG sub-trips + ~7 unrelated Sunwing bookings" share the same Sunwing deposit check, + 1 cancelled). So PR3 is two SELECT-only / DRY-default scripts mirroring the already-merged #817 triage→confirm→apply pattern — no one guesses trip IDs.

Scripts

  1. triage-issue805-granite-subtrips.ts (READ-ONLY) — from the one explicit input (the inbound orphan id) derives the Granite parent check (orphan.check_id), enumerates every linked commission line on it → trips, and dumps the discriminating fields (description, supplier, activity name, trip status, owner, traveller headcount, Σ commission cents) so Al marks the 24 genuine ClubEG sub-trips (CONFIRM_is_granite_subtrip = YES) vs the unrelated Sunwing bookings + the cancelled trip.
  2. backfill-issue805-granite-charge-through.ts (DRY-default) — consumes the Al-confirmed worksheet; re-asserts every trip against prod (in the orphan's agency, status != 'cancelled', owner_id == EXPECTED_OWNER_ID Andy, and a genuine linked Granite line on the parent check); splits $25,370.25 by per-traveller headcount via largest-remainder apportionment (Σ == program total exactly, deterministic remainder, fresh headcount read); find-or-creates one floating charge-through package AP per sub-trip (is_charge_through=true, commission 0, pay_on_site=false — the PR1 CHECK shape); excludes the inbound orphan (reason pass_through_program_funds, NOT linked — design §10.8); and surfaces the Σ(per-subtrip) == inbound == intended-outbound reconciliation (aborts on mismatch). Idempotent; count-guarded (--expect); asserts the Phase-3a + PR1 CHECKs are live.
  3. issue805-granite-constants.ts — shared constants + the apportionment helper.

Operator recipe (Al runs — DRY/SELECT-only in-session)

Preconditions: PR1 (#1203) and Phase-3a (#1199) CHECKs live in prod (the backfill asserts both). Run locally with a prod DATABASE_URL; no app secrets needed.

  1. Triage (read-only):
    DATABASE_URL=<prod> TARGET_ORPHAN_ID=<full uuid of a31b9896…> npx tsx scripts/migration/triage-issue805-granite-subtrips.ts > granite-worksheet.csv
  2. Confirm the 24: open granite-worksheet.csv, set CONFIRM_is_granite_subtrip = YES on the 24 genuine ClubEG sub-trips ONLY (leave the ~7 unrelated Sunwing + 1 cancelled BLANK). Note Andy's owner_id (column) and the orphan's full uuid.
  3. Backfill DRY:
    DATABASE_URL=<prod> ACTOR_ID=<admin uuid> EXPECTED_OWNER_ID=<Andy owner_id> TARGET_ORPHAN_ID=<orphan uuid> npx tsx scripts/migration/backfill-issue805-granite-charge-through.ts --file=granite-worksheet.csv
  4. Verify the DRY output: exact 24-trip list (ids + names + ref + status), per-trip headcount + share, Σ == $25,370.25 == inbound == intended outbound, orphan exclusion plan. If the confirmed count ≠ 24, investigate (or pass --expect=N only if genuinely intended).
  5. Execute: same command + --confirm (sole write gate). Post-state proof prints Σ + orphan-excluded.
  6. Later (manual, separate): Al creates the Pass-through payouts: payout_type separation, settlement statements, T4A guardrail (accountant requirements — unblocks ClubEG $25.4K) #761 outbound payout to ClubEG once it's a provisioned payee — not done by this backfill.

Verification

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Added migration tooling to validate and reconcile a specific set of billing records more safely.
    • Improved safeguards with stricter pre-checks, confirmation gating, and post-run verification.
    • Added a read-only triage export to help review related records before making changes.

Systemsaholic and others added 2 commits June 26, 2026 02:30
…triage + DRY backfill)

The final piece of the #805/#817#1186 charge-through cluster. Models the
$25,370.25 ClubEG golf-program funds as a per-sub-trip charge-through
(pass-through) activity on each active Granite sub-trip, and excludes the inbound
orphan commission line. Does NOT create the #761 outbound payout (Al runs that
manually later once ClubEG is a provisioned payee).

The Granite group is informal (trip_group_id NULL) and no auditable list of the
24 active ClubEG sub-trips existed — only prose in the cluster plan. So PR3 is two
SELECT-only / DRY-default scripts mirroring the #817 triage→confirm→apply pattern:

1. triage-issue805-granite-subtrips.ts (READ-ONLY): from the one explicit input
   (the inbound orphan id a31b9896…) derives the Granite parent check
   (orphan.check_id), enumerates every LINKED commission line on it → trips, and
   dumps the discriminating fields (description, supplier, activity name, trip
   status, owner, traveller headcount, Σ commission cents) so Al marks the 24
   genuine ClubEG sub-trips vs the ~7 unrelated Sunwing bookings + 1 cancelled.

2. backfill-issue805-granite-charge-through.ts (DRY-default): consumes the
   Al-CONFIRMED worksheet; RE-ASSERTS every trip against prod (active + owner ==
   EXPECTED_OWNER_ID Andy + a genuine linked Granite line on the parent check);
   splits $25,370.25 by per-traveller headcount via largest-remainder
   apportionment (Σ == program total EXACTLY, deterministic remainder, fresh
   headcount read); find-or-creates one floating charge-through AP per sub-trip
   (is_charge_through=true, commission 0, pay_on_site=false — the PR1 CHECK
   shape); excludes the inbound orphan (reason pass_through_program_funds, NOT
   linked — design §10.8); and surfaces the Σ(per-subtrip)==inbound==intended-
   outbound reconciliation (aborts on mismatch). Idempotent; count-guarded
   (--expect); preconditions assert the Phase-3a + PR1 CHECKs are live.

issue805-granite-constants.ts: shared constants + the apportionment helper.

Both DRY/SELECT-only — Al runs them via the operator pattern. No prod writes in
session. No migration. Typecheck clean (strict); apportionment unit-verified
(Σ exact, deterministic tie-break); guards smoke-tested.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…gate)

1. CT_ACTIVITY_TYPE 'tour' → 'package'. A FLOATING activity (itinerary_day_id
   NULL, trip_id set) is constrained by chk_activity_has_parent (#812 migration)
   to activity_type IN ('package','insurance') — a 'tour' insert would have been
   REJECTED on the first --confirm row. 'package' is constraint-allowed and is the
   design's own wording; charge-through is already excluded from money rollups by
   PR2/PR2.5 (only a cosmetic +1 in the package COUNT remains). ('insurance' is
   semantically wrong + has its own special handling.)

2. backfill write gate: --confirm is now the SOLE execution gate — removed the
   `|| process.env.CONFIRM === 'true'` env bypass so a stray env var can never turn
   a DRY rehearsal into a prod write.

tsc strict clean; CT_ACTIVITY_TYPE/marker consistency verified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tailfire-client Ready Ready Preview, Comment Jun 26, 2026 6:38am
tailfire-ota Ready Ready Preview, Comment Jun 26, 2026 6:38am

Request Review

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4fa83125-16fa-44b7-a84a-40a4f48f79e3

📥 Commits

Reviewing files that changed from the base of the PR and between 867d8d8 and ad33096.

📒 Files selected for processing (3)
  • scripts/migration/backfill-issue805-granite-charge-through.ts
  • scripts/migration/issue805-granite-constants.ts
  • scripts/migration/triage-issue805-granite-subtrips.ts

📝 Walkthrough

Walkthrough

Adds a Granite triage exporter, shared constants with deterministic headcount apportionment, and a transactional backfill that validates confirmed sub-trips, creates charge-through pricing, excludes the orphan commission item, and verifies the final totals.

Changes

Granite charge-through workflow

Layer / File(s) Summary
Shared constants and apportionment
scripts/migration/issue805-granite-constants.ts
Defines Granite IDs, formatting and UUID helpers, and deterministic headcount-based cents allocation.
Triage CSV export
scripts/migration/triage-issue805-granite-subtrips.ts
Reads the orphan, resolves linked commission lines through trips, and emits a confirmation CSV with hints and counts.
Backfill validation and dry run
scripts/migration/backfill-issue805-granite-charge-through.ts
Parses confirmed trips, revalidates the orphan and each sub-trip, checks live constraints, and computes the planned charge-through allocation without writing in dry-run mode.
Backfill transaction and proof
scripts/migration/backfill-issue805-granite-charge-through.ts
Creates or updates charge-through pricing rows, excludes the orphan commission item, inserts history, and re-checks the post-commit totals and exclusion state.

Sequence Diagram(s)

Triage CSV generation

sequenceDiagram
  participant Operator
  participant TriageScript as triage-issue805-granite-subtrips.ts
  participant Postgres

  Operator->>TriageScript: run with TARGET_ORPHAN_ID
  TriageScript->>Postgres: validate orphan and fetch linked commission lines
  Postgres-->>TriageScript: orphan row and trip aggregates
  TriageScript-->>Operator: CSV rows on stdout and coverage summary
Loading

Backfill confirm execution

sequenceDiagram
  participant Operator
  participant BackfillScript as backfill-issue805-granite-charge-through.ts
  participant CsvAllowList as CSV allow-list
  participant Postgres

  Operator->>BackfillScript: run with --file and --confirm
  BackfillScript->>CsvAllowList: read confirmed trip IDs
  CsvAllowList-->>BackfillScript: allowed sub-trips
  BackfillScript->>Postgres: validate orphan, sub-trips, and live constraints
  Postgres-->>BackfillScript: validation rows
  BackfillScript->>Postgres: transaction inserts, updates, and orphan exclusion
  Postgres-->>BackfillScript: commit result
  BackfillScript->>Postgres: post-commit proof queries
  Postgres-->>BackfillScript: totals and exclusion state
  BackfillScript-->>Operator: completion logs
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

Poem

I nibbled the CSV under moonlit trees,
and split the cents like dewdrops with ease.
The orphan hopped off, the totals aligned,
while charge-through trails were neatly twined.
🐇✨ Thump thump, the ledger sings!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/issue-1186-charge-through-backfill

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.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed: dependency version conflict. Check your lock file or package.json.


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.

@Systemsaholic Systemsaholic merged commit ba09b5b into main Jun 26, 2026
11 of 18 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.

1 participant