An MRI of a model doing Salesforce work

Forcebench grades what models write. This study looks inside one while it works: Qwen3.8 27B, an open-weights model with 64 layers, read layer by layer as it writes Apex, weighs up languages and reads Forcebench’s own tasks. The tools are the ones explained on Interpretability 101, and the code and every number are on GitHub.

How it was looked at

The model is Qwen3.8 27B in MLX 8-bit, the same weights Forcebench grades as qwen3.8-27b-mlx-8bit. It ran locally with its internals exposed: after each of its 64 layers, the activations (the residual stream) can be read, swapped for another run’s or nudged in a direction. No new answers were graded: study 3 uses the grades Forcebench already has for this model. Reasoning was off unless a section says otherwise.

Does it think in Java while writing Apex?

Apex looks like Java, and there is far more Java to learn from, so a model could plausibly draft in Java and translate. To test that, 64 short coding requests were each written three ways: asking for Apex, asking for Java, and naming no language. Each stops at a point where the two languages part ways, such as System. before debug( or out.println(, across 10 kinds of idiom. At that point the logit lens reads, at every layer, which language’s next token is ahead.

Which language’s next token is ahead, layer by layer
Median over 64 pairs of log P(Apex token) − log P(Java token), from the logit lens at each layer; above zero the Apex token is ahead. Shaded: 95% intervals.
  • Asked for Apex
  • Asked for Java
  • No language named
Which language’s next token is ahead, layer by layerMedian log-odds of the Apex token over the Java token at each of 64 layers, over 64 pairs, for three requests.−16−12−8−4048120816243240485663LayerApex token ahead (log-odds)Asked for ApexNo language namedAsked for Java
Show the numbers
LayerAsked for ApexAsked for JavaNo language named95% interval, asked for Apex
0+0.8+0.8+0.80.6 to 1.5
1+0.8+0.8+0.90.5 to 1.2
2+0.8+0.6+0.70.6 to 1.1
3+0.8+0.5+0.60.4 to 1.1
4+0.8+0.8+0.70.5 to 1.0
5+0.8+0.7+0.70.5 to 1.1
6+0.7+0.6+0.50.4 to 1.0
7+0.6+0.5+0.60.4 to 0.8
8+0.7+0.6+0.7−0.0 to 0.8
9+0.4+0.6+0.7−0.4 to 0.8
10+0.3+0.6+0.7−0.6 to 0.9
11+0.3+0.5+0.6−0.5 to 0.7
12+0.3+0.5+0.6−0.3 to 0.4
13+0.1+0.5+0.5−0.4 to 0.5
14+0.1+0.4+0.2−0.3 to 0.6
15+0.1+0.2+0.0−0.3 to 0.6
16+0.1+0.4+0.1−0.4 to 0.4
17−0.1+0.1+0.1−0.5 to 0.2
18+0.1+0.1+0.1−0.2 to 0.2
19−0.2−0.2−0.1−0.7 to 0.2
20−0.0−0.3−0.3−0.7 to 0.2
21−0.2−0.1−0.2−0.4 to 0.5
22+0.1−0.2−0.2−0.2 to 0.8
23+0.2−0.3−0.2−0.7 to 0.7
24−0.3−0.6−0.6−0.6 to 0.2
25−0.2−0.6−0.7−0.6 to 0.0
26−0.2−0.7−0.8−0.9 to 0.3
27−0.2−0.6−0.8−0.7 to 0.6
28−0.1−0.7−0.7−0.6 to 0.8
29−0.2−0.7−0.9−0.4 to 0.6
30−0.1−0.6−0.8−0.5 to 0.5
31−0.1−0.6−0.7−0.5 to 0.5
32+0.1−0.8−0.8−0.3 to 0.4
33+0.0−0.7−0.6−0.8 to 0.7
34+0.0−0.6−0.7−0.5 to 0.7
35+0.0−0.7−0.8−0.6 to 0.3
36+0.2−0.9−1.0−1.3 to 0.9
37−0.1−1.3−1.1−0.9 to 1.0
38−0.4−1.0−1.1−1.3 to 0.7
39−0.2−0.9−1.0−1.4 to 0.8
40+0.1−1.2−0.9−0.9 to 0.6
41+0.1−0.8−0.9−1.0 to 0.8
42+0.1−1.0−0.8−0.8 to 0.9
43+0.0−1.0−0.9−0.8 to 0.4
44−0.4−0.9−1.4−1.0 to 0.4
45−0.8−1.5−1.8−1.4 to 0.3
46−1.1−1.8−2.0−1.7 to 0.2
47−0.6−1.6−1.9−1.2 to 0.3
48−0.3−1.5−1.6−0.9 to 0.1
49−0.4−1.7−1.5−1.0 to −0.1
50−1.4−2.7−3.2−2.3 to −0.6
51−2.1−3.1−3.2−3.1 to −1.4
52−1.9−5.1−4.8−3.1 to −1.7
53−2.5−5.6−5.8−3.8 to −2.2
54−2.0−6.7−6.5−2.9 to −1.4
55−2.0−7.2−7.3−3.5 to −1.3
56−1.2−9.4−8.8−1.9 to 0.5
57+1.0−11.9−10.9−0.2 to 2.3
58+4.5−12.2−11.33.5 to 5.9
59+5.8−12.2−11.04.9 to 6.7
60+5.3−12.4−11.94.6 to 6.3
61+6.7−11.9−11.05.9 to 8.4
62+9.2−11.6−10.67.9 to 9.9
63+8.9−9.6−7.26.7 to 10.1

Asked for Apex, the model ends with the Apex token ahead in 60 of 64 pairs. On the way there, from layer 49 to 55, the Java token is ahead, most at layer 53 (−2.5), where it leads in 75% of pairs. Apex takes over at layer 57 and finishes far ahead.

That dip is not the model translating from Java as such. Asked for no language at all, it reads like the Java request to the end, and it picks the Java form in 92% of pairs: Java is its default for this kind of code. Up to layer 50, the Apex request follows that default closely, pair by pair: a correlation of at least 0.81 from layer 24, and never more than 0.8 log-odds apart in the median. From layer 49 the default swings hard towards the Java token, and the Apex correction follows a few layers behind; in between, Java is ahead. Where the default is Apex’s own form, string equality with == (5 pairs), the middle layers favour == even when asked for Java. So the middle of the network drafts the most likely code with little regard for the language asked for, and the language is applied late.

The model does know the language early. A linear probe on the same position tells the Apex request from the Java one with at least 96% accuracy from layer 1 on, on pairs it wasn’t trained on. It just doesn’t use that for the next token until about layer 50.

Knowing the language, and using it
A probe’s held-out accuracy at telling the Apex request from the Java one, against the share of pairs where the Apex token is ahead when asked for Apex. Shaded: 95% interval.
  • Probe reads the language
  • Apex token ahead
Knowing the language, and using itHeld-out accuracy of a linear probe for the requested language at each layer, and the share of pairs where the Apex token is ahead in the Apex request.0%25%50%75%100%0816243240485663LayerShareProbe reads the languageApex token ahead
Show the numbers
LayerProbe accuracyApex token ahead95% interval
090%69%56–80%
196%69%56–80%
298%66%53–77%
399%69%56–80%
4100%69%56–80%
598%69%56–80%
698%69%56–80%
7100%64%52–75%
899%61%48–72%
998%55%42–67%
1098%55%42–67%
1198%55%42–67%
1298%55%42–67%
1399%53%41–66%
1498%53%42–66%
1598%53%42–66%
16100%56%44–69%
17100%44%31–56%
18100%58%45–70%
19100%48%36–61%
20100%48%36–61%
21100%44%31–56%
22100%56%45–69%
23100%53%41–66%
24100%45%33–58%
25100%41%30–53%
26100%47%34–59%
27100%47%34–59%
28100%47%34–59%
29100%45%33–58%
30100%48%36–61%
31100%48%36–61%
32100%52%41–64%
33100%50%39–63%
34100%50%38–63%
35100%52%39–64%
3698%53%41–66%
3799%48%36–61%
3898%42%30–55%
3998%48%36–61%
4098%52%39–64%
4198%50%38–63%
4298%53%41–66%
4398%50%38–61%
4498%42%30–55%
4598%44%33–56%
4698%42%30–55%
4798%41%28–53%
4899%42%30–55%
4999%36%25–48%
50100%28%17–39%
51100%28%17–39%
5299%28%17–39%
5399%25%16–36%
5499%20%11–31%
5599%23%13–34%
5699%42%30–55%
5799%59%47–72%
5899%80%70–89%
5998%80%70–89%
6098%84%75–92%
6199%86%77–94%
6299%89%81–95%
6398%94%88–98%

Patching finds where the switch happens. Writing the Java request’s activations into the Apex request’s run at one layer, at the last position only, moves 5% of the answer at layer 32 and 35% at layer 48, then 76% at layer 52 and 89% at layer 56. Before about layer 50 the language is still being read in from the request by later layers; between layers 50 and 54 the decision settles at the position that writes the next token. The other way round, Apex into Java, the last position moves 8% at layer 48 and 80% at layer 52. Swapping all of the code that way gives uneven results in the middle layers, with wide intervals, and isn’t read into here.

How much of the decision moves when one layer is swapped
The Java request’s activations written into the Apex request’s run at one layer, at the last position or over all of the code. 0% leaves the Apex answer, 100% gives the Java one. Median over pairs, with 95% intervals.
  • All of the code
  • Last position only
How much of the decision moves when one layer is swappedMedian share of the gap between the Apex and Java answers that moves when the Java request’s activations replace the Apex request’s at one layer.0%25%50%75%100%0816243240485663Layer swappedShare of the answer movedAll of the codeLast position only
Show the numbers
LayerLast position only95% intervalAll of the code95% interval
00%0–0%0%0–0%
41%0–2%2%1–3%
82%1–3%5%3–8%
122%1–4%7%4–10%
162%1–5%8%5–12%
203%0–7%22%12–34%
245%1–8%19%14–29%
285%2–13%19%13–25%
325%3–12%19%15–28%
3616%10–29%54%48–58%
4022%14–36%61%55–66%
4429%18–49%67%61–71%
4835%25–54%70%64–74%
5054%41–71%79%75–84%
5276%63–86%93%91–94%
5485%77–88%94%93–95%
5689%85–92%97%95–98%
5892%88–95%99%98–99%
6093%89–96%99%98–100%
6298%97–100%99%99–100%
63100%100–100%100%100–100%

Steering checks that this is one direction the model uses. The average difference between the Apex and Java requests at layer 50, at the last position, works like a switch. Taken away from the Apex request, it cuts the pairs where the Apex form wins from 60 of 64 to 21; a random direction of the same length leaves 59. Added to the Java request, it gives the Apex form in 15 of 64 pairs, up from 3 (random: 4). Each test leaves the pair it is tested on out of the direction.

Steering at layer 50: pairs where the Apex form wins, of 64
The average difference between the Apex and Java requests, taken away from or added to the last position at one layer, at twice its size, against a random direction of the same length.
Asked for Apex no steering
60 of 64
Asked for Apex a random direction of the same length
59 of 64
Asked for Apex the Apex direction taken away
21 of 64
Asked for Java no steering
3 of 64
Asked for Java a random direction of the same length
4 of 64
Asked for Java the Apex direction added
15 of 64
    Show the numbers
    Asked forSteeringPairs where the Apex form wins, of 64
    Asked for Apexno steering60
    Asked for Apexa random direction of the same length59
    Asked for Apexthe Apex direction taken away21
    Asked for Javano steering3
    Asked for Javaa random direction of the same length4
    Asked for Javathe Apex direction added15

    That direction, added or taken away at every position at layer 50, was then tried on 12 Apex requests the model answered in full: tests, utility methods and a trigger handler. The replies were searched for things an Apex compiler rejects or only Java writes, such as System.out, JUnit’s @Test, int, double-quoted strings, ArrayList and import. Unsteered, there were 0. With the direction taken away, the model answered in Java: 10 of 12 replies opened a Java code block, and all 12 had Java-isms, 98 in all. A random direction of the same length left 0.

    Steering at layer 50Java-isms foundReplies with any, of 12Replies in a Java code block
    No steering000
    Apex direction added000
    Apex direction taken away981210
    Random direction000

    With the direction taken away, 53 of the 98 are double-quoted strings and 19 are JUnit’s @Test.

    The opening lines of one reply, to “Write an Apex unit test for a DiscountCalculator.apply(Decimal amount) method that gives 10% off amounts over 1,000. Reply with just the code.”:

    Unsteered
    @isTest
    private class DiscountCalculatorTest {
        @isTest
        static void testApplyDiscountForAmountOver1000() {
            Decimal amount = 1500.00;
            Decimal expected = 1350.00;
            Decimal actual = DiscountCalculator.apply(amount);
            System.assertEquals(expected, actual, 'Discount should be 10% off for amounts over 1000');
        }
    Apex direction taken away at layer 50
    public class DiscountCalculatorTest {
        @Test
        public void testApplyDiscount() {
            // Test case: Amount over 1000 should get 10% discount
            double amount = 1500.0;
            double expected = amount * 0.9;
            double result = DiscountCalculator.apply(amount);
            assertEquals(expected, result, "Discount should be 10% for amounts over 1000");
    

    Some Java does reach the answers. In 4 of the 6 assertion pairs, the model’s own next token when asked for Apex is a Java library’s (.assertEquals, .assertFalse, .notNull), which Apex’s Assert class doesn’t have. By kind of idiom:

    Kind of idiomPairsJava token ahead at layer 54Final answer, asked for ApexFinal answer, no language named
    Logging (System.debug / System.out.println)863%+11.7 (Apex)−11.6 (Java)
    String quotes (single / double)888%+9.9 (Apex)−12.0 (Java)
    Collections (List / ArrayList)978%+12.1 (Apex)−6.5 (Java)
    Number types (Integer, Decimal / int, BigDecimal)888%+4.1 (Apex)−7.3 (Java)
    Test annotations (@IsTest / @Test)6100%+8.2 (Apex)−10.6 (Java)
    Class modifiers (with sharing / none)6100%+0.8 (Apex)−9.0 (Java)
    Assertions (Assert.areEqual / assertEquals)6100%−0.5 (Java)−1.6 (Java)
    Exceptions (DmlException / SQLException)475%+8.9 (Apex)−3.8 (Java)
    String equality (== / .equals)50%+5.6 (Apex)+0.6 (Apex)
    Queries (inline SOQL / a repository)4100%+10.2 (Apex)−3.0 (Java)

    Final answers are the median log-odds at the last layer: above zero the Apex form wins.

    Does it reach for code when clicks would do?

    Much Salesforce work can be done two ways: with clicks, the platform’s declarative features such as validation rules, formula fields and Flow, or with code, which on Salesforce means Apex. Salesforce’s guidance is to use clicks where they do the job and code where they don’t. A model that has read far more code than configuration might reach for code anyway.

    The study wrote 36 requirements, each with a textbook answer from that guidance: 12 that a standard feature does (a validation rule, a formula or roll-up summary field, an assignment, escalation, duplicate or sharing rule, an approval process), 12 that a flow does, and 12 that need Apex (high volumes, logic across many objects, callouts with retries, a custom endpoint, parsing a file). With reasoning off, the model was asked about each one three ways: how it would build it, in one sentence; the one feature it would use; and, offered both, clicks or code in one word, asked in both orders with the chance of each answer read directly. An answer reaches for code if it names custom code: Apex, a Lightning Web Component, or a flow that calls Apex.

    How often it reaches for code, by the textbook answer
    Nobody named. For the two open questions, the share of requirements whose answer names code; offered both, the average chance it answers “code”. 12, 12 and 12 requirements. Whiskers: 95% intervals.
    • 95% interval
    A standard feature does it how it would build it, in one sentence
    0%, 95% interval 0–0%
    A standard feature does it the one feature it would use
    0%, 95% interval 0–0%
    A standard feature does it offered both: clicks or code, in one word
    3%, 95% interval 2–5%
    A flow does it how it would build it, in one sentence
    8%, 95% interval 0–25%
    A flow does it the one feature it would use
    8%, 95% interval 0–25%
    A flow does it offered both: clicks or code, in one word
    2%, 95% interval 1–3%
    It needs Apex how it would build it, in one sentence
    92%, 95% interval 75–100%
    It needs Apex the one feature it would use
    25%, 95% interval 0–50%
    It needs Apex offered both: clicks or code, in one word
    89%, 95% interval 77–97%
      Show the numbers
      Textbook answerAskedReaches for code95% interval
      A standard feature does ithow it would build it, in one sentence0%0–0%
      A standard feature does itthe one feature it would use0%0–0%
      A standard feature does itoffered both: clicks or code, in one word3%2–5%
      A flow does ithow it would build it, in one sentence8%0–25%
      A flow does itthe one feature it would use8%0–25%
      A flow does itoffered both: clicks or code, in one word2%1–3%
      It needs Apexhow it would build it, in one sentence92%75–100%
      It needs Apexthe one feature it would use25%0–50%
      It needs Apexoffered both: clicks or code, in one word89%77–97%

      Where clicks will do, it almost never reaches for code. Its one-sentence answer names code for 1 of the 24 requirements that a standard feature or a flow can do, and offered both, it gives “code” a 2% chance on average. Where Apex is needed, its one-sentence answer names code for 11 of 12, 3 of them as a flow that calls Apex, which the guidance also allows, and offered both, it gives “code” 89%.

      Asked for a single feature, it names code less often, even where code is needed: 3 of 12 times. For those requirements it most often names Platform Events (4 of 12), which carries events to a flow or to Apex rather than doing the work itself.

      Textbook answerThe feature it named (times)
      A standard feature does itValidation Rule (3), Formula (2), Formula Field (1), Roll-Up Summary (1), Assignment Rules (1), Duplicate Management (1), Approval Process (1), Territory Management (1), Time-Dependent Workflow (1)
      A flow does itFlow (9), Workflow Rule (1), Scheduled Apex (1), Guided Setup (1)
      It needs ApexPlatform Events (4), Flow (2), Duplicate Management (1), Bulk API (1), Sharing Rules (1), Trigger Handler (1), Scheduled Apex (1), Apex REST (1)

      Asked for the one Salesforce feature it would use, with nobody named: its answers as it wrote them.

      Who asks: how often its one-sentence answer reaches for code
      The same question with nobody named, starting “I’m a Salesforce admin.” and starting “I’m a Salesforce developer.”. 12, 12 and 12 requirements. Whiskers: 95% intervals.
      • 95% interval
      A standard feature does it nobody named
      0%, 95% interval 0–0%
      A standard feature does it an admin asks
      0%, 95% interval 0–0%
      A standard feature does it a developer asks
      0%, 95% interval 0–0%
      A flow does it nobody named
      8%, 95% interval 0–25%
      A flow does it an admin asks
      0%, 95% interval 0–0%
      A flow does it a developer asks
      33%, 95% interval 8–59%
      It needs Apex nobody named
      92%, 95% interval 75–100%
      It needs Apex an admin asks
      92%, 95% interval 75–100%
      It needs Apex a developer asks
      100%, 95% interval 100–100%
        Show the numbers
        Textbook answerWho asksReaches for code95% interval
        A standard feature does itnobody named0%0–0%
        A standard feature does itan admin asks0%0–0%
        A standard feature does ita developer asks0%0–0%
        A flow does itnobody named8%0–25%
        A flow does itan admin asks0%0–0%
        A flow does ita developer asks33%8–59%
        It needs Apexnobody named92%75–100%
        It needs Apexan admin asks92%75–100%
        It needs Apexa developer asks100%100–100%

        Who asks moves the open answer, one way. When a developer asks, its one-sentence answer reaches for code for 4 of the 12 requirements a flow can do, against 1 with nobody named and 0 when an admin asks: +33 points against the admin (95% interval +8 to +58), and not clearly different from nobody named (+25, 0 to +50). Requirements a standard feature does don’t move. Offered a straight choice, the asker barely matters: on requirements that need Apex, an admin asking lowers the chance of “code” by 6 points (95% interval 3 to 9), and a developer asking is not clearly different from nobody named (+1, 0 to +2).

        Its answers also show their age. With nobody named, 33% of its one-sentence answers for requirements clicks can do suggest Process Builder or Workflow Rules, often as “a Flow (or Process Builder)”. Salesforce has retired both in favour of Flow.

        Code or clicks, layer by layer
        The logit lens on the one-word choice, nobody named: the median log-odds of “code” over “clicks” after each layer, by the requirement’s textbook answer. Above zero, “code” is ahead. Shaded: 95% intervals.
        • A standard feature does it
        • A flow does it
        • It needs Apex
        Code or clicks, layer by layerMedian log-odds of “code” over “clicks” at each of 64 layers, for the one-word choice, by the requirement’s textbook answer.−12−8−4048120816243240485663Layer“Code” ahead (log-odds)It needs ApexA standard feature does itA flow does it
        Show the numbers
        LayerA standard feature does itA flow does itIt needs Apex95% interval, it needs Apex
        0+0.4+0.4+0.40.4 to 0.4
        1+0.7+0.7+0.70.7 to 0.7
        2+0.9+0.9+0.90.9 to 0.9
        3+0.9+0.9+0.90.9 to 0.9
        4+1.0+1.0+1.11.0 to 1.1
        5+0.8+0.8+0.80.8 to 0.9
        6+0.5+0.5+0.50.5 to 0.5
        7+0.6+0.6+0.60.6 to 0.7
        8+0.7+0.7+0.70.7 to 0.8
        9+1.0+1.0+1.01.0 to 1.1
        10+1.0+1.0+1.01.0 to 1.1
        11+1.1+1.1+1.21.2 to 1.2
        12+1.2+1.2+1.31.2 to 1.3
        13+1.2+1.2+1.31.2 to 1.4
        14+1.1+1.1+1.21.2 to 1.3
        15+0.5+0.6+0.60.5 to 0.6
        16+0.0+0.1+0.10.0 to 0.1
        17−0.4−0.4−0.4−0.4 to −0.4
        18−0.1−0.1−0.0−0.1 to −0.0
        19−0.6−0.6−0.7−0.7 to −0.6
        20+0.1+0.1+0.10.1 to 0.1
        21−0.5−0.5−0.5−0.5 to −0.5
        22−0.3−0.3−0.4−0.5 to −0.4
        23−0.3−0.3−0.4−0.4 to −0.3
        24−0.8−0.7−0.7−0.8 to −0.7
        25−0.9−0.9−0.8−0.9 to −0.7
        26−0.1−0.1−0.0−0.1 to 0.0
        27+0.4+0.4+0.50.4 to 0.6
        28−0.4−0.4−0.4−0.5 to −0.4
        29−1.2−1.3−1.2−1.2 to −1.1
        30−1.2−1.2−1.0−1.1 to −0.9
        31−0.2−0.1−0.1−0.1 to 0.1
        32+0.7+0.7+0.80.7 to 1.0
        33+1.4+1.6+1.81.7 to 1.9
        34+1.9+2.1+2.32.1 to 2.4
        35+2.1+2.2+2.52.3 to 2.5
        36+1.9+2.1+2.42.3 to 2.5
        37+0.4+0.5+0.80.7 to 0.9
        38+0.2+0.3+0.30.3 to 0.4
        39+0.3+0.3+0.30.3 to 0.4
        40+0.4+0.5+0.50.4 to 0.5
        41+0.2+0.3+0.20.1 to 0.3
        42+0.1+0.1+0.0−0.0 to 0.1
        43+0.0+0.1−0.0−0.1 to 0.1
        44−0.2−0.3−0.2−0.3 to −0.1
        45−0.5−0.5−0.5−0.6 to −0.4
        46−0.0−0.1−0.1−0.2 to 0.1
        47−0.0−0.0+0.50.4 to 0.6
        48−0.8−0.7−0.1−0.2 to −0.0
        49−0.9−0.9−0.3−0.4 to −0.2
        50+0.2+0.4+1.21.1 to 1.2
        51+0.3+0.5+1.31.1 to 1.3
        52−0.2+0.2+0.90.8 to 1.1
        53−0.6−0.3+0.30.2 to 0.4
        54−0.5−0.2+0.40.2 to 0.5
        55+0.2−0.5+5.54.5 to 6.6
        56−0.5−1.1+4.53.8 to 5.6
        57−0.9−1.5+4.53.8 to 5.5
        58−2.2−2.7+3.93.2 to 4.7
        59−8.7−9.5+6.55.3 to 8.0
        60−6.1−7.3+8.87.0 to 10.3
        61−7.6−8.7+7.25.3 to 8.8
        62−6.0−7.1+6.24.6 to 7.6
        63−3.9−4.6+3.32.2 to 4.4

        Inside, there is no hidden pull towards code that the model then overrules. Read through the logit lens, the one-word choice looks much the same for all three kinds of requirement up to layer 46, never more than 0.53 log-odds apart (at layer 36): the middle layers’ larger swings are shared by every requirement alike. The one steady difference is a slight lean in the textbook order from layer 30 to 37, with the requirements that need Apex a little nearer code than the flow ones, and those a little nearer than the standard ones. From layer 47 the requirements that need Apex pull ahead, by several log-odds from layer 55, and the others settle firmly on clicks only from layer 58. The choice is made late, like the Apex form in study 1.

        Does it know it’ll fail?

        Forcebench graded this model, in these weights, on the 272 public tasks of benchmark 0.1.0, with reasoning effort low: it passed 78 (29%; the leaderboard’s suite-averaged score is 28%). Each task was shown to it again exactly as in that run, and its activations were read at the end of the prompt, before it had started reasoning. Separately, with reasoning off, it was shown each task with a question added at the end that told it not to solve the task yet: how likely its answer would be to pass, answered with one digit from 0 to 9.

        Each signal is scored by its AUROC: the chance that a task it passed ranks above a task it failed, where 0.5 is a coin toss and 1 is perfect.

        How well each signal tells passes from fails
        AUROC over 272 tasks (78 passed). Probes are scored only on tasks held out of their training. Whiskers: 95% intervals.
        • 95% interval
        • A coin toss (0.5)
        A probe on its activations before it starts, layer 40
        0.81, 95% interval 0.76–0.86
        The suite, difficulty and prompt length no model needed
        0.75, 95% interval 0.69–0.81
        What it says: its chance of passing asked before it starts, 0 to 9
        0.64, 95% interval 0.56–0.71
          Show the numbers
          SignalAUROC95% intervalWithin a suite
          A probe on its activations, before it starts0.810.76–0.860.70
          The suite, difficulty and prompt length0.750.69–0.810.59
          What it says: its chance of passing0.640.56–0.710.61

          Before it has written a word, a linear probe on its activations at layer 40, a layer fixed before looking, tells the tasks it will pass from the ones it will fail with an AUROC of 0.81 (95% interval 0.76–0.86); the best layer, 46, reaches 0.83. With the labels shuffled, the best of all 64 layers gets past 0.62 only one time in twenty.

          Some of that is just the kind of task. The suite, the difficulty and the prompt’s length alone give 0.75. The probe adds to that (+0.07, 95% interval +0.03 to +0.10), and it still reaches 0.70 comparing tasks within the same suite, where the baseline manages 0.59. Reading its own finished answer doesn’t help: a probe at the end of the answer does about as well (0.78 at layer 40).

          Each label is one graded attempt at temperature 1.0, so some passes and fails are luck. As a check, 6 other runs of the same model in AWQ int4, at low, medium and xhigh effort, give each task a pass rate over at least 4 attempts. That pass rate predicts this run’s results with an AUROC of 0.89, about as well as anything could, and a probe trained on that pass rate predicts it with a rank correlation of 0.59 (the suite baseline: 0.40).

          Telling passes from fails, layer by layer
          Out-of-fold AUROC of a linear probe at each layer, at the end of the prompt and at the end of the model’s own answer, with each task’s score averaged over the repeats of the cross-validation, as in the text. Flat lines: the suite, difficulty and length baseline, and what shuffled labels reach. Shaded: 95% intervals.
          • Before it starts
          • After its own answer
          • Suite, difficulty, length
          • Shuffled labels (best layer, 95th percentile)
          Telling passes from fails, layer by layerOut-of-fold AUROC of a linear probe for pass or fail at each layer, at the end of the prompt and after the model's own answer, against the suite baseline and shuffled labels.0.50.60.70.80.90816243240485663LayerAUROCBefore it startsAfter its own answerSuite, difficulty, lengthShuffled labels
          Show the numbers
          LayerBefore it starts95% intervalAfter its own answer95% interval
          00.670.60–0.740.700.62–0.76
          10.750.68–0.810.720.66–0.78
          20.760.70–0.820.730.66–0.80
          30.720.64–0.780.730.66–0.80
          40.710.64–0.780.730.66–0.79
          50.710.65–0.790.740.68–0.81
          60.720.66–0.780.760.70–0.83
          70.730.66–0.790.760.69–0.81
          80.770.70–0.830.780.72–0.83
          90.780.72–0.830.760.70–0.82
          100.790.74–0.850.750.69–0.82
          110.770.70–0.830.740.68–0.81
          120.750.69–0.810.760.70–0.82
          130.780.72–0.830.750.69–0.81
          140.780.72–0.840.750.69–0.81
          150.770.71–0.830.750.69–0.81
          160.770.71–0.820.750.69–0.82
          170.810.75–0.860.760.70–0.82
          180.820.77–0.870.750.68–0.81
          190.810.74–0.860.740.67–0.80
          200.800.75–0.860.740.67–0.80
          210.800.74–0.840.740.68–0.81
          220.790.73–0.840.740.68–0.81
          230.790.73–0.850.750.69–0.81
          240.790.73–0.850.750.68–0.81
          250.790.73–0.840.750.69–0.82
          260.770.70–0.830.760.69–0.82
          270.790.73–0.840.750.69–0.82
          280.770.71–0.830.750.69–0.82
          290.780.72–0.840.760.70–0.82
          300.790.73–0.840.750.69–0.82
          310.790.72–0.840.770.72–0.83
          320.800.75–0.860.760.71–0.82
          330.820.77–0.870.780.72–0.84
          340.830.77–0.870.780.72–0.84
          350.820.76–0.870.780.72–0.83
          360.810.75–0.860.780.73–0.84
          370.810.76–0.860.790.73–0.84
          380.820.77–0.870.760.71–0.82
          390.810.75–0.860.770.70–0.82
          400.810.75–0.860.780.73–0.84
          410.810.75–0.860.770.71–0.83
          420.810.75–0.860.770.72–0.84
          430.810.76–0.860.780.72–0.83
          440.820.76–0.860.790.74–0.85
          450.820.77–0.870.790.73–0.85
          460.830.78–0.880.780.72–0.83
          470.820.77–0.880.800.74–0.86
          480.820.76–0.870.800.74–0.85
          490.820.77–0.870.810.75–0.86
          500.820.77–0.870.810.75–0.86
          510.820.76–0.870.800.75–0.86
          520.810.75–0.860.810.76–0.87
          530.820.76–0.870.810.74–0.86
          540.820.76–0.870.810.75–0.86
          550.810.76–0.870.810.75–0.87
          560.820.77–0.870.810.76–0.86
          570.820.76–0.870.800.75–0.86
          580.820.77–0.870.810.74–0.86
          590.820.76–0.870.800.74–0.86
          600.820.76–0.870.810.76–0.86
          610.820.77–0.870.800.73–0.86
          620.830.77–0.880.800.74–0.85
          630.820.76–0.880.800.74–0.86

          What it says is another matter. Asked how likely its answer is to pass, it gives 7.6 out of 9 on average (7.9 for tasks it went on to pass, 7.5 for ones it failed), against a pass rate of 29%. Its stated confidence separates passes from fails with an AUROC of 0.64, below the suite baseline (−0.12, 95% interval −0.19 to −0.04); the probe beats it by +0.18 (+0.10 to +0.26). Only its most confident sixth of tasks pass noticeably more often.

          Two things tilt that comparison against what it says. It answered with reasoning off, while the graded answers were written with reasoning on. And the probe, like the baseline, was fitted to these grades (scored only on tasks held out of the fit), while its stated confidence was never fitted to anything. So the fair yardstick for the probe is the baseline. What stands either way: the model rates its chances far above how often it passes, and its rating tells passes from fails less well than the kind of task does.

          Tasks passed, by how confident it said it was
          The 272 tasks in six groups of equal size by stated confidence (the expected digit, 0 to 9). The tick marks the pass rate that confidence would mean. Whiskers: 95% intervals.
          • 95% interval
          • The pass rate it implied
          Said 6.1 of 9 46 tasks
          17%, 95% interval 7–30%
          Said 7.2 of 9 46 tasks
          30%, 95% interval 17–43%
          Said 7.7 of 9 45 tasks
          20%, 95% interval 9–33%
          Said 8.0 of 9 45 tasks
          24%, 95% interval 13–38%
          Said 8.3 of 9 45 tasks
          18%, 95% interval 7–29%
          Said 8.7 of 9 45 tasks
          62%, 95% interval 47–76%
            Show the numbers
            Stated confidence (of 9)TasksPassed95% intervalThe pass rate it implied
            6.14617%7–30%68%
            7.24630%17–43%80%
            7.74520%9–33%85%
            8.04524%13–38%89%
            8.34518%7–29%92%
            8.74562%47–76%96%
            SuiteTasksPassedStated chance of passing (0–9)95% interval
            SOQL2055%8.27.9 to 8.5
            Governor limits1947%7.57.1 to 7.9
            Salesforce APIs1844%8.17.8 to 8.3
            Trigger Actions Framework1844%7.16.7 to 7.6
            Permissions1540%7.67.2 to 8.1
            Apex2035%7.97.6 to 8.2
            NPSP1833%7.46.9 to 7.8
            Salesforce CLI2030%8.38.1 to 8.4
            CI/CD1729%8.07.8 to 8.2
            Lightning Web Components1822%8.27.9 to 8.4
            Packaging1822%7.57.3 to 7.7
            Flow1817%7.26.8 to 7.6
            Scratch org definitions186%7.16.8 to 7.4
            Platform docs170%6.35.9 to 6.7
            fflib180%8.07.8 to 8.2

            Graded run: 20260927T004832Z_qwen3.8-27b-mlx-8bit@low. The stated chance is the expected digit from the model’s probabilities for 0 to 9, asked as “Don't solve it yet. How likely is it that your answer would pass the grader's hidden tests? Reply with one digit, 0 (certain to fail) to 9 (certain to pass).”

            What this doesn’t show

            Numbers from the study file generated 22 October 2026. Code, stimuli and per-item results: github.com/forcebench-ai/forcebench-mri.

            Want this for your team?

            Azul Labs runs Forcebench privately for two kinds of Salesforce team.

            Choosing a model to run yourself?

            A private benchmark of your shortlist

            Open-weight models at the quantisations and reasoning efforts you could run on infrastructure you control, tested on your kind of Salesforce work, with a written recommendation.

            • Private tasks written from your work only ever run on local models; they are never sent to an API.
            • Re-testing after any change of model, quantisation or engine is available.
            • Typical turnaround is 2–3 weeks; large models take about a day of machine time per configuration.

            Building or buying a Salesforce AI tool?

            A private evaluation of your tool

            Your tool on held-out tasks it has never seen, against API baselines (models reached through a hosted service) and Local ones (served on Forcebench’s own hardware), with a written report on where it holds up and where it fails.

            • It can start as a smaller pilot, about 30 held-out tasks with your tool against two baselines, before a full evaluation.
            • Repeat evaluations after your releases are available.
            • Typical turnaround is 2–3 weeks.

            Fixed fee, quoted up front. The quote depends on the number of tasks and the number of models, tools or configurations tested.

            Or email leo@azl.au · azl.au