Rails 7 - Accessing joined model generate new query

but once I call states from pipeline it generates new query without
limitation

yeah, that's how joins work, it doesn't do more than that so the docs even don't bother adding more information. If you want to access the states within a single query (really it'll end up being 2 queries) you could use includes instead;
ActiveRecord::QueryMethods#includes;

Specify relationships to be included in the result set. It allows you
to access the address attribute of the User model without firing an
additional query.

and your query would slightly change to;
Pipeline
  .includes(states: :items)
  .where(items: { name: "Lorem" })
  .find('123')

The fact that invoking states on a Pipeline instance makes a new query is because you're invoking an instance method on that object which by definition makes a new query. Active Record won't know that you're referring to something that was already created in the query unless is specified otherwise (by using a custom select clause or similar).

Notice
Pipeline.joins(states: :items).where(items: { name: "Lorem" }) is the same as Pipeline.joins(states: :items).where(states: { items: { name: "Lorem" } }), you don't need to nest items under states.

I have Rails 7 project where I've got tables
Pipelines
class Pipeline < ApplicationRecord
  has_many :states, inverse_of: :pipeline, dependent: :destroy  
end

States
class State < ApplicationRecord
  belongs_to :pipeline, inverse_of: :states 
  has_many :items, inverse_of: :state
end

Items
class Item < ApplicationRecord
  belongs_to :state, inverse_of: :items
end

I wrote a query to limit only specific items on state
@pipeline = Pipeline.joins(states: :items)
                    .where(states: { items: { name: "Lorem" } })
                    .find_by(id: '123')

query:
Pipeline Load (0.7ms)  SELECT "pipelines".* FROM "pipelines" INNER JOIN "states" ON "states"."pipeline_id" = "pipelines"."id" INNER JOIN "items" ON "items"."state_id" = "states"."id" WHERE "items"."name" = $1 AND "pipelines"."id" = $2 LIMIT $3  [["name", "Lotem"], ["id", "123"], ["LIMIT", 1]]

but once I call states from pipeline it generates new query without limitation:
puts @pipeline.states.inspect

State Load (0.4ms)  SELECT "states".* FROM "states" WHERE "states"."pipeline_id" = $1 /* loading for inspect */ LIMIT $2  [["pipeline_id", "123"], ["LIMIT", 11]]

I'm expecting that it should grab states from collection it already has loaded,
am I missing something ?
