diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Revision history for ghc-stack-profiler-speedscope
 
+## 0.5.0.0 -- 2026-09-14
+
+Major version number changed to match `ghc-stack-profiler` and `ghc-stack-profiler-core`.
+
 ## 0.4.0.0 -- 2026-07-14
 
 - Switch to `ipedb` version 0.2.0.0 and the new database format.
diff --git a/ghc-stack-profiler-speedscope.cabal b/ghc-stack-profiler-speedscope.cabal
--- a/ghc-stack-profiler-speedscope.cabal
+++ b/ghc-stack-profiler-speedscope.cabal
@@ -1,16 +1,18 @@
 cabal-version: 3.8
 name: ghc-stack-profiler-speedscope
-version: 0.4.0.0
+version: 0.5.0.0
 license: BSD-3-Clause
 author: Hannes Siebenhandl, Wen Kokke, Matthew Pickering
 maintainer: hannes@well-typed.com
 build-type: Simple
-synopsis: Convert eventlog messages from `ghc-stack-profiler` into a speedscope json.
+synopsis:
+  Export a ghc-stack-profiler call-stack profile from an eventlog to speedscope
+
 description:
-  Convert eventlog messages from `ghc-stack-profiler` into a speedscope json.
-  Produce a flame graph for the RTS callstack samples obtained from a program instrumented with
-  'ghc-stack-profiler'.
+  Export a @ghc-stack-profiler@ call-stack profile from an eventlog to speedscope.
 
+  For details, see [@ghc-stack-profiler@](https://hackage.haskell.org/package/ghc-stack-profiler).
+
 extra-doc-files: CHANGELOG.md
 category: Profiling, Benchmarking, Development
 tested-with:
@@ -51,14 +53,14 @@
 
   build-depends:
     aeson >=2.2 && <2.3,
-    base >=4.20 && <4.23,
+    base >=4.20 && <5,
     bytestring >=0.11 && <0.13,
     containers ^>=0.7 || ^>=0.8,
     data-default >=0.2 && <0.9,
     extra ^>=1.8.1,
     filepath ^>=1.4 || ^>=1.5,
     ghc-events ^>=0.20,
-    ghc-stack-profiler-core >=0.3 && <0.5,
+    ghc-stack-profiler-core ==0.5.0.0,
     hs-speedscope ^>=0.3,
     ipedb ^>=0.2.0.0,
     machines ^>=0.7.4,
diff --git a/src/GHC/Stack/Profiler/Speedscope.hs b/src/GHC/Stack/Profiler/Speedscope.hs
--- a/src/GHC/Stack/Profiler/Speedscope.hs
+++ b/src/GHC/Stack/Profiler/Speedscope.hs
@@ -29,10 +29,7 @@
 import Data.Word (Word64)
 import qualified GHC.RTS.Events as E
 import qualified GHC.RTS.Events.Incremental as E
-import qualified GHC.Stack.Profiler.Core.Eventlog as GSP
-import qualified GHC.Stack.Profiler.Core.SymbolTable as GSP
-import qualified GHC.Stack.Profiler.Core.ThreadSample as GSP
-import qualified GHC.Stack.Profiler.Core.Util as GSP (word64ToInt)
+import qualified GHC.Stack.Profiler.Core as GSPC
 import GHC.Stack.Profiler.Speedscope.Options
 import GHC.Stack.Profiler.Speedscope.Types
 import qualified IpeDB.Database as DB
@@ -190,20 +187,22 @@
     E.RtsIdentifier{rtsident} ->
       pure st{maybeRtsVersion = parseIdent rtsident}
     E.UserBinaryMessage bs ->
-      case GSP.deserializeEventlogMessage $ BSL.fromStrict bs of
+      case GSPC.deserializeEventlogMessage $ BSL.fromStrict bs of
         Left _err ->
           pure st
         Right evMsg -> case evMsg of
-          GSP.CallStackFinal msg -> do
+          GSPC.ProtocolVersion _ver -> do
+            pure st
+          GSPC.CallStackFinal msg -> do
             let
               (callStackMessage, elProf1) = hydrateBinaryEventlog st msg
             processCallStackMessage infoProvTable elProf1 callStackMessage
-          GSP.CallStackChunk msg ->
+          GSPC.CallStackChunk msg ->
             pure st{current_callstack_chunks = msg : current_callstack_chunks st}
-          GSP.StringDef msg ->
-            pure st{hydration_table = GSP.insertTextMessage msg (hydration_table st)}
-          GSP.SourceLocationDef msg ->
-            case GSP.insertSourceLocationMessage msg (hydration_table st) of
+          GSPC.StringDef msg ->
+            pure st{hydration_table = GSPC.insertTextMessage msg (hydration_table st)}
+          GSPC.SourceLocationDef msg ->
+            case GSPC.insertSourceLocationMessage msg (hydration_table st) of
               Left err ->
                 pure $ addDecodingErrorsForStack [fromMissingKeyError err] st
               Right newTable ->
@@ -214,16 +213,16 @@
 processCallStackMessage ::
   DB.Table IP.InfoProvId IP.InfoProv ->
   EventlogProfileState ->
-  GSP.CallStackMessage ->
+  GSPC.CallStack ->
   IO EventlogProfileState
-processCallStackMessage infoProvTable st0 GSP.MkCallStackMessage{callThreadId, callCapabilityId, callStack} = do
+processCallStackMessage infoProvTable st0 GSPC.MkCallStack{callThreadId, callCapabilityId, callStack} = do
   stackFramesOrErrors <- traverse (toStackFrame infoProvTable) callStack
   let
     (processingErrors, stackFrames) = partitionEithers stackFramesOrErrors
     (st1, stackFrameIds) = mapAccumR processStackFrame st0 stackFrames
     sample =
       Sample
-        { sampleThreadId = callThreadId
+        { sampleThreadId = GSPC.getThreadId callThreadId
         , sampleCapabilityId = callCapabilityId
         , -- TODO: Don't use an arbitrary cutoff, but reduce cycles within the stack.
           -- TODO: Perform cycle reduction or cutoff before the database lookups.
@@ -232,7 +231,7 @@
     st2 = addDecodingErrorsForStack processingErrors st1
   pure $ st2{samples = sample : st2.samples}
 
-hydrateBinaryEventlog :: EventlogProfileState -> GSP.BinaryCallStackMessage -> (GSP.CallStackMessage, EventlogProfileState)
+hydrateBinaryEventlog :: EventlogProfileState -> GSPC.CallStackChunk -> (GSPC.CallStack, EventlogProfileState)
 hydrateBinaryEventlog st msg =
   let
     chunks = current_callstack_chunks st
@@ -253,12 +252,12 @@
     --    [2,1]
     -- 3. When reading the eventlog, we store prepend later messages, resulting in:
     --    [2,1] [4,3] [6,5]
-    -- 4. 'catCallStackMessage' reverses the individual callstack chunks to be the inverse of 'chunkCallStackMessage'
+    -- 4. 'joinCallStackChunks' reverses the individual callstack chunks to be the inverse of 'chunkCallStack'
     orderedChunks = NonEmpty.reverse $ msg :| chunks
-    fullBinaryCallStackMessage = GSP.catCallStackMessage orderedChunks
+    fullBinaryCallStackMessage = GSPC.joinCallStackChunks orderedChunks
     (callStackMessage, errs) =
-      GSP.hydrateEventlogCallStackMessage
-        (GSP.mkIntMapSymbolTableReader (hydration_table st))
+      GSPC.hydrateEventlogCallStackMessage
+        (GSPC.mkIntMapSymbolTableReader (hydration_table st))
         fullBinaryCallStackMessage
   in
     ( callStackMessage
@@ -270,16 +269,16 @@
 -- | Convert a `StackItem` into a `StackFrame`.
 toStackFrame ::
   DB.Table IP.InfoProvId IP.InfoProv ->
-  GSP.StackItem ->
+  GSPC.StackItem ->
   IO (Either EventlogError StackFrame)
 toStackFrame infoProvTable = \case
-  GSP.IpeId (toInfoProvId -> infoProvId) -> do
+  GSPC.IpeId (toInfoProvId -> infoProvId) -> do
     maybeInfoProv <- DB.lookup infoProvTable infoProvId
     pure $
       case maybeInfoProv of
         Nothing -> Left $! UnknownInfoProvId infoProvId
         Just infoProv -> Right $! StackFrameInfoProv infoProv
-  GSP.UserAnnotation (Text.pack -> message) (toSrcLoc -> srcLoc) ->
+  GSPC.UserAnnotation (Text.pack -> message) (toSrcLoc -> srcLoc) ->
     pure . Right $! StackFrameMessage message srcLoc
 
 -- | Ensure a `StackFrame` has a `StackFrameId` in the `EventlogProfileState`.
@@ -327,11 +326,11 @@
   -- ^ A unique counter for stack frames.
   , samples :: [Sample]
   -- ^ All samples in the reverse order of finding them in the eventlog.
-  , hydration_table :: !GSP.IntMapTable
+  , hydration_table :: !GSPC.IntMapTable
   -- ^ The symbol table storing 'Text' and 'SourceLocation' symbols
-  -- for hydrating a 'BinaryCallStackMessage' into a 'CallStackMessage'.
-  , current_callstack_chunks :: [GSP.BinaryCallStackMessage]
-  -- ^ Chunks of 'BinaryCallStackMessage' we are currently decoding.
+  -- for hydrating a 'CallStackChunk' into a 'CallStack'.
+  , current_callstack_chunks :: [GSPC.CallStackChunk]
+  -- ^ Chunks of 'CallStackChunk' we are currently decoding.
   -- All chunks are assumed to be from the same callstack and will be decoded once a
   -- 'CallStackFinal' message is encountered.
   , processingErrors :: [[EventlogError]]
@@ -345,7 +344,7 @@
     , stackFrames = Map.empty
     , stackFrameCounter = 0
     , samples = []
-    , hydration_table = GSP.emptyIntMapTable
+    , hydration_table = GSPC.emptyIntMapTable
     , current_callstack_chunks = []
     , processingErrors = []
     }
@@ -361,19 +360,19 @@
 
 data EventlogError
   = UnknownInfoProvId IP.InfoProvId
-  | UnknownStringId GSP.StringId
-  | UnknownSrcLocId GSP.SourceLocationId
-  | SourceLocationPartUndefined GSP.SourceLocationId GSP.StringId
+  | UnknownStringId GSPC.StringId
+  | UnknownSrcLocId GSPC.SourceLocationId
+  | SourceLocationPartUndefined GSPC.SourceLocationId GSPC.StringId
   deriving (Show, Eq, Ord)
 
-fromBinaryError :: GSP.BinaryCallStackDecodeError -> EventlogError
+fromBinaryError :: GSPC.BinaryCallStackDecodeError -> EventlogError
 fromBinaryError = \case
-  GSP.StringIdNotFound sid -> UnknownStringId sid
-  GSP.SourceLocationIdNotFound sid -> UnknownSrcLocId sid
+  GSPC.StringIdNotFound sid -> UnknownStringId sid
+  GSPC.SourceLocationIdNotFound sid -> UnknownSrcLocId sid
 
-fromMissingKeyError :: GSP.MissingKeyError -> EventlogError
+fromMissingKeyError :: GSPC.MissingKeyError -> EventlogError
 fromMissingKeyError = \case
-  GSP.KeyStringIdNotFound sourceLocId stringId -> SourceLocationPartUndefined sourceLocId stringId
+  GSPC.KeyStringIdNotFound sourceLocId stringId -> SourceLocationPartUndefined sourceLocId stringId
 
 prettyEventlogError :: EventlogError -> String
 prettyEventlogError = \case
@@ -412,8 +411,8 @@
         { unit = Speedscope.Nanoseconds
         , name = programName <> " " <> Text.show sampleId
         , startValue = 0
-        , endValue = length samples
-        , weights = replicate (length samples) 1
+        , endValue = length sampleStack
+        , weights = replicate (length sampleStack) 1
         , samples = sampleStack
         }
 
@@ -423,16 +422,16 @@
       -- NOTE: groupSort is assumed to be stable
       PerThread -> groupSort $ toThreadSample <$> reverse samples
       PerCapability -> groupSort $ toCapabilitySample <$> reverse samples
-      NoAggregation -> [(1, toSingleProfileSample <$> reverse samples)]
+      NoAggregation -> [(0, toSingleProfileSample <$> reverse samples)]
    where
     toThreadSample :: Sample -> (Word64, [Int])
-    toThreadSample sample = (sample.sampleThreadId, toSingleProfileSample sample)
+    toThreadSample sample = (fromIntegral . sampleThreadId $ sample, toSingleProfileSample sample)
 
     toCapabilitySample :: Sample -> (Word64, [Int])
-    toCapabilitySample sample = (coerce sample.sampleCapabilityId, toSingleProfileSample sample)
+    toCapabilitySample sample = (fromIntegral . GSPC.getCapabilityId $ sample.sampleCapabilityId, toSingleProfileSample sample)
 
     toSingleProfileSample :: Sample -> [Int]
-    toSingleProfileSample sample = map (GSP.word64ToInt . coerce) sample.sampleStack
+    toSingleProfileSample sample = map (fromIntegral @Word64 @Int . coerce) sample.sampleStack
 
 toSpeedscopeFrame :: StackFrame -> Speedscope.Frame
 toSpeedscopeFrame = \case
diff --git a/src/GHC/Stack/Profiler/Speedscope/Options.hs b/src/GHC/Stack/Profiler/Speedscope/Options.hs
--- a/src/GHC/Stack/Profiler/Speedscope/Options.hs
+++ b/src/GHC/Stack/Profiler/Speedscope/Options.hs
@@ -142,9 +142,9 @@
 aggregationModeParser :: O.Parser AggregationMode
 aggregationModeParser =
   asum
-    [ O.flag' PerThread (O.long "per-thread" <> O.help "Group the profile per thread (default)")
-    , O.flag' PerCapability (O.long "per-capability" <> O.help "Group the profile per capability")
-    , O.flag' NoAggregation (O.long "no-aggregation" <> O.help "Perform no grouping, single view")
+    [ O.flag' PerThread (O.long "per-thread" <> O.help "Group the profiles by thread. (Default.)")
+    , O.flag' PerCapability (O.long "per-capability" <> O.help "Group the profiles by capability")
+    , O.flag' NoAggregation (O.long "no-aggregation" <> O.help "Do not aggregate the profiles.")
     , pure PerThread
     ]
 
diff --git a/src/GHC/Stack/Profiler/Speedscope/Types.hs b/src/GHC/Stack/Profiler/Speedscope/Types.hs
--- a/src/GHC/Stack/Profiler/Speedscope/Types.hs
+++ b/src/GHC/Stack/Profiler/Speedscope/Types.hs
@@ -3,9 +3,7 @@
 import Data.Text (Text)
 import qualified Data.Text as Text
 import Data.Word (Word64)
-import qualified GHC.Stack.Profiler.Core.Eventlog as GSP (CapabilityId, IpeId (..))
-import qualified GHC.Stack.Profiler.Core.SourceLocation as GSP (SourceLocation (..))
-import qualified GHC.Stack.Profiler.Core.Util as GSP
+import qualified GHC.Stack.Profiler.Core as GSP (CapabilityId, IpeId (..), SourceLocation (..))
 import IpeDB.Types.InfoProv (InfoProv (..), InfoProvId (..))
 import IpeDB.Types.SrcLoc (Point (..), Range (..), SrcLoc (..))
 
@@ -47,9 +45,9 @@
   | otherwise = Just (Text.pack srcFilePath)
 
 srcLocColumn :: SrcLoc -> Maybe Int
-srcLocColumn SrcLoc{srcRange = Just Range{start = Point{column}}} = Just (GSP.word32ToInt column)
+srcLocColumn SrcLoc{srcRange = Just Range{start = Point{column}}} = Just (fromIntegral column)
 srcLocColumn _ = Nothing
 
 srcLocLine :: SrcLoc -> Maybe Int
-srcLocLine SrcLoc{srcRange = Just Range{start = Point{line}}} = Just (GSP.word32ToInt line)
+srcLocLine SrcLoc{srcRange = Just Range{start = Point{line}}} = Just (fromIntegral line)
 srcLocLine _ = Nothing
