Skip to content

Pantheon 2.0: version-centric manifest and query-time difficultyTier - #133

Merged
owens1127 merged 8 commits into
mainfrom
pantheon-version-manifest
Jun 10, 2026
Merged

Pantheon 2.0: version-centric manifest and query-time difficultyTier#133
owens1127 merged 8 commits into
mainfrom
pantheon-version-manifest

Conversation

@owens1127

@owens1127 owens1127 commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

API layer for Pantheon 2.0 — boss identity is version-centric; difficulty is a separate read-time axis.

  • Removes gauntletVersionIds from /manifest
  • Adds pantheonVersionIds (active 132–141 on activity 102) and pantheonSunsetVersionIds (sunset 128–131 on activity 101)
  • Pantheon activities resolved by path: "pantheon" only (activities 101 and 102; no thepantheon, no activity 201)
  • Adds nullable difficultyTier (adventure | standard | custom) on instance responses — computed at query time, not stored on core.instance
  • Classification joins instance.skull_hashes against activity_feat_definition per request (batched for list endpoints)
  • Pantheon leaderboard version lookup resolves by path across all pantheon activities (prefers active over sunset)

Model: Card titles and PGCR version names use boss versionId / metadata.versionName. difficultyTier is derived from PGCR skulls and does not rename versions.

Tier classification (src/services/difficulty-tier/)

Tier Rule
adventure Skulls 845104503, 2008962334
standard Customize portal with ≤2 optional feats (incl. empty-feat baseline)
custom Customize portal with >2 optional feats, or non-baseline feat selection
null Non-pantheon activities, or no tier signal

Requires activity_feat_definition populated (manifest-downloader). Null skull_hashes handled safely.

Related PRs

Repo PR Role
Services #48 Seeds + production SQL + PGCR skull dedupe
API #133 (this) Manifest lists + query-time difficultyTier
Website #325 Flat pantheon UI + Adventure badge

Deploy order

  1. Merge and deploy Services #48 + run production SQL + refresh pantheon MV
  2. Deploy this PR
  3. Deploy Website #325

Test plan

  • bun test src/services/manifest/pantheon.test.ts
  • bun test src/services/difficulty-tier/classify.test.ts
  • /manifest returns pantheonVersionIds [132–141] and pantheonSunsetVersionIds [128–131]
  • Instance/PGCR/history responses include difficultyTier derived from skulls + feat definitions
  • Pantheon leaderboard resolves version paths on activity 102 (and sunset 101)
  • OpenAPI docs regenerated (bun run docs)

Expose pantheonVersionIds and pantheonSunsetVersionIds from /manifest.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copilot AI review requested due to automatic review settings June 10, 2026 02:29
@barecheck

barecheck Bot commented Jun 10, 2026

Copy link
Copy Markdown

Barecheck - Code coverage report

Total: 91.01%

Your code coverage diff: 1.89% ▴

Uncovered files and lines
FileLines
src/services/difficulty-tier/resolve.ts11, 13-14, 17-18
src/services/instance/instance.ts190
src/services/manifest/pantheon.ts23, 27-28
src/services/player-instances/instances.ts51

Comment thread src/services/manifest/pantheon.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the /manifest API to stop exposing Pantheon “gauntlet” version IDs and instead expose two explicit Pantheon version ID lists (active vs sunset), while also tightening Pantheon activity/version resolution logic in the manifest helper utilities.

Changes:

  • Replaces gauntletVersionIds with pantheonVersionIds and pantheonSunsetVersionIds in /manifest response + OpenAPI.
  • Refactors Pantheon manifest helpers to compute/split Pantheon versions by associated activity + isSunset.
  • Updates unit tests to validate the new Pantheon version list behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
src/services/manifest/pantheon.ts Refactors Pantheon activity/version selection logic and version lookup query.
src/services/manifest/pantheon.test.ts Updates tests to cover the new getPantheonVersionIds API and modified Pantheon fixtures.
src/routes/manifest.ts Updates /manifest schema + handler to return the new Pantheon version ID fields.
open-api/openapi.json Regenerates OpenAPI schema to reflect the /manifest field changes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/services/manifest/pantheon.ts
Comment thread src/services/manifest/pantheon.ts
Comment thread src/services/manifest/pantheon.test.ts
Comment thread src/services/manifest/pantheon.test.ts
Comment thread src/services/manifest/pantheon.test.ts
owens1127 and others added 2 commits June 9, 2026 22:52
Sunset Pantheon versions remain associated with activity 101 (path thepantheon) in production, so lookups and manifest splits must accept both paths.

Co-authored-by: Cursor <cursoragent@cursor.com>
Both sunset (101) and active (102) Pantheon activities use path pantheon in Services; drop thepantheon legacy handling.

Co-authored-by: Cursor <cursoragent@cursor.com>
Align OpenAPI with nullable DB column; null for legacy runs and non-tier activities.

Co-authored-by: Cursor <cursoragent@cursor.com>
Look up feat skulls from activity_feat_definition per request instead of
reading a stored column or using a startup cache.

Co-authored-by: Cursor <cursoragent@cursor.com>
owens1127 and others added 2 commits June 9, 2026 23:13
Handle null skull_hashes in difficulty tier resolution, restore pantheon
path compatibility for production sunset activity, and update tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
Align manifest helpers and OpenAPI examples with Services seeds where
activity 101 and 102 both use path pantheon.

Co-authored-by: Cursor <cursoragent@cursor.com>
instance.duration::int AS "duration",
instance.platform_type AS "platformType",
pgcr.date_crawled AS "dateResolved"
FROM instance

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: The INNER JOIN in getInstanceBasic causes it to return null for valid instances if their activity_version is missing, leading callers to incorrectly assume the instance doesn't exist.
Severity: HIGH

Suggested Fix

Change the INNER JOIN in getInstanceBasic to a LEFT JOIN on the activity_version table. This will ensure that the instance data is always returned, even if the corresponding activity version details are missing. The function should then handle the case where activity version fields are null, preventing silent failures and incorrect 'not found' responses.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: src/services/instance/instance.ts#L228

Potential issue: The `getInstanceBasic` function uses an `INNER JOIN` with the
`activity_version` table. If an instance exists in the database but its hash does not
have a corresponding row in `activity_version` (e.g., due to incomplete data seeding for
a new activity), the query will return no results. Callers interpret the resulting
`null` as the instance not being found, leading to incorrect 404 errors in admin routes
or silent data omission in status endpoints. This failure occurs without logging an
error, masking the underlying data integrity issue.

Co-authored-by: Cursor <cursoragent@cursor.com>
}

const rows = await pgReader.queryRows<{ skullHash: number }>(
`SELECT skull_hash::int AS "skullHash"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: The SQL query in loadKnownFeatSkulls incorrectly casts skull_hash to int. This will cause a database error for any hash value exceeding the 32-bit signed integer limit.
Severity: CRITICAL

Suggested Fix

Remove the ::int cast from the SQL query in loadKnownFeatSkulls. Instead, retrieve the bigint value directly and use a transformer like convertUInt32Value, as demonstrated in listFeatDefinitions, to safely convert it to a JavaScript number.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: src/services/difficulty-tier/resolve.ts#L14

Potential issue: The SQL query in `loadKnownFeatSkulls` at
`src/services/difficulty-tier/resolve.ts:14` incorrectly casts the `skull_hash` column
from `bigint` to `int`. Destiny 2 hashes are treated as unsigned 32-bit integers and can
exceed the maximum value for a signed 32-bit integer (2,147,483,647). When a hash value
larger than this is encountered in the `activity_feat_definition` table, PostgreSQL will
raise an "integer out of range" error. This will cause the query to fail, crashing core
instance retrieval endpoints like `getInstances` and `getActivities` when they process
affected activities.

@owens1127 owens1127 changed the title Replace gauntlet manifest fields with version-centric pantheon lists Pantheon 2.0: version-centric manifest and query-time difficultyTier Jun 10, 2026
@owens1127
owens1127 merged commit fd1b7f1 into main Jun 10, 2026
7 checks passed
@owens1127
owens1127 deleted the pantheon-version-manifest branch June 10, 2026 03:24
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.

2 participants