Skip to content

feat(database): add roster and analytics schema - #27

Open
MagicTheDev wants to merge 10 commits into
mainfrom
feat/personal-armies-contract
Open

MagicTheDev wants to merge 10 commits into
mainfrom
feat/personal-armies-contract

Conversation

@MagicTheDev

@MagicTheDev MagicTheDev commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

Summary

Adds the authoritative Goose migrations for personal armies, scoped roster signup and admission, Discord roster publication/webhooks, automation event offsets and capacity, legend daily metadata, CWL participation, and daily army setups. A forward migration removes the retired per-server link-token switch so new links require token proof. Migrations 037–041 add publication nonce reservations and tighten daily analytics JSON counts, including exclusive-use totals, three-star ceilings, and per-item troop/spell triple bounds. Migration 041 also indexes saved-army references by composition for FK cascades. Migration 042 restores structured validation of CWL town-hall distribution data; 043 rejects the zero sentinel as a stored Legend selected-siege ID; 044 validates CWL same-TH hit-rate entries. Previously applied migration files remain byte-for-byte unchanged in the final tree.

The branch also includes the local development workspace/CLI that had remained local, plus migration inventory, fixture-profile, schema tests, and documentation updates. The contributor-guide change already present on main was skipped during rebase because its patch matched exactly.

Daily setup rank_limit intentionally remains NULL (Overall), 1000, or 200. The experimental top_100 cohort is Legend metadata only; both the active API setup endpoint and Tracking setup writer explicitly use the three supported setup cohorts.

Validation

  • go test ./migrations ./schema from database/
  • node --test scripts/with-test-timescale.test.mjs
  • Fresh disposable Timescale migrated by Goose through 044, then TestDailyAnalyticsUsageConstraints checked valid, malformed, duplicate, unordered, zero-ID, and overcounted pet/siege/equipment data, exclusive three-star totals, and per-item troop/spell triples. TestCWLParticipationTownHalls and TestCWLParticipationHitRates checked typed descending CWL entries and actual table constraints. The populated baseline-021 personal-army migration test also passed; the fixture-gated tests now run in CI.
  • git diff --check

No production migration has been run. Merge this schema PR before the dependent API and Tracking changes are deployed; API CI pins this PR's commit for isolated PostgreSQL tests.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-25T16:25:31.516069Z c5fc3ec New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8ab2642014

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +30 to +32
uses_value := (entry->>'uses')::bigint;
triples_value := (entry->>'triples')::bigint;
IF triples_value > uses_value OR uses_value > attack_limit THEN RETURN false; END IF;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Enforce distribution-wide bounds for pet combos

When an aggregate contains duplicate pet sets or several buckets whose combined uses exceed attack_count, this validator accepts it because each entry is checked independently. Since each attack contributes at most one whole pet set, the resulting daily analytics can report impossible totals; track the previous combo to reject duplicates/noncanonical ordering and ensure cumulative uses do not exceed attack_limit.

Useful? React with 👍 / 👎.

Comment on lines +5 to +6
ADD COLUMN siege_usage jsonb NOT NULL DEFAULT '[]'::jsonb
CHECK (jsonb_typeof(siege_usage) = 'array');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate siege usage entries and their total

When a writer supplies malformed or overcounted siege data, this check accepts any JSON array, including entries without numeric id/attacks fields, duplicate siege IDs, or totals above the row's attack_count. Because the column is documented as per-attack normalized siege counts, add a validator that enforces the entry shape, nonnegative counts, unique/canonical IDs, and a cumulative count no greater than attack_count.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 935d26ddd9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +72 to +73
ADD CONSTRAINT legend_daily_stats_siege_check
CHECK (public.item_usage_triples_within_attack_count(siege_stats,attack_count)),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound total siege uses by the attack count

When a cohort contains different selected siege machines, this reuses the generic item validator, which only checks each entry's uses independently. For example, two siege IDs can each report uses = attack_count, even though the canonical selected siege is mutually exclusive per attack, producing impossible daily totals; use a siege-specific validator that also caps cumulative uses at attack_count.

Useful? React with 👍 / 👎.

Comment on lines +48 to +51
IF first_value >= second_value OR triples_value > uses_value OR uses_value > attack_limit
OR hero_value < previous_hero
OR (hero_value = previous_hero AND first_value < previous_first)
OR (hero_value = previous_hero AND first_value = previous_first AND second_value <= previous_second)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Cap equipment-pair uses separately for each hero

When one hero appears with multiple equipment-pair buckets, each bucket may independently use the full attack_limit, so a five-attack aggregate can claim five uses for each of two mutually exclusive loadouts on the same hero. Track cumulative uses for the current heroId and reject a per-hero total above attack_limit.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ff23f25e74

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +49 to +50
ADD CONSTRAINT legend_daily_stats_pet_combos_check
CHECK (public.pet_combo_usage_triples_within_attack_count(pet_combo_stats,attack_count));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound pet-combo triples by the row's triple count

Although migration 038 now caps cumulative uses, a row with three_star_count = 0 and pet_combo_stats = [{"petIds":[1],"uses":1,"triples":1}] still passes because the validator receives only attack_count and checks triples <= uses. Whole pet combos are mutually exclusive per attack, so their cumulative triples cannot exceed legend_daily_stats.three_star_count; otherwise the aggregate can report triple attacks while its star totals report none.

Useful? React with 👍 / 👎.

Comment on lines +12 to +13
if os.Getenv("CLASHKING_DISPOSABLE_TIMESCALE") != "1" || os.Getenv("TEST_DATABASE_URL") == "" {
t.Skip("requires disposable authoritative Goose schema")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Run the new fixture-gated tests in CI

In the checked workflows, schema-upgrade.yml invokes only the baseline-006, baseline-013, and baseline-018 profiles, while validate-repository.sh runs go test without a disposable database, so this new analytics test always skips in CI; TestPersonalArmyStorageMigration likewise always skips because it requires baseline-021. Add explicit retained-api and baseline-021 workflow invocations so the new constraint logic and populated 021-to-022 upgrade are actually exercised.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3e4c66bad5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

CREATE TABLE public.army_setup_daily_stats (
day date NOT NULL,
league_tier_id integer NOT NULL CHECK (league_tier_id > 0),
rank_limit integer CHECK (rank_limit IN (200,1000)),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow top-100 army setup observations

Include 100 in the allowed rank limits. Migration 030 adds the top_100 cohort to legend_daily_stats, and migration 034 attaches army-analysis readiness to those rows, but an analyzer cannot persist the corresponding setup observations because every row with rank_limit = 100 is rejected by this constraint. This can cause the top-100 analysis transaction to fail or leave that cohort permanently unclassified.

Useful? React with 👍 / 👎.

Comment on lines +77 to +81
ADD CONSTRAINT legend_daily_stats_siege_check
CHECK (public.legend_siege_usage_within_attack_count(siege_stats,attack_count)),
DROP CONSTRAINT legend_daily_stats_equipment_pairs_check,
ADD CONSTRAINT legend_daily_stats_equipment_pairs_check
CHECK (public.equipment_pair_usage_triples_within_attack_count(equipment_pair_stats,attack_count));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound exclusive triple counts by the row total

Pass three_star_count into these final siege and equipment-pair validators and enforce it alongside attack_count. Currently a row with three_star_count = 0 accepts, for example, siege metadata containing {"uses":1,"triples":1}; equipment pairs have the same problem. Because siege choices are exclusive per attack and equipment-pair choices are exclusive per hero, their triple totals can otherwise contradict the row's authoritative star distribution.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9f57a895b3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +68 to +71
ADD CONSTRAINT legend_daily_stats_troops_check
CHECK (public.item_usage_triples_within_attack_count(troop_stats,attack_count)),
ADD CONSTRAINT legend_daily_stats_spells_check
CHECK (public.item_usage_triples_within_attack_count(spell_stats,attack_count)),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound item triples by the row's three-star count

When a writer supplies troop or spell metadata for a row with fewer three-star attacks, these checks only bound uses by attack_count and triples by uses. For example, a row with three_star_count = 0 still accepts troop_stats = [{"id":1,"uses":1,"triples":1}], allowing analytics to contradict the authoritative star distribution. Each troop/spell entry's triples should also be capped by three_star_count without summing across items, since several items can occur in the same attack.

Useful? React with 👍 / 👎.

-- Removing the save never removes the shared composition or retained history.
CREATE TABLE public.user_saved_armies (
user_id text NOT NULL REFERENCES public.auth_users(user_id) ON DELETE CASCADE,
share_code text NOT NULL REFERENCES public.army_compositions(share_code) ON DELETE CASCADE,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Index saved armies by composition before cascading deletes

When an army_compositions row is deleted, PostgreSQL must find every referencing saved-army row to enforce this cascade, but both available indexes lead with user_id; therefore each composition deletion scans the entire unlimited personal-army library. Add an index beginning with share_code so cleanup or batch deletion of canonical compositions does not become increasingly expensive or hold locks for a full-table scan.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a07172d655

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

group_count >= 0 AND clan_count >= 0 AND registered_player_count >= 0
AND finalized_wars >= 0 AND archived_wars >= 0 AND archived_wars <= finalized_wars
),
CONSTRAINT cwl_participation_townhall_check CHECK (jsonb_typeof(townhall_counts) = 'array'),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore structured validation for CWL town-hall counts

When a manual rebuild accidentally writes a value such as [null] or [{"level":"17","count":-1}], this constraint accepts it because it checks only the outer array type. The earlier CWL aggregate's cwl_town_halls_valid function validated entry keys, numeric types, level bounds, counts, and canonical ordering; without equivalent validation, malformed distributions can be retained and returned as analytics.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 465ef44ad0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

entry jsonb;
total_uses bigint := 0;
BEGIN
IF attack_limit < 0 OR NOT public.item_usage_triples_valid(value) THEN RETURN false; END IF;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject zero-valued Legend siege IDs

When a writer supplies siege_stats containing {"id":0,"uses":1,"triples":0}, item_usage_triples_valid accepts the entry and this validator only sums its usage, so the row passes despite representing no canonical selected siege. The equivalent normalized setup validator explicitly rejects siege_id = 0 in migration 038, and both columns describe the selected siege; apply the same positive-ID requirement here to prevent an invalid bucket from reaching Legend analytics.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a671f095f0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +24 to +25
CONSTRAINT cwl_participation_hitrates_check CHECK (
same_th_hitrates IS NULL OR jsonb_typeof(same_th_hitrates) = 'array'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate entries in same-TH hit-rate arrays

When a rebuild writes malformed hit-rate data such as [null] or [{}], this constraint accepts it because it checks only the outer array type, and no later migration tightens this column. Such values can therefore be retained as analytics even though consumers cannot interpret them as hit-rate observations; validate each entry’s required fields, numeric types, bounds, and canonical ordering.

Useful? React with 👍 / 👎.

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