From 3da0a145b03ab7ebc7fa36470ff183e54e764851 Mon Sep 17 00:00:00 2001
From: Qingxin Wu <qingxinwu@google.com>
Date: Tue, 14 Nov 2023 01:04:20 -0500
Subject: [PATCH 1/7] Spec group-by-origin execution mode.

---
 spec.bs | 228 ++++++++++++++++++++++++++++++++------------------------
 1 file changed, 132 insertions(+), 96 deletions(-)

diff --git a/spec.bs b/spec.bs
index a27bcf81b..840573ae3 100644
--- a/spec.bs
+++ b/spec.bs
@@ -1313,19 +1313,20 @@ To <dfn>build bid generators map</dfn> given an [=auction config=] |auctionConfi
     1. If |ig|'s [=interest group/additional bid key=] is not null:
       1. [=map/Set=] |negativeTargetInfo|[(|buyer|, |igName|)] to (|ig|'s
         [=interest group/joining origin=], |ig|'s [=interest group/additional bid key=]).
+    1. Let |biddingUrl| be |ig|'s [=interest group/bidding url=].
     1. [=iteration/Continue=] if any of the following conditions hold:
-      * |ig|'s [=interest group/bidding url=] is null;
+      * |biddingUrl| is null;
       * |ig|'s [=interest group/ads=] is null, or [=list/is empty=].
     1. Let |signalsUrl| be |ig|'s [=interest group/trusted bidding signals url=].
     1. Let |joiningOrigin| be |ig|'s [=interest group/joining origin=].
     1. If |bidGenerators| does not [=map/contain=] |buyer|:
       1. Let |perBuyerGenerator| be a new [=per buyer bid generator=].
       1. Let |perSignalsUrlGenerator| be a new [=per signals url bid generator=].
-      1. [=map/Set=] |perSignalsUrlGenerator|[|joiningOrigin|] to « |ig| ».
+      1. Let |perBiddingUrlGenerator| be a new [=per bidding url bid generator=].
+      1. [=map/Set=] |perBiddingUrlGenerator|[|biddingUrl|] to « |ig| ».
+      1. [=map/Set=] |perSignalsUrlGenerator|[|joiningOrigin|] to |perBiddingUrlGenerator|.
       1. [=map/Set=] |perBuyerGenerator|[|signalsUrl|] to |perSignalsUrlGenerator|.
       1. [=map/Set=] |bidGenerators|[|buyer|] to |perBuyerGenerator|.
-      1. TODO: add a perBiddingScriptUrlGenerator layer that replaces the list of IGs with a map
-        from biddingScriptUrl to a list of IGs.
     1. Otherwise:
       1. Let |perBuyerGenerator| be |bidGenerators|[|buyer|].
       1. If |perBuyerGenerator| does not [=map/contain=] |signalsUrl|:
@@ -1334,9 +1335,13 @@ To <dfn>build bid generators map</dfn> given an [=auction config=] |auctionConfi
         1. [=map/Set=] |perBuyerGenerator|[|signalsUrl|] to |perSignalsUrlGenerator|.
       1. Otherwise:
         1. Let |perSignalsUrlGenerator| be |perBuyerGenerator|[|signalsUrl|].
-        1. If |perSignalsUrlGenerator| does not [=map/contain=] |joiningOrigin|, then [=map/set=]
-          |perSignalsUrlGenerator|[|joiningOrigin|] to « |ig| ».
-        1. Otherwise, [=list/append=] |ig| to |perSignalsUrlGenerator|[|joiningOrigin|].
+        1. If |perSignalsUrlGenerator| does not [=map/contain=] |joiningOrigin|:
+          1. Let |perBiddingUrlGenerator| be a new [=per bidding url bid generator=].
+          1. [=map/Set=] |perBiddingUrlGenerator|[|biddingUrl|] to « |ig| ».
+          1. [=map/Set=] |perSignalsUrlGenerator|[|joiningOrigin|] to |perBiddingUrlGenerator|.
+        1. Otherwise:
+          1. Let |perBiddingUrlGenerator| be |perSignalsUrlGenerator|[|joiningOrigin|].
+          1. [=list/Append=] |ig| to ||perBiddingUrlGenerator||[|biddingUrl|].
 1. Return « |bidGenerators|, |negativeTargetInfo| ».
 
 </div>
@@ -1347,7 +1352,7 @@ To <dfn>generate a bid</dfn> given an [=ordered map=] |allTrustedBiddingSignals|
 |auctionSignals|, a {{BiddingBrowserSignals}} |browserSignals|, a [=string=]-or-null |perBuyerSignals|,
 a {{DirectFromSellerSignalsForBuyer}} |directFromSellerSignalsForBuyer|, a [=duration=]
 |perBuyerTimeout| in milliseconds, a [=currency tag=] |expectedCurrency|, an [=interest group=] |ig|,
-and a [=moment=] |auctionStartTime|:
+a [=moment=] |auctionStartTime|, and a [=ECMAScript/realm=] |realm|:
   1. Let |igGenerateBid| be the result of [=building an interest group passed to generateBid=]
     with |ig|.
   1. Set |browserSignals|["{{BiddingBrowserSignals/joinCount}}"] to the sum of |ig|'s
@@ -1383,7 +1388,7 @@ and a [=moment=] |auctionStartTime|:
       |allTrustedBiddingSignals|[|key|].
   1. Return the result of [=evaluating a bidding script=] with |biddingScript|, |ig|, |expectedCurrency|,
     |igGenerateBid|, |auctionSignals|, |perBuyerSignals|, |trustedBiddingSignals|, |browserSignals|,
-    |directFromSellerSignalsForBuyer|, and |perBuyerTimeout|.
+    |directFromSellerSignalsForBuyer|, |perBuyerTimeout| and |realm|.
 </div>
 
 <div algorithm="generate and score bids">
@@ -1534,11 +1539,11 @@ To <dfn>generate and score bids</dfn> given an [=auction config=] |auctionConfig
     1. Let |keys| be a new [=ordered set=].
     1. Let |igNames| be a new [=ordered set=].
     1. Let |fetchSignalStartTime| be |settings|'s [=environment settings object/current monotonic time=].
-
-    1. [=map/For each=] joiningOrigin → |groups| of |perSignalsUrlGenerator|:
-      1. [=list/For each=] |ig| of |groups|:
-        1. [=set/Append=] |ig|'s [=interest group/trusted bidding signals keys=] to |keys|.
-        1. [=set/Append=] |ig|'s [=interest group/name=] to |igNames|.
+    1. [=map/For each=] |joiningOrigin| → |perBiddingUrlGenerator| of |perSignalsUrlGenerator|:
+      1. [=map/For each=] biddingUrl → |groups| of |perBiddingUrlGenerator|:
+        1. [=list/For each=] |ig| of |groups|:
+          1. [=set/Append=] |ig|'s [=interest group/trusted bidding signals keys=] to |keys|.
+          1. [=set/Append=] |ig|'s [=interest group/name=] to |igNames|.
     1. Let |biddingSignalsUrl| be the result of [=building trusted bidding signals url=] with
       |signalsUrl|, |keys|, |igNames|, |buyerExperimentGroupId|, and |topLevelOrigin|.
     1. Let « |allTrustedBiddingSignals|, |dataVersion| » be the result of [=fetching trusted signals=]
@@ -1550,66 +1555,70 @@ To <dfn>generate and score bids</dfn> given an [=auction config=] |auctionConfig
     1. If |perBuyerCumulativeTimeout| is not null:
       1. Decrement |perBuyerCumulativeTimeout| by |fetchSignalDuration|.
       1. If |perBuyerCumulativeTimeout| is negative, then [=iteration/break=];
-    1. [=map/For each=] joiningOrigin → |groups| of |perSignalsUrlGenerator|:
-      1. [=list/For each=] |ig| of |groups|:
-        1. If |ig|'s [=interest group/bidding url=] is null, [=iteration/continue=].
-        1. If |perBuyerCumulativeTimeout| is not null and is less than |perBuyerTimeout|, then set
-          |perBuyerTimeout| to |perBuyerCumulativeTimeout|.
-        1. Let |generateBidStartTime| be |settings|'s
-          [=environment settings object/current monotonic time=].
-        1. Let |generatedBid| be the result of [=generate a bid=] given |allTrustedBiddingSignals|,
-          |auctionSignals|, a [=map/clone=] of |browserSignals|, |perBuyerSignals|,
-          |perBuyerTimeout|, |expectedCurrency|, |ig|, and |auctionStartTime|.
-        1. Let |generateBidDuration| be the [=duration from=] |generateBidStartTime| to |settings|'s
-          [=environment settings object/current monotonic time=], in milliseconds.
-        1. If |perBuyerCumulativeTimeout| is not null, decrement |perBuyerCumulativeTimeout| by
-          |generateBidDuration|.
-        1. If |generatedBid| is failure, [=iteration/continue=].
-        1. If [=query generated bid k-anonymity count=] given |generatedBid| returns false:
-
-          Note: [=Generate a bid=] is now rerun with only k-anonymous [=interest group/ads=] to give
-          the buyer a chance to [=generate a bid=] for k-anonymous [=interest group/ads=]. Allowing
-          the buyer to first [=generate a bid=] for non-k-anonymous [=interest group/ads=] provides a
-          mechanism to bootstrap the k-anonymity count, otherwise no [=interest group/ads=] would
-          ever trigger [=increment k-anonymity count=] and all ads would fail
-          [=query k-anonymity count=].
-          1. TODO: Run [=score and rank a bid=] on |generatedBid| to find the highest scoring bid
-            that isn't k-anonymous. After the auction, if the highest scoring bid that isn't
-            k-anonymous has a higher score than the highest scoring k-anonymous bid, then call
-            [=increment ad k-anonymity count=] on it.
-          1. Let |originalAds| be |ig|'s [=interest group/ads=].
-          1. If |originalAds| is not null:
-            1. Set |ig|'s [=interest group/ads=] to a new [=list=] of [=interest group ad=].
-            1. [=list/For each=] |ad| in |originalAds|:
-              1. If [=query ad k-anonymity count=] given |ig| and |ad|'s
-                [=interest group ad/render url=] returns true, [=list/append=] |ad| to |ig|'s
-                [=interest group/ads=].
-          1. Let |originalAdComponents| be |ig|'s [=interest group/ad components=].
-          1. If |originalAdComponents| is not null:
-            1. Set |ig|'s [=interest group/ad components=] to a new [=list=] of [=interest group ad=].
-            1. [=list/For each=] |adComponent| in |originalAdComponents|:
-              1. If [=query component ad k-anonymity count=] given |adComponent|'s
-                [=interest group ad/render url=] returns true, [=list/append=] |adComponent| to |ig|'s
-                [=interest group/ad components=].
+    1. [=map/For each=] |joiningOrigin| → |perBiddingUrlGenerator| of |perSignalsUrlGenerator|:
+      1. [=map/For each=] biddingUrl → |groups| of |perBiddingUrlGenerator|:
+        1. Let |realmForOriginGroupMode| be null.
+        1. [=list/For each=] |ig| of |groups|:
           1. If |perBuyerCumulativeTimeout| is not null and is less than |perBuyerTimeout|, then set
             |perBuyerTimeout| to |perBuyerCumulativeTimeout|.
           1. Let |generateBidStartTime| be |settings|'s
             [=environment settings object/current monotonic time=].
-          1. Set |generatedBid| to the result of [=generate a bid=] given
+          1. Let « |generatedBid|, |realm| » be the result of [=generate a bid=] given
             |allTrustedBiddingSignals|, |auctionSignals|, a [=map/clone=] of |browserSignals|,
-            |perBuyerSignals|, |directFromSellerSignalsForBuyer|, |perBuyerTimeout|, |expectedCurrency|,
-            and |ig|.
-          1. Set |ig|'s [=interest group/ads=] to |originalAds|.
-          1. Set |ig|'s [=interest group/ad components=] to |originalAdComponents|.
-          1. Let |generateBidDuration| be the [=duration from=] |generateBidStartTime| to
-            |settings|'s [=environment settings object/current monotonic time=], in milliseconds.
-          1. If |perBuyerCumulativeTimeout| is not null, then decrement |perBuyerCumulativeTimeout|
-            by |generateBidDuration|.
+            |perBuyerSignals|, |perBuyerTimeout|, |expectedCurrency|, |ig|, |auctionStartTime|, and
+            |realmForOriginGroupMode|.
+          1. Set |realmForOriginGroupMode| to |realm|.
+          1. Let |generateBidDuration| be the [=duration from=] |generateBidStartTime| to |settings|'s
+            [=environment settings object/current monotonic time=], in milliseconds.
+          1. If |perBuyerCumulativeTimeout| is not null, decrement |perBuyerCumulativeTimeout| by
+            |generateBidDuration|.
           1. If |generatedBid| is failure, [=iteration/continue=].
-        1. [=list/Insert=] |generatedBid|'s [=generated bid/interest group=] in |bidIgs|.
-        1. [=Score and rank a bid=] with |auctionConfig|, |generatedBid|, |leadingBidInfo|,
-          |decisionLogicScript|, |directFromSellerSignalsForSeller|, |dataVersion|, |auctionLevel|,
-          |componentAuctionExpectedCurrency|, and |topLevelOrigin|.
+          1. If [=query generated bid k-anonymity count=] given |generatedBid| returns false:
+
+            Note: [=Generate a bid=] is now rerun with only k-anonymous [=interest group/ads=] to give
+            the buyer a chance to [=generate a bid=] for k-anonymous [=interest group/ads=]. Allowing
+            the buyer to first [=generate a bid=] for non-k-anonymous [=interest group/ads=] provides a
+            mechanism to bootstrap the k-anonymity count, otherwise no [=interest group/ads=] would
+            ever trigger [=increment k-anonymity count=] and all ads would fail
+            [=query k-anonymity count=].
+            1. TODO: Run [=score and rank a bid=] on |generatedBid| to find the highest scoring bid
+              that isn't k-anonymous. After the auction, if the highest scoring bid that isn't
+              k-anonymous has a higher score than the highest scoring k-anonymous bid, then call
+              [=increment ad k-anonymity count=] on it.
+            1. Let |originalAds| be |ig|'s [=interest group/ads=].
+            1. If |originalAds| is not null:
+              1. Set |ig|'s [=interest group/ads=] to a new [=list=] of [=interest group ad=].
+              1. [=list/For each=] |ad| in |originalAds|:
+                1. If [=query ad k-anonymity count=] given |ig| and |ad|'s
+                  [=interest group ad/render url=] returns true, [=list/append=] |ad| to |ig|'s
+                  [=interest group/ads=].
+            1. Let |originalAdComponents| be |ig|'s [=interest group/ad components=].
+            1. If |originalAdComponents| is not null:
+              1. Set |ig|'s [=interest group/ad components=] to a new [=list=] of [=interest group ad=].
+              1. [=list/For each=] |adComponent| in |originalAdComponents|:
+                1. If [=query component ad k-anonymity count=] given |adComponent|'s
+                  [=interest group ad/render url=] returns true, [=list/append=] |adComponent| to |ig|'s
+                  [=interest group/ad components=].
+            1. If |perBuyerCumulativeTimeout| is not null and is less than |perBuyerTimeout|, then set
+              |perBuyerTimeout| to |perBuyerCumulativeTimeout|.
+            1. Let |generateBidStartTime| be |settings|'s
+              [=environment settings object/current monotonic time=].
+            1. Let « |generatedBidResult|, realmIgnored » be the result of [=generate a bid=] given
+              |allTrustedBiddingSignals|, |auctionSignals|, a [=map/clone=] of |browserSignals|,
+              |perBuyerSignals|, |directFromSellerSignalsForBuyer|, |perBuyerTimeout|,
+              |expectedCurrency|, |ig|, |auctionStartTime| and |realmForOriginGroupMode|.
+            1. Set |generatedBid| to |generatedBidResult|.
+            1. Set |ig|'s [=interest group/ads=] to |originalAds|.
+            1. Set |ig|'s [=interest group/ad components=] to |originalAdComponents|.
+            1. Let |generateBidDuration| be the [=duration from=] |generateBidStartTime| to
+              |settings|'s [=environment settings object/current monotonic time=], in milliseconds.
+            1. If |perBuyerCumulativeTimeout| is not null, then decrement |perBuyerCumulativeTimeout|
+              by |generateBidDuration|.
+            1. If |generatedBid| is failure, [=iteration/continue=].
+          1. [=list/Insert=] |generatedBid|'s [=generated bid/interest group=] in |bidIgs|.
+          1. [=Score and rank a bid=] with |auctionConfig|, |generatedBid|, |leadingBidInfo|,
+            |decisionLogicScript|, |directFromSellerSignalsForSeller|, |dataVersion|, |auctionLevel|,
+            |componentAuctionExpectedCurrency|, and |topLevelOrigin|.
   1. Decrement |pendingBuyers| by 1.
 1. Wait until both |pendingBuyers| and |pendingAdditionalBids| are 0.
 1. If |leadingBidInfo|'s [=leading bid info/leading bid=] is null, return null.
@@ -2939,9 +2948,6 @@ execution environment. In particular, they:
     1. Let |agent| be the result of [=obtaining a script runner agent=] given null, true, and false.
       Run the rest of these steps in |agent|.
 
-       Issue: This exclusively creates a new [=ECMAScript/agent cluster=] for a given script to run
-       in, but we should make this work with [=interest group/execution mode=] somehow.
-
     1. Let |realmExecutionContext| be the result of [=creating a new realm=] given |agent| and the
        following customizations:
 
@@ -2981,10 +2987,22 @@ of the following global objects:
   [=currency tag=] |expectedCurrency|, a {{GenerateBidInterestGroup}} |igGenerateBid|, a [=string=]-or-null
   |auctionSignals|, a [=string=]-or-null |perBuyerSignals|, an [=ordered map=] |trustedBiddingSignals|,
   a {{BiddingBrowserSignals}} |browserSignals|, a {{DirectFromSellerSignalsForBuyer}}
-  |directFromSellerSignalsForBuyer| and an integer millisecond [=duration=] |timeout|:
+  |directFromSellerSignalsForBuyer|, an integer millisecond [=duration=] |timeout|, and a
+  [=ECMAScript/realm=]-or-null |realmForOriginGroupMode|:
+
+    1. Let |realmIsReusedForOriginGroupMode| be false.
+
+    1. If |ig|'s [=interest group/execution mode=] is "group-by-origin", and |realmForOriginGroupMode|
+      is not null, then:
+      1. Let |realm| be |realmForOriginGroupMode|.
+      1. Set |realmIsReusedForOriginGroupMode| to true.
+
+    1. Otherwise:
+      1. Let |realm| be the result of [=creating a new script runner realm=] given
+        {{InterestGroupBiddingScriptRunnerGlobalScope}}.
+      1. If |ig|'s [=interest group/execution mode=] is "group-by-origin", then set
+        |realmForOriginGroupMode| to |realm|.
 
-    1. Let |realm| be the result of [=creating a new script runner realm=] given
-      {{InterestGroupBiddingScriptRunnerGlobalScope}}.
     1. Let |global| be |realm|'s [=realm/global object=].
     1. Let |settings| be |realm|'s [=realm/settings object=].
 
@@ -3010,7 +3028,8 @@ of the following global objects:
     1. Let |directFromSellerSignalsJs| be |directFromSellerSignalsForBuyer|
       [=converted to ECMAScript values=].
     1. Let |startTime| be |settings|'s [=environment settings object/current monotonic time=].
-    1. Let |result| be the result of [=evaluating a script=] with |realm|, |script|, "`generateBid`",
+    1. Let |result| be the result of [=evaluating a script=] with |realm|,
+      |realmIsReusedForOriginGroupMode|, |script|, "`generateBid`",
       « |igJS|, |auctionSignalsJS|, |perBuyerSignalsJS|, |trustedBiddingSignalsJS|, |browserSignalsJS|,
       |directFromSellerSignalsJs| », and |timeout|.
     1. Let |duration| be |settings|'s [=environment settings object/current monotonic time=] minus
@@ -3043,7 +3062,7 @@ of the following global objects:
     1. If |generatedBid| is not failure:
       1. Set |generatedBid|'s [=generated bid/bid duration=] to |duration|.
       1. Set |generatedBid|'s [=generated bid/interest group=] to |ig|.
-    1. Return |generatedBid|.
+    1. Return « |generatedBid|, |realmForOriginGroupMode| ».
 </div>
 
 <div algorithm>
@@ -3060,7 +3079,7 @@ of the following global objects:
     1. Let |trustedScoringSignalsJS| be |trustedScoringSignals| [=converted to ECMAScript values=].
     1. Let |directFromSellerSignalsJs| be |directFromSellerSignalsForSeller|
       [=converted to ECMAScript values=].
-    1. Return the result of [=evaluating a script=] with |realm|, |script|, "`scoreAd`",
+    1. Return the result of [=evaluating a script=] with |realm|, false, |script|, "`scoreAd`",
       «|adMetadata|, |bidValue|, |auctionConfigJS|, |trustedScoringSignalsJS|, |browserSignalsJS|,
       |directFromSellerSignalsJs|», and |timeout|.
 </div>
@@ -3075,7 +3094,7 @@ of the following global objects:
     1. Let |argumentsJS| be the result of [=converting a Web IDL arguments list to an ECMAScript
       arguments list|converting=] |arguments| to an ECMAScript arguments list. If this
       [=exception/throws=] an exception, return « "null", null, null, null ».
-    1. Let |result| be the result of [=evaluating a script=] with |realm|, |script|,
+    1. Let |result| be the result of [=evaluating a script=] with |realm|, false, |script|,
       |functionName|, |argumentsJS|, and 50 milliseconds.
     1. If |result| is an [=ECMAScript/abrupt completion=], return « "null", null, null, null ».
     1. Let |resultJSON| be "null".
@@ -3093,8 +3112,9 @@ of the following global objects:
 </div>
 
 <div algorithm>
-  To <dfn>evaluate a script</dfn> with a [=ECMAScript/realm=] |realm|, [=string=] |script|, [=string=]
-  |functionName|, a [=list=] |arguments|, and an integer millisecond [=duration=] |timeout|, run these steps.
+  To <dfn>evaluate a script</dfn> with a [=ECMAScript/realm=] |realm|, a [=boolean=]
+  |realmIsReusedForOriginGroupMode|, [=string=] |script|, [=string=] |functionName|, a [=list=]
+  |arguments|, and an integer millisecond [=duration=] |timeout|, run these steps.
   They return a [=ECMAScript/Completion Record=], which is either an [=ECMAScript/abrupt completion=] (in
   the case of a parse failure or execution error), or a [=ECMAScript/normal completion=] populated with the
   [=ECMAScript/ECMAScript language value=] result of invoking |functionName|.
@@ -3106,24 +3126,37 @@ of the following global objects:
 
     1. Let |global| be |realm|'s [=realm/global object=], and run these steps in |realm|'s [=realm/agent=]:
 
-    1. Let |result| be [$ParseScript$](|script|, |realm|, `empty`).
+    1. If |realmIsReusedForOriginGroupMode|:
+
+      1. [=Assert=] |functionName| is "`generateBid`".
 
-       Note: The resulting [=ECMAScript/Script Record=] will have no \[[HostDefined]] component,
-       unlike traditional [=scripts=] on the web platform.
+      1. *Prepare to run script*: Push |realmExecutionContext| onto the
+        [=ECMAScript/execution context stack|JavaScript execution context stack=]; it is now the
+        [=ECMAScript/running execution context|running JavaScript execution context=].
 
-    1. If |result| is a list of errors, return
-       Completion { \[[Type]]: `throw`, \[[Value]]: |result|, \[[Target]]: `empty` }.
+      Note: When the execution environment is re-used in "`group-by-origin`" mode, the top-level script
+        will not be re-executed, with only the "`generateBid()`" function being run the subsequent times.
 
-    1. [=Assert=]: |result| is a [=ECMAScript/Script Record=].
+    1. Otherwise:
 
-    1. *Prepare to run script*: Push |realmExecutionContext| onto the [=ECMAScript/execution context
-       stack|JavaScript execution context stack=]; it is now the [=ECMAScript/running execution
-       context|running JavaScript execution context=].
+      1. Let |result| be [$ParseScript$](|script|, |realm|, `empty`).
 
-    1. Let |evaluationStatus| be the result of [$ScriptEvaluation$](result).
+         Note: The resulting [=ECMAScript/Script Record=] will have no \[[HostDefined]] component,
+         unlike traditional [=scripts=] on the web platform.
 
-    1. If |evaluationStatus| is an [=ECMAScript/abrupt completion=], jump to the step labeled <i>
-       <a href="#evaluate-script-return">return</a></i>.
+      1. If |result| is a list of errors, return
+         Completion { \[[Type]]: `throw`, \[[Value]]: |result|, \[[Target]]: `empty` }.
+
+      1. [=Assert=]: |result| is a [=ECMAScript/Script Record=].
+
+      1. *Prepare to run script*: Push |realmExecutionContext| onto the [=ECMAScript/execution context
+         stack|JavaScript execution context stack=]; it is now the [=ECMAScript/running execution
+         context|running JavaScript execution context=].
+
+      1. Let |evaluationStatus| be the result of [$ScriptEvaluation$](result).
+
+      1. If |evaluationStatus| is an [=ECMAScript/abrupt completion=], jump to the step labeled <i>
+         <a href="#evaluate-script-return">return</a></i>.
 
     1. Let |F| be [$Get$](|global|, |functionName|). If that returns a [=ECMAScript/throw completion=],
        set |finalCompletion| to |F| and jump to the step labeled <i>
@@ -3574,7 +3607,7 @@ The <dfn for=Navigator method>updateAdInterestGroups()</dfn> method steps are:
 
         <dt>"`executionMode`"
         <dd>
-        1. If |value| is "`compatibility`" or "`group-by-origin`",
+        1. If |value| is "`compatibility`" or "`group-by-origin`" or "`frozen-context`",
           set |ig|'s [=interest group/execution mode=] to |value|.
         1. Otherwise, jump to the step labeled <i><a href=#abort-update>Abort update</a></i>.
 
@@ -4125,7 +4158,7 @@ An interest group is a [=struct=] with the following [=struct/items=]:
 :: Null or an [=ordered map=] whose [=map/keys=] are [=strings=] and whose [=map/values=] are
   {{double}}. Overrides the {{AuctionAdConfig}}'s corresponding priority signals.
 : <dfn>execution mode</dfn>
-:: "`compatibility`" or "`group-by-origin`".
+:: "`compatibility`" or "`group-by-origin`" or "`frozen-context`".
   TODO: Define spec for these execution modes, link to it from here and explain these modes.
 : <dfn>bidding url</dfn>
 :: Null or a [=URL=]. The URL to fetch the buyer's JavaScript from.
@@ -4466,9 +4499,12 @@ A per buyer bid generator is an [=ordered map=] whose [=map/keys=] are [=URLs=]
 <h3 dfn-type=dfn>Per signals url bid generator</h3>
 
 A per signals url bid generator is an [=ordered map=] whose [=map/keys=] are [=origins=]
-representing [=interest group/joining origins=], and whose [=map/values=] are [=lists=] of
-[=interest groups=].
+representing [=interest group/joining origins=], and whose [=map/values=] are
+[=per bidding url bid generators=].
 
+A <dfn>per bidding url bid generator</dfn> is an [=ordered map=] whose [=map/keys=] are [=URLs=]
+representing [=interest group/bidding urls=], and whose [=map/values=] are [=lists=] of
+[=interest groups=].
 
 <h3 dfn-type=dfn>Previous win</h3>
 

From d4b0b9a4cafc34287a8975756ac39ad47f1e8395 Mon Sep 17 00:00:00 2001
From: Qingxin Wu <qingxinwu@google.com>
Date: Tue, 14 Nov 2023 14:08:03 -0500
Subject: [PATCH 2/7] Add a note about what build generator does.

---
 spec.bs | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/spec.bs b/spec.bs
index 840573ae3..74a9b4cbb 100644
--- a/spec.bs
+++ b/spec.bs
@@ -1303,6 +1303,17 @@ To <dfn>parse an https origin</dfn> given a [=string=] |input|:
 <div algorithm>
 
 To <dfn>build bid generators map</dfn> given an [=auction config=] |auctionConfig|:
+
+  <div class="note"> This algorithm builds an [=ordered map=] with:
+    * key: An [=origin=], which is a buyer of [=auction config/interest group buyers=]
+    * value: A [=per buyer bid generator=], which is an [=ordered map=] with
+      * key: An [=URL=], which is [=interest group/trusted bidding signals url=]
+      * value: A [=per signals url bid generator=], which is an [=ordered map=] with
+        * key: An [=origin=], which is [=interest group/joining origin=]
+        * value: A [=per bidding url bid generator=], which is an [=ordered map=] with
+          * key: A [=URL=], which is [=interest group/bidding url=]
+          * value: A [=list=] of [=interest groups=]
+  </div>
 1. Let |bidGenerators| be a new [=ordered map=] whose [=map/keys=] are [=origins=] and whose
   [=map/values=] are [=per buyer bid generators=].
 1. Let |negativeTargetInfo| be a new [=negative target info=].

From e7d0a001020e0767717c5cd9a23232d30e60774e Mon Sep 17 00:00:00 2001
From: Qingxin Wu <qingxinwu@google.com>
Date: Wed, 15 Nov 2023 00:01:26 -0500
Subject: [PATCH 3/7] Reuse context for kanon rerun.

---
 spec.bs | 35 +++++++++++++++++------------------
 1 file changed, 17 insertions(+), 18 deletions(-)

diff --git a/spec.bs b/spec.bs
index 74a9b4cbb..be9115356 100644
--- a/spec.bs
+++ b/spec.bs
@@ -1617,7 +1617,7 @@ To <dfn>generate and score bids</dfn> given an [=auction config=] |auctionConfig
             1. Let « |generatedBidResult|, realmIgnored » be the result of [=generate a bid=] given
               |allTrustedBiddingSignals|, |auctionSignals|, a [=map/clone=] of |browserSignals|,
               |perBuyerSignals|, |directFromSellerSignalsForBuyer|, |perBuyerTimeout|,
-              |expectedCurrency|, |ig|, |auctionStartTime| and |realmForOriginGroupMode|.
+              |expectedCurrency|, |ig|, |auctionStartTime| and |realm|.
             1. Set |generatedBid| to |generatedBidResult|.
             1. Set |ig|'s [=interest group/ads=] to |originalAds|.
             1. Set |ig|'s [=interest group/ad components=] to |originalAdComponents|.
@@ -2999,20 +2999,19 @@ of the following global objects:
   |auctionSignals|, a [=string=]-or-null |perBuyerSignals|, an [=ordered map=] |trustedBiddingSignals|,
   a {{BiddingBrowserSignals}} |browserSignals|, a {{DirectFromSellerSignalsForBuyer}}
   |directFromSellerSignalsForBuyer|, an integer millisecond [=duration=] |timeout|, and a
-  [=ECMAScript/realm=]-or-null |realmForOriginGroupMode|:
+  [=ECMAScript/realm=]-or-null |realmForOriginGroupModeOrRerun|, and a [=boolean=] |isKAnonRerun|:
 
-    1. Let |realmIsReusedForOriginGroupMode| be false.
+    1. Let |reuseRealm| be false.
+    1. Set |reuseRealm| to true if all of the following conditions hold:
+      * |realmForOriginGroupModeOrRerun| is not null;
+      * |ig|'s [=interest group/execution mode=] is "group-by-origin", or |isKAnonRerun| is true.
 
-    1. If |ig|'s [=interest group/execution mode=] is "group-by-origin", and |realmForOriginGroupMode|
-      is not null, then:
-      1. Let |realm| be |realmForOriginGroupMode|.
-      1. Set |realmIsReusedForOriginGroupMode| to true.
+    1. If |reuseRealm| is true, then let |realm| be |realmForOriginGroupModeOrRerun|.
 
     1. Otherwise:
       1. Let |realm| be the result of [=creating a new script runner realm=] given
         {{InterestGroupBiddingScriptRunnerGlobalScope}}.
-      1. If |ig|'s [=interest group/execution mode=] is "group-by-origin", then set
-        |realmForOriginGroupMode| to |realm|.
+      1. Set |realmForOriginGroupModeOrRerun| to |realm|.
 
     1. Let |global| be |realm|'s [=realm/global object=].
     1. Let |settings| be |realm|'s [=realm/settings object=].
@@ -3023,8 +3022,8 @@ of the following global objects:
       [=InterestGroupBiddingScriptRunnerGlobalScope/group has ad components=] to true if |ig|'s
       [=interest group/ad components=] is not null, or false otherwise.
     1. Set |global|'s [=InterestGroupBiddingScriptRunnerGlobalScope/expected currency=] to |expectedCurrency|.
-    1. Let |isComponentAuction| be true if |browserSignals|["{{BiddingBrowserSignals/topLevelSeller}}"] is not null, or
-      false otherwise.
+    1. Let |isComponentAuction| be true if |browserSignals|["{{BiddingBrowserSignals/topLevelSeller}}"]
+      is not null, or false otherwise.
     1. Set |global|'s [=InterestGroupBiddingScriptRunnerGlobalScope/is component auction=] to
       |isComponentAuction|.
     1. Set |global|'s [=InterestGroupBiddingScriptRunnerGlobalScope/interest group=] to |ig|.
@@ -3039,8 +3038,7 @@ of the following global objects:
     1. Let |directFromSellerSignalsJs| be |directFromSellerSignalsForBuyer|
       [=converted to ECMAScript values=].
     1. Let |startTime| be |settings|'s [=environment settings object/current monotonic time=].
-    1. Let |result| be the result of [=evaluating a script=] with |realm|,
-      |realmIsReusedForOriginGroupMode|, |script|, "`generateBid`",
+    1. Let |result| be the result of [=evaluating a script=] with |realm|, |reuseRealm|, |script|, "`generateBid`",
       « |igJS|, |auctionSignalsJS|, |perBuyerSignalsJS|, |trustedBiddingSignalsJS|, |browserSignalsJS|,
       |directFromSellerSignalsJs| », and |timeout|.
     1. Let |duration| be |settings|'s [=environment settings object/current monotonic time=] minus
@@ -3073,7 +3071,7 @@ of the following global objects:
     1. If |generatedBid| is not failure:
       1. Set |generatedBid|'s [=generated bid/bid duration=] to |duration|.
       1. Set |generatedBid|'s [=generated bid/interest group=] to |ig|.
-    1. Return « |generatedBid|, |realmForOriginGroupMode| ».
+    1. Return « |generatedBid|, |realmForOriginGroupModeOrRerun| ».
 </div>
 
 <div algorithm>
@@ -3124,7 +3122,7 @@ of the following global objects:
 
 <div algorithm>
   To <dfn>evaluate a script</dfn> with a [=ECMAScript/realm=] |realm|, a [=boolean=]
-  |realmIsReusedForOriginGroupMode|, [=string=] |script|, [=string=] |functionName|, a [=list=]
+  |reuseRealm|, [=string=] |script|, [=string=] |functionName|, a [=list=]
   |arguments|, and an integer millisecond [=duration=] |timeout|, run these steps.
   They return a [=ECMAScript/Completion Record=], which is either an [=ECMAScript/abrupt completion=] (in
   the case of a parse failure or execution error), or a [=ECMAScript/normal completion=] populated with the
@@ -3137,7 +3135,7 @@ of the following global objects:
 
     1. Let |global| be |realm|'s [=realm/global object=], and run these steps in |realm|'s [=realm/agent=]:
 
-    1. If |realmIsReusedForOriginGroupMode|:
+    1. If |reuseRealm|:
 
       1. [=Assert=] |functionName| is "`generateBid`".
 
@@ -3145,8 +3143,9 @@ of the following global objects:
         [=ECMAScript/execution context stack|JavaScript execution context stack=]; it is now the
         [=ECMAScript/running execution context|running JavaScript execution context=].
 
-      Note: When the execution environment is re-used in "`group-by-origin`" mode, the top-level script
-        will not be re-executed, with only the "`generateBid()`" function being run the subsequent times.
+      Note: When the execution environment is re-used in "`group-by-origin`" mode or kAnon rerun,
+        the top-level script will not be re-executed, with only the "`generateBid()`" function being
+        run the subsequent times.
 
     1. Otherwise:
 

From 7149411984f06419b1e9f6621044b0dd938a5be5 Mon Sep 17 00:00:00 2001
From: Qingxin Wu <qingxinwu@google.com>
Date: Thu, 15 Feb 2024 00:50:52 -0500
Subject: [PATCH 4/7] Fix merge

---
 spec.bs | 48 ++++++++++++++++++++++--------------------------
 1 file changed, 22 insertions(+), 26 deletions(-)

diff --git a/spec.bs b/spec.bs
index 9fbf4c896..0661f259c 100644
--- a/spec.bs
+++ b/spec.bs
@@ -1262,14 +1262,13 @@ To <dfn>build bid generators map</dfn> given an [=auction config=] |auctionConfi
   <div class="note"> This algorithm builds an [=ordered map=] with:
     * key: An [=origin=], which is a buyer of [=auction config/interest group buyers=]
     * value: A [=per buyer bid generator=], which is an [=ordered map=] with
-      * key: An [=URL=], which is [=interest group/trusted bidding signals url=]
-      * value: 
-        * key: 
-        * value: A [=per signals url bid generator=], which is an [=ordered map=] with
-          * key: An [=origin=], which is [=interest group/joining origin=]
-          * value: A [=per bidding url bid generator=], which is an [=ordered map=] with
-            * key: A [=URL=], which is [=interest group/bidding url=]
-            * value: A [=list=] of [=interest groups=]
+      * key: A tuple consisting of a [=URL=], which is [=interest group/trusted bidding signals url=],
+        and a [=string=], which is the ad slot size query param.
+      * value: A [=per signals url bid generator=], which is an [=ordered map=] with
+        * key: An [=origin=], which is [=interest group/joining origin=]
+        * value: A [=per bidding url bid generator=], which is an [=ordered map=] with
+          * key: A [=URL=], which is [=interest group/bidding url=]
+          * value: A [=list=] of [=interest groups=]
   </div>
 1. Let |bidGenerators| be a new [=ordered map=] whose [=map/keys=] are [=origins=] and whose
   [=map/values=] are [=per buyer bid generators=].
@@ -1295,30 +1294,27 @@ To <dfn>build bid generators map</dfn> given an [=auction config=] |auctionConfi
       1. Let |perBiddingUrlGenerator| be a new [=per bidding url bid generator=].
       1. [=map/Set=] |perBiddingUrlGenerator|[|biddingUrl|] to « |ig| ».
       1. [=map/Set=] |perSignalsUrlGenerator|[|joiningOrigin|] to |perBiddingUrlGenerator|.
-      1. [=map/Set=] |perBuyerGenerator|[|signalsUrl|] to |perSignalsUrlGenerator|.
+      1. [=map/Set=] |perBuyerGenerator|[(|signalsUrl|, |slotSizeQueryParam|)] to
+        |perSignalsUrlGenerator|.
       1. [=map/Set=] |bidGenerators|[|buyer|] to |perBuyerGenerator|.
     1. Otherwise:
       1. Let |perBuyerGenerator| be |bidGenerators|[|buyer|].
-      1. If |perBuyerGenerator| does not [=map/contain=] |signalsUrl|:
+      1. If |perBuyerGenerator| does not [=map/contain=] (|signalsUrl|, |slotSizeQueryParam|):
         1. Let |perSignalsUrlGenerator| be a new [=per signals url bid generator=].
-        1. [=map/Set=] |perSignalsUrlGenerator|[|joiningOrigin|] to « |ig| ».
-        1. [=map/Set=] |perSlotSizeQueryParam|[|slotSizeQueryParam|] to |perSignalsUrlGenerator|.
-        1. [=map/Set=] |perBuyerGenerator|[|signalsUrl|] to |perSlotSizeQueryParam|.
+        1. Let |perBiddingUrlGenerator| be a new [=per bidding url bid generator=].
+        1. [=map/Set=] |perBiddingUrlGenerator|[|biddingUrl|] to « |ig| ».
+        1. [=map/Set=] |perSignalsUrlGenerator|[|joiningOrigin|] to |perBiddingUrlGenerator|.
+        1. [=map/Set=] |perBuyerGenerator|[(|signalsUrl|, |slotSizeQueryParam|)] to
+          |perSignalsUrlGenerator|.
       1. Otherwise:
-        1. Let |perSlotSizeQueryParam| be |perBuyerGenerator|[|signalsUrl|].
-        1. If |perSlotSizeQueryParam| does not [=map/contain=] |slotSizeQueryParam|:       
-          1. Let |perSignalsUrlGenerator| be a new [=per signals url bid generator=].
-          1. [=map/Set=] |perSignalsUrlGenerator|[|joiningOrigin|] to « |ig| ».
-          1. [=map/Set=] |perSlotSizeQueryParam|[|slotSizeQueryParam|] to |perSignalsUrlGenerator|.
+        1. Let |perSignalsUrlGenerator| be |perBuyerGenerator|[(|signalsUrl|, |slotSizeQueryParam|)].
+        1. If |perSignalsUrlGenerator| does not [=map/contain=] |joiningOrigin|:
+          1. Let |perBiddingUrlGenerator| be a new [=per bidding url bid generator=].
+          1. [=map/Set=] |perBiddingUrlGenerator|[|biddingUrl|] to « |ig| ».
+          1. [=map/Set=] |perSignalsUrlGenerator|[|joiningOrigin|] to |perBiddingUrlGenerator|.
         1. Otherwise:
-          1. Let |perSignalsUrlGenerator| be |perSlotSizeQueryParam|[|slotSizeQueryParam|].
-          1. If |perSignalsUrlGenerator| does not [=map/contain=] |joiningOrigin|:
-            1. Let |perBiddingUrlGenerator| be a new [=per bidding url bid generator=].
-            1. [=map/Set=] |perBiddingUrlGenerator|[|biddingUrl|] to « |ig| ».
-            1. [=map/Set=] |perSignalsUrlGenerator|[|joiningOrigin|] to |perBiddingUrlGenerator|.
-          1. Otherwise:
-            1. Let |perBiddingUrlGenerator| be |perSignalsUrlGenerator|[|joiningOrigin|].
-            1. [=list/Append=] |ig| to ||perBiddingUrlGenerator||[|biddingUrl|].      
+          1. Let |perBiddingUrlGenerator| be |perSignalsUrlGenerator|[|joiningOrigin|].
+          1. [=list/Append=] |ig| to ||perBiddingUrlGenerator||[|biddingUrl|].      
 1. Return « |bidGenerators|, |negativeTargetInfo| ».
 
 </div>

From fab05c8e4d3105f119c57a101900cba140c5e818 Mon Sep 17 00:00:00 2001
From: Qingxin Wu <qingxinwu@google.com>
Date: Thu, 15 Feb 2024 00:54:31 -0500
Subject: [PATCH 5/7] fix merge

---
 spec.bs | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/spec.bs b/spec.bs
index 0661f259c..59fd9f946 100644
--- a/spec.bs
+++ b/spec.bs
@@ -4902,9 +4902,11 @@ To <dfn>look up per-buyer currency</dfn> given an [=auction config=] |auctionCon
 
 <h3 id=bid-generators>Bid generator</h3>
 
-A <dfn>per buyer bid generator</dfn> is an [=ordered map=] whose [=map/keys=] are [=URLs=]
-representing [=interest group/trusted bidding signals urls=], and whose [=map/values=] are
-[=per signals url bid generators=].
+A <dfn>per buyer bid generator</dfn> is an [=ordered map=] whose
+  * [=map/keys=] are tuples consisting of [=URLs=] representing 
+    [=interest group/trusted bidding signals urls=], and [=strings=] representing ad slot size query
+    params;
+  * [=map/values=] are [=per signals url bid generators=].
 
 A <dfn>per signals url bid generator</dfn> is an [=ordered map=] whose [=map/keys=] are [=origins=]
 representing [=interest group/joining origins=], and whose [=map/values=] are

From 2c560eb81c4d559e2443bc1ef0aca3af71551805 Mon Sep 17 00:00:00 2001
From: Qingxin Wu <qingxinwu@google.com>
Date: Mon, 19 Feb 2024 18:22:33 -0500
Subject: [PATCH 6/7] work in progress. not working yet

---
 spec.bs | 100 ++++++++++++++++++++++++++++++++------------------------
 1 file changed, 58 insertions(+), 42 deletions(-)

diff --git a/spec.bs b/spec.bs
index 59fd9f946..a78daffe2 100644
--- a/spec.bs
+++ b/spec.bs
@@ -1255,24 +1255,23 @@ To <dfn>validate and convert auction ad config</dfn> given an {{AuctionAdConfig}
 
 </div>
 
-<div algorithm>
+<div algorithm="build bid generators map">
 
 To <dfn>build bid generators map</dfn> given an [=auction config=] |auctionConfig|:
 
   <div class="note"> This algorithm builds an [=ordered map=] with:
     * key: An [=origin=], which is a buyer of [=auction config/interest group buyers=]
     * value: A [=per buyer bid generator=], which is an [=ordered map=] with
-      * key: A tuple consisting of a [=URL=], which is [=interest group/trusted bidding signals url=],
-        and a [=string=], which is the ad slot size query param.
-      * value: A [=per signals url bid generator=], which is an [=ordered map=] with
-        * key: An [=origin=], which is [=interest group/joining origin=]
-        * value: A [=per bidding url bid generator=], which is an [=ordered map=] with
-          * key: A [=URL=], which is [=interest group/bidding url=]
-          * value: A [=list=] of [=interest groups=]
+      * key: A [=script runner key=], which is ...
+      * value: A [=list=] of [=tuples=]
+        ([=interest groups=], [=tuples=] ({{double}} for priority, [=origin=] for
+         [=interest group/joining origin=], [=string=] for [=interest group/execution mode=]).
   </div>
 1. Let |bidGenerators| be a new [=ordered map=] whose [=map/keys=] are [=origins=] and whose
   [=map/values=] are [=per buyer bid generators=].
 1. Let |negativeTargetInfo| be a new [=negative target info=].
+1. Let |igs| to be a [=list=] of [=tuples=] ([=interest groups=], [=tuples=] ({{double}} for priority,
+  [=origin=] for [=interest group/joining origin=], [=string=] for [=interest group/execution mode=]).
 1. [=list/For each=] |buyer| in |auctionConfig|'s [=auction config/interest group buyers=]:
   1. [=list/For each=] |ig| of the [=user agent=]'s [=interest group set=] whose
     [=interest group/owner=] is |buyer|:
@@ -1284,6 +1283,39 @@ To <dfn>build bid generators map</dfn> given an [=auction config=] |auctionConfi
     1. [=iteration/Continue=] if any of the following conditions hold:
       * |biddingUrl| is null;
       * |ig|'s [=interest group/ads=] is null, or [=list/is empty=].
+    1. [=list/Append=] ([|ig|, (|ig|'s [=interest group/priority=], |ig|'s
+      [=interest group/joining origin=], |ig|'s [=interest group/execution mode=])) to |igs|.
+
+      Issue: TODO: Calculate priority from [=interest group/priority vector=]
+      (<a href="https://github.com/WICG/turtledove/issues/1041">WICG/turtledove#1041</a>).
+
+  1. <dfn>Apply interest groups limits to prioritized list</dfn>:
+    1. Let |buyerGroupLimit| be |auctionConfig|'s [=auction config/all buyers group limit=].
+    1. Let |perBuyerGroupLimits| be |auctionConfig|'s [=auction config/per buyer group limits=].
+    1. If |perBuyerGroupLimits| is not null and |perBuyerGroupLimits|[|buyer|] [=map/exists=], then
+      set |buyerGroupLimit| to |perBuyerGroupLimits|[|buyer|].
+    1. If |igs|'s [=list/size=] &lt; |buyerGroupLimit|, then set |buyerGroupLimit| to |igs|'s
+      [=list/size=].
+    1. If |perBuyerGroupLimits| is 0, then return « |bidGenerators|, |negativeTargetInfo| ».
+    1. [=list/Sort in descending order|Sort=] |igs| in descending order, with |a| being less than
+      |b| if |a|[1] is less than |b|[1].
+    1. Let |igsToKeep| be a new [=list=] of [=interest groups=].
+    1. Let |igsToKeepWithLowestPriority| be a new [=list=] of [=interest groups=].
+    1. Let |minPriority| be |igs|[|buyerGroupLimit|-1][1][0].
+    1. [=list/For each=] |igWithInfo| of |igs|:
+      1. If |igWithInfo|[1][0] &gt; |minPriority|, then [=list/append=] |igWithInfo| to |igsToKeep|.
+      1. If |igWithInfo|[1][0] &eq; |minPriority|, then [=list/append=] |igWithInfo| to
+        |igsToKeepWithLowestPriority|.
+      1. If |igWithInfo|[1][0] &lt; |minPriority|, then [=iteration/break=];
+    1. Randomize the order of [=list/items=] of |igsToKeepWithLowestPriority|.
+    1. [=map/Remove=] all except the first `|buyerGroupLimit| minus |igsToKeep|'s [=list/size=]`
+      [=list/items=] from |igsToKeepWithLowestPriority|.
+    1. [=list/Sort in descending order|Sort=] |igsToKeepWithLowestPriority| in descending order,
+      with |a| being less than |b| if |a|[1] is less than |b|[1], to restore the original grouping.
+    1. [=list/Extend=] |igsToKeep| with |igsToKeepWithLowestPriority|.
+
+  1. [=list/For each=] |igWithInfo| of |igsToKeep|:
+    1. Let |ig| be |igWithInfo|[0].
     1. Let |signalsUrl| be |ig|'s [=interest group/trusted bidding signals url=].
     1. Let |slotSizeQueryParam| be the result of [=calculating the ad slot size query param=]
       given |ig| and |auctionConfig|.
@@ -1442,7 +1474,6 @@ To <dfn>generate and score bids</dfn> given an [=auction config=] |auctionConfig
   then return failure.
 1. Let |allBuyersExperimentGroupId| be |auctionConfig|'s
   [=auction config/all buyer experiment group id=].
-1. Let |allBuyersGroupLimit| be |auctionConfig|'s [=auction config/all buyers group limit=].
 1. Let |auctionSignals| be |auctionConfig|'s [=auction config/auction signals=].
 1. Let |directFromSellerSignals| be the result of running [=get direct from seller signals=] given
   |auctionConfig|'s [=auction config/seller=], |auctionConfig|'s
@@ -1486,24 +1517,6 @@ To <dfn>generate and score bids</dfn> given an [=auction config=] |auctionConfig
     [=auction config/per buyer experiment group ids=].
   1. If |perBuyerExperimentGroupIds| is not null and |perBuyerExperimentGroupIds|[|buyer|]
     [=map/exists=], then set |buyerExperimentGroupId| to |perBuyerExperimentGroupIds|[|buyer|].
-  1. <dfn>Apply interest groups limits to prioritized list</dfn>:
-    1. Let |buyerGroupLimit| be |allBuyersGroupLimit|.
-    1. Let |perBuyerGroupLimits| be |auctionConfig|'s
-      [=auction config/per buyer group limits=].
-    1. If |perBuyerGroupLimits| is not null and |perBuyerGroupLimits|[|buyer|] [=map/exists=], then
-      set |buyerGroupLimit| to |perBuyerGroupLimits|[|buyer|].
-    1. Let |igs| be a new [=list=] of [=interest groups=].
-    1. [=map/For each=] signalsUrl → |perSlotSizeQueryParam| of |perBuyerGenerator|:
-      1. [=map/For each=] slotSizeQueryParam → |perSignalsUrlGenerator| of |perSlotSizeQueryParam|:
-        1. [=map/For each=] joiningOrigin → |groups| of |perSignalsUrlGenerator|:
-          1. [=list/Extend=] |igs| with |groups|.
-    1. [=list/Sort in descending order=] |igs|, with |a| being less than |b| if |a|'s
-      [=interest group/priority=] is less than |b|'s [=interest group/priority=].
-    1. [=list/Remove=] the first |buyerGroupLimit| items from |igs|.
-    1. [=map/For each=] signalsUrl → |perSlotSizeQueryParam| of |perBuyerGenerator|:
-      1. [=map/For each=] slotSizeQueryParam → |perSignalsUrlGenerator| of |perSlotSizeQueryParam|:
-        1. [=map/For each=] joiningOrigin → |groups| of |perSignalsUrlGenerator|:
-          1. [=list/Remove=] from |groups| any [=interest group=] [=list/contained=] in |igs|.
   1. Let |perBuyerSignals| be null.
   1. If |auctionConfig|'s [=auction config/per buyer signals=] is not null and
     [=auction config/per buyer signals=][|buyer|] [=map/exists=], then set |perBuyerSignals| to
@@ -4902,21 +4915,24 @@ To <dfn>look up per-buyer currency</dfn> given an [=auction config=] |auctionCon
 
 <h3 id=bid-generators>Bid generator</h3>
 
-A <dfn>per buyer bid generator</dfn> is an [=ordered map=] whose
-  * [=map/keys=] are tuples consisting of [=URLs=] representing 
-    [=interest group/trusted bidding signals urls=], and [=strings=] representing ad slot size query
-    params;
-  * [=map/values=] are [=per signals url bid generators=].
-
-A <dfn>per signals url bid generator</dfn> is an [=ordered map=] whose [=map/keys=] are [=origins=]
-representing [=interest group/joining origins=], and whose [=map/values=] are
-[=per bidding url bid generators=].
-
-
-
-A <dfn>per bidding url bid generator</dfn> is an [=ordered map=] whose [=map/keys=] are [=URLs=]
-representing [=interest group/bidding urls=], and whose [=map/values=] are [=lists=] of
-[=interest groups=].
+A <dfn>per buyer bid generator</dfn> is an [=ordered map=] whose [=map/keys=] are
+[=script runner keys=], and [=map/values=] are [=lists=] of [=tuples=]
+        ([=interest groups=], [=tuples=] ({{double}} for priority, [=origin=] for
+         [=interest group/joining origin=], [=string=] for [=interest group/execution mode=]).
+
+a <dfn>script runner key</dfn> is a [=struct=] that contains enough information to uniquely
+identify a [=script runner=]. It has following [=struct/items=]:
+
+<dl dfn-for="script runner key">
+  : <dfn>script url</dfn>
+  :: A [=URL=]. The URL to fetch the JavaScript from.
+  : <dfn>signals url</dfn>
+  :: Null or a [=URL=]. The URL to fetch the trusted bidding/scoring signals from.
+  : <dfn>experiment group id</dfn>
+  :: Null or a {{unsigned short}}.
+  : <dfn>trusted bidding signals slot size param</dfn>
+  :: A [=string=].
+</dl>
 
 A <dfn>generated bid</dfn> is a bid that needs to be scored by the seller. The bid is either the
 result of [=evaluating a bidding script=], or an [=additional bid=] provided by the

From a36e6f9741fb616ddde7d3820daa90dc6c46621d Mon Sep 17 00:00:00 2001
From: Qingxin Wu <qingxinwu@google.com>
Date: Tue, 5 Mar 2024 11:46:41 -0500
Subject: [PATCH 7/7] in-progress, not working yet.

---
 spec.bs | 55 ++++++++++++++++++++++---------------------------------
 1 file changed, 22 insertions(+), 33 deletions(-)

diff --git a/spec.bs b/spec.bs
index cd0b87206..8e409f28f 100644
--- a/spec.bs
+++ b/spec.bs
@@ -1265,18 +1265,19 @@ To <dfn>build bid generators map</dfn> given an [=auction config=] |auctionConfi
 
   <div class="note"> This algorithm builds an [=ordered map=] with:
     * key: An [=origin=], which is a buyer of [=auction config/interest group buyers=]
-    * value: A [=per buyer bid generator=], which is an [=ordered map=] with
-      * key: A [=script runner key=], which is ...
-      * value: A [=list=] of [=tuples=]
+    * value: A [=list=] of [=tuples=]
         ([=interest groups=], [=tuples=] ({{double}} for priority, [=origin=] for
          [=interest group/joining origin=], [=string=] for [=interest group/execution mode=]).
   </div>
 1. Let |bidGenerators| be a new [=ordered map=] whose [=map/keys=] are [=origins=] and whose
-  [=map/values=] are [=per buyer bid generators=].
+  [=map/values=] are [=lists=] of [=tuples=]
+  ([=interest groups=], [=tuples=] ({{double}} for priority, [=origin=] for
+  [=interest group/joining origin=], [=string=] for [=interest group/execution mode=]).
 1. Let |negativeTargetInfo| be a new [=negative target info=].
-1. Let |igs| to be a [=list=] of [=tuples=] ([=interest groups=], [=tuples=] ({{double}} for priority,
-  [=origin=] for [=interest group/joining origin=], [=string=] for [=interest group/execution mode=]).
 1. [=list/For each=] |buyer| in |auctionConfig|'s [=auction config/interest group buyers=]:
+  1. Let |igs| be a [=list=] of [=tuples=] ([=interest groups=], [=tuples=] ({{double}} for
+    priority, [=origin=] for [=interest group/joining origin=], [=string=] for
+    [=interest group/execution mode=]).
   1. [=list/For each=] |ig| of the [=user agent=]'s [=interest group set=] whose
     [=interest group/owner=] is |buyer|:
     1. Let |igName| be |ig|'s [=interest group/name=].
@@ -1324,33 +1325,21 @@ To <dfn>build bid generators map</dfn> given an [=auction config=] |auctionConfi
     1. Let |slotSizeQueryParam| be the result of [=calculating the ad slot size query param=]
       given |ig| and |auctionConfig|.
     1. Let |joiningOrigin| be |ig|'s [=interest group/joining origin=].
-    1. If |bidGenerators| does not [=map/contain=] |buyer|:
-      1. Let |perBuyerGenerator| be a new [=per buyer bid generator=].
-      1. Let |perSignalsUrlGenerator| be a new [=per signals url bid generator=].
-      1. Let |perBiddingUrlGenerator| be a new [=per bidding url bid generator=].
-      1. [=map/Set=] |perBiddingUrlGenerator|[|biddingUrl|] to « |ig| ».
-      1. [=map/Set=] |perSignalsUrlGenerator|[|joiningOrigin|] to |perBiddingUrlGenerator|.
-      1. [=map/Set=] |perBuyerGenerator|[(|signalsUrl|, |slotSizeQueryParam|)] to
-        |perSignalsUrlGenerator|.
-      1. [=map/Set=] |bidGenerators|[|buyer|] to |perBuyerGenerator|.
-    1. Otherwise:
-      1. Let |perBuyerGenerator| be |bidGenerators|[|buyer|].
-      1. If |perBuyerGenerator| does not [=map/contain=] (|signalsUrl|, |slotSizeQueryParam|):
-        1. Let |perSignalsUrlGenerator| be a new [=per signals url bid generator=].
-        1. Let |perBiddingUrlGenerator| be a new [=per bidding url bid generator=].
-        1. [=map/Set=] |perBiddingUrlGenerator|[|biddingUrl|] to « |ig| ».
-        1. [=map/Set=] |perSignalsUrlGenerator|[|joiningOrigin|] to |perBiddingUrlGenerator|.
-        1. [=map/Set=] |perBuyerGenerator|[(|signalsUrl|, |slotSizeQueryParam|)] to
-          |perSignalsUrlGenerator|.
-      1. Otherwise:
-        1. Let |perSignalsUrlGenerator| be |perBuyerGenerator|[(|signalsUrl|, |slotSizeQueryParam|)].
-        1. If |perSignalsUrlGenerator| does not [=map/contain=] |joiningOrigin|:
-          1. Let |perBiddingUrlGenerator| be a new [=per bidding url bid generator=].
-          1. [=map/Set=] |perBiddingUrlGenerator|[|biddingUrl|] to « |ig| ».
-          1. [=map/Set=] |perSignalsUrlGenerator|[|joiningOrigin|] to |perBiddingUrlGenerator|.
-        1. Otherwise:
-          1. Let |perBiddingUrlGenerator| be |perSignalsUrlGenerator|[|joiningOrigin|].
-          1. [=list/Append=] |ig| to ||perBiddingUrlGenerator||[|biddingUrl|].      
+    1. Let |key| be [=script runner key=] with the following [=struct/items=]:
+      : xxx
+      :: xxxx
+      : xxx
+      :: xxxx
+      : xxx
+      :: xxxx
+      : xxx
+      :: xxxx
+      : xxx
+      :: xxxx
+      : xxx
+      :: xxxx      
+  1. [=map/Set=] |bidGenerators|[|buyer|] to |sortedAndGroupedIgs|.
+
 1. Return « |bidGenerators|, |negativeTargetInfo| ».
 
 </div>