Can anyone explain how these boolean outputs make sense?

I created a workflow that looks like this:

name: CI
on:
  pull_request:

jobs:
  validate:
    runs-on: ubuntu-latest
    outputs:
      IS_DEPLOYER: ${{ steps.isFakeTeamMember.outputs.isTeamMember || github.actor == 'roryabraham' }}
    steps:
      - id: isTeamMember
        uses: tspascoal/get-user-teams-membership@baf2e6adf4c3b897bd65a7e3184305c165aec872
        with:
          GITHUB_TOKEN: ${{ secrets.RORY_TOKEN }}
          username: ${{ github.actor }}
          team: rory-custom-team

      - id: isFakeTeamMember
        uses: tspascoal/get-user-teams-membership@baf2e6adf4c3b897bd65a7e3184305c165aec872
        with:
          GITHUB_TOKEN: ${{ secrets.RORY_TOKEN }}
          username: ${{ github.actor }}
          team: fake-team

      # This correctly outputs `true`
      - run: echo ${{ steps.isTeamMember.outputs.isTeamMember }}

      # This correctly outputs `false`
      - run: echo ${{ steps.isFakeTeamMember.outputs.isTeamMember }}

      # This correctly outputs `true`
      - run: echo ${{ github.actor == 'roryabraham' }}

      # This correctly outputs `false`
      - run: echo ${{ github.actor == 'AndrewGable' }}

      # This outputs `false`, but it should be true!
      - run: echo ${{ steps.isFakeTeamMember.outputs.isTeamMember || github.actor == 'roryabraham' }}

      # This correctly outputs `true`
      - run: echo ${{ github.actor == 'roryabraham' || steps.isFakeTeamMember.outputs.isTeamMember }}

      # This correctly outputs `true`.
      # But it's basically `false || true`, which is the same arrangement of booleans as the incorrect step two above that outputs `false
      - run: echo ${{ github.actor == 'AndrewGable' || steps.i`sTeamMember.outputs.isTeamMember }}

      # This correctly outputs `false`
      - run: echo ${{ github.actor == 'AndrewGable' || steps.isFakeTeamMember.outputs.isTeamMember }}

      # This correctly outputs `true`
      - run: echo ${{ steps.isTeamMember.outputs.isTeamMember || github.actor == 'AndrewGable' }}

This result doesn’t really make any sense to me. Can anyone help explain, or is this a bug?

Figured this out – the problem here was that the output of the JS action was actually a string true or false. I solved this by wrapping every access to that data in fromJSON. Would be great if the GH Actions runner could handle this better and treat the string 'true' as true and the string 'false' as false.

Outputs are always strings. Automatic casting would add magic behavior that is possibly worse than having to account for this.

You don’t necessarily have to use fromJSON by the way, you can also write ${{ steps.foo.outputs.bar == 'true' || 'fallback' }}.

So I have a lot of existing workflows that already use string comparisons. I guess I just never understood exactly when a given variable would be a string and when it would be a boolean. I think I have a better understanding of that now, but I’m also hoping to land on a best-practice where we are less likely to make the same mistake in the future.

I sort of think that consistently using fromJSON to decode outputs will lead to greater clarity about what data needs to be json-decoded and what does not. Haven’t decided for sure yet though.