Scope directories
The three declaration kinds use parallel directory names:
Project-wide declarations remain under the top-level roots and are visible from every supported SQL surface. Inherited and local directories must be below one of these canonical authored roots:
models/tests/unit/tests/scenarios/hooks/sql/functions/sql/audits/sources/
One path example
Consider this model tree:
Inherited declarations compose from every matching ancestor. They do not stop at the nearest declaration directory. Local declarations apply only to resources whose exact parent is the owning path; they are not inherited by descendants.
Declarations never flow upward, sideways into siblings, or out of their canonical authored root. An inaccessible name produces a compile error that distinguishes it from an unknown name.
Names and shadowing
Every public declaration name must remain globally unique within its kind, including names in inherited and local directories. A narrower declaration cannot shadow a project-wide or ancestor declaration. For example, two public macros namedformat_currency conflict even when their owning paths do not overlap.
Macro, constant, and enum namespaces are independent. A macro, constant, and enum may all use the same public name because references identify the kind: @format_currency(), @const("format_currency"), and @enum("format_currency").
Model-private constants and enums declared with an underscore name in MODEL() are different: they belong only to that model. Private names must begin with exactly one _; names beginning with __ are reserved for SQLBuild. They are available in the owner’s query and inline SQL hooks, but not to descendants, tests, scenarios, or any other resource. Their introspection identities include the owner, for example enum:model:stg_orders._state, so private names may repeat across models without shadowing.
Which path resolves SQL
Most SQL resolves declarations from the path where that SQL is authored:
Named hook arguments are still supplied by the model invocation, and hook context such as
@@CTX:destination.qualified still describes that model. The declarations and macros used by the named hook body, however, resolve from the hook definition path. Inline hooks resolve from the model path because that is where their SQL is authored.
Tests and scenarios resolve macros and their directly visible enums and constants from the authored test or scenario path. Each explicit __expected__<model> relationship additionally grants the public enums and constants visible to that expected model. Grants are the deterministic union across all expected models and retain per-model provenance. They do not grant macros or model-private declarations, and filenames never imply a relationship.
This lets expected SQL use the same public domain vocabulary as the model it describes without pretending that the test file lives in the model directory. Path visibility and relationship visibility remain separate facts.
Macro lexical scope
Macro calls written directly in a model, hook, function, audit, source, test, or scenario resolve from that current author’s path. This includes nested argument calls:format_money and net_amount are selected from the scope of the SQL containing this expression.
A macro may return SQL containing further @macro() calls. Those emitted calls resolve from the callee macro’s definition path, not from the original model or other caller. This keeps a macro’s dependencies lexical and lets an inherited macro compose with project-wide declarations and declarations visible beside its own module.
Macro modules must not import other project macro modules in Python. Compose them through tracked @macro() calls instead. SQLBuild rejects inaccessible emitted dependencies and dependency cycles. A project-wide macro therefore cannot depend on an inherited or local macro: its top-level definition has no access to narrower scopes.
The generated manifest exposes each macro’s declaration visibility and owning scope path, and records tracked macro dependencies in the macro node’s dependency metadata.
Usage and placement
SQLBuild records resolved enum, constant, and macro use while expanding the complete project. Comments, quoted text, macro test identity checks, and mocks do not count as runtime use. Generated enum contracts and audits do count because they depend on the enum domain. Every declaration must have a genuine use. Unused project-wide, inherited, local, and model-private declarations fail compilation. Project-wide declarations are an explicit stable API decision. Once used, they remain valid even when all current consumers happen to be close together; SQLBuild does not tell you to narrow them. Narrow declarations must use the closest exact placement required by all consumers:- One owning directory requires the corresponding
_local_.../directory. - Multiple directories in one authored resource root require the inherited
_.../directory at their lowest common ancestor. - Consumers in different authored resource roots require the top-level project-wide root.
Related declarations
sqb scope- inspect visibility, usage, placement, prospective paths, and move impact offline- Python Macros
- Enums and Constants
- Interpolation

