Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 29 additions & 18 deletions src/sqlbuild/.agents/skills/sqlbuild/references/docs/cli/scope.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ Online: https://sqlbuild.com/docs/cli/scope/

The command is read-only and offline. It does not connect to a warehouse, require warehouse credentials, or inspect relations. It reads the compiler-owned declaration-scope index and reuses a deterministic cache when the relevant source and configuration fingerprint has not changed. Building a cold cache uses the normal compiler expansion path, which loads the configured adapter and may execute authored Python macros; a warm cache hit reconstructs scope facts without importing macro modules.

Text output is deterministic, bounded, and requires no interactive pager.
Text output is deterministic, bounded, and requires no interactive pager. In a color terminal it
uses the same color conventions as other commands; redirected output and `NO_COLOR` produce plain
text.

## Usage

Expand Down Expand Up @@ -90,6 +92,7 @@ These identities are for inspection. SQL authored inside the owning model contin
| `--page-size N` | Return at most `N` declarations per paged section (default: `100`). |
| `--after CURSOR` | Continue after a declaration identity returned as the previous section's next cursor. |
| `--paths MODE` | Render paths as `relative`, `compact`, or `none`. |
| `--verbose`, `-v` | Show each declaration's scope details and column, and keep empty optional sections. |
| `--json` | Write the canonical versioned JSON report instead of the text tree. |

Filters combine in a fixed order: definition path, kind, glob, then usage. `--dependency-depth`
Expand All @@ -107,11 +110,17 @@ The default text report keeps distinct facts in distinct sections:
- **Scope chain** starts with the resource's owner folder, then shows parent folders and the project
declaration roles. Its compact labels identify the access rule checked at each path.

Ordinary text rows show each declaration's qualified identity, definition location, visibility,
reason for appearing, declaration-role root, and safe type or signature metadata where useful.
`--explain` additionally shows its owning path, narrowest required placement, current consumers,
dependencies, and the consumers affected by a placement mismatch. JSON includes the complete
structured declaration report.
By default, each text row shows a usage marker, the declaration's qualified identity, and its
`file:line` definition. A one-line legend explains the markers. Empty optional sections
(**Relationship grants**, **Nearby unavailable**, **Private retained**, **Relationship retained**,
and **Diagnostics**) are omitted; the core sections are always shown, even when empty.

`--verbose` restores the full rows: kind, visibility, reason for appearing, declaration-role root,
safe type or signature metadata where useful, and the definition column. It also shows empty
optional sections. `--explain` always shows the explained declaration in full, plus its owning
path, narrowest required placement, current consumers, dependencies, and the consumers affected by
a placement mismatch. JSON includes the complete structured declaration report regardless of
`--verbose`.

### See the whole scope at once

Expand Down Expand Up @@ -160,28 +169,30 @@ Scope
Path: models/commerce/orders.sql

Used (1)
└─ ● macro:formatted_order_total [macro; descendant-public; inherited_ancestor; params 1; role models/commerce/macros] models/commerce/macros/orders.py:5:1
└─ ● macro:formatted_order_total models/commerce/macros/orders.py:5

Scope chain
├─ exact-owner-private models/commerce (1)
├─ descendant-public models (0)
└─ project global (2)

Available (1 of 3, 2 collapsed)
└─ ● macro:formatted_order_total [macro; descendant-public; inherited_ancestor; params 1; role models/commerce/macros] models/commerce/macros/orders.py:5:1
└─ ● macro:formatted_order_total models/commerce/macros/orders.py:5
… 2 globals collapsed; run sqb scope model:orders --globals all

Relationship grants (0 of 0)
(none)
● used by this resource ○ not used by this resource --verbose for scope details

Nearby unavailable (0 of 0)
(none)

Diagnostics (0)
(none)
Completeness: complete
```

With `--verbose`, the same `Used` section shows why each declaration is visible:

```console
$ sqb scope model:orders --verbose
Used (1)
└─ ● macro:formatted_order_total [macro; descendant-public; inherited_ancestor; params 1; role models/commerce/macros] models/commerce/macros/orders.py:5:1
```

This answers several questions without opening declaration files: what the model actually uses,
where that declaration came from, which owner and parent folders SQLBuild checks, how many
project-wide declarations are available, and whether the result is complete. The scope-chain count
Expand All @@ -202,9 +213,9 @@ Scope
Path: models/commerce/orders.sql

Used (3)
├─ ● macro:add_tax [macro; project; dependency; params 1; role macros] macros/currency.py:1:1
├─ ● macro:formatted_order_total [macro; descendant-public; inherited_ancestor; params 1; role models/commerce/macros] models/commerce/macros/orders.py:5:1
└─ ● macro:round_money [macro; project; dependency; params 1; role macros] macros/currency.py:5:1
├─ ● macro:add_tax macros/currency.py:1
├─ ● macro:formatted_order_total models/commerce/macros/orders.py:5
└─ ● macro:round_money macros/currency.py:5
```

The remaining report sections follow the `Used` section as usual. The dependency graph comes from
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,30 +75,32 @@ Scope
Path: models/commerce/orders.sql

Used (2)
├─ ● constant:minimum_order_value [constant; descendant-public; inherited_ancestor; type integer; role models/commerce/constants] models/commerce/constants/limits.sql:1:1
└─ ● macro:formatted_order_total [macro; descendant-public; inherited_ancestor; params 1; role models/commerce/macros] models/commerce/macros/orders.py:4:1
├─ ● constant:minimum_order_value models/commerce/constants/limits.sql:1
└─ ● macro:formatted_order_total models/commerce/macros/orders.py:4

Scope chain
├─ exact-owner-private models/commerce (3)
├─ descendant-public models (0)
└─ project global (2)

Available (3 of 5, 2 collapsed)
├─ ● constant:minimum_order_value [constant; descendant-public; inherited_ancestor; type integer; role models/commerce/constants] models/commerce/constants/limits.sql:1:1
├─ ○ enum:order_status [enum; descendant-public; inherited_ancestor; members 4; type VARCHAR; role models/commerce/enums] models/commerce/enums/status.sql:1:1
└─ ● macro:formatted_order_total [macro; descendant-public; inherited_ancestor; params 1; role models/commerce/macros] models/commerce/macros/orders.py:4:1
├─ ● constant:minimum_order_value models/commerce/constants/limits.sql:1
├─ ○ enum:order_status models/commerce/enums/status.sql:1
└─ ● macro:formatted_order_total models/commerce/macros/orders.py:4
… 2 globals collapsed; run sqb scope model:orders --globals all

Diagnostics (0)
(none)
● used by this resource ○ not used by this resource --verbose for scope details

Completeness: complete
```

`●` marks a declaration included in `Used`: a direct usage by default, or a followed declaration
dependency when `--dependency-depth` is nonzero. `○` marks a declaration present in the section but
not in `Used`.

The compact labels translate to ordinary folder rules:
Add `--verbose` to see each declaration's access details, for example
`[enum; descendant-public; inherited_ancestor; members 4; type VARCHAR; role models/commerce/enums]`.
Those labels translate to ordinary folder rules:

| Output label | Meaning in this report |
|--------------|------------------------|
Expand All @@ -122,10 +124,10 @@ declarations. The expanded `Used` section is:
```console
$ sqb scope model:orders --used-only --dependency-depth 1
Used (4)
├─ ● constant:minimum_order_value [constant; descendant-public; inherited_ancestor; type integer; role models/commerce/constants] models/commerce/constants/limits.sql:1:1
├─ ● macro:add_tax [macro; project; dependency; params 1; role macros] macros/currency.py:1:1
├─ ● macro:formatted_order_total [macro; descendant-public; inherited_ancestor; params 1; role models/commerce/macros] models/commerce/macros/orders.py:4:1
└─ ● macro:round_money [macro; project; dependency; params 1; role macros] macros/currency.py:5:1
├─ ● constant:minimum_order_value models/commerce/constants/limits.sql:1
├─ ● macro:add_tax macros/currency.py:1
├─ ● macro:formatted_order_total models/commerce/macros/orders.py:4
└─ ● macro:round_money macros/currency.py:5
```

SQLBuild derives these edges from actual Python calls, including nested calls and calls reached
Expand Down Expand Up @@ -166,6 +168,8 @@ Explanation
Required path: models/commerce
Promotion impact: model:orders

● used by this resource ○ not used by this resource --verbose for scope details

Diagnostics (1)
ERROR S008 models/commerce/macros/orders.py: Declaration 'macro:formatted_order_total' is currently descendant-public at 'models/commerce' (models/commerce/macros/orders.py); required exact-owner-private at 'models/commerce'. Consumers: model:orders. Move it to 'models/commerce/_sqlbuild/_macros/'
Completeness: complete
Expand All @@ -181,8 +185,8 @@ cannot use it. The relevant section is:
```console
$ sqb scope model:orders --include-nearby
Nearby unavailable (2 of 2)
├─ ○ enum:finance_status [enum; descendant-public; sibling_scope; members 3; type VARCHAR; role models/finance/enums] models/finance/enums/status.sql:1:1
└─ ○ macro:calculate_margin [macro; descendant-public; sibling_scope; params 2; role models/finance/macros] models/finance/macros/margin.py:4:1
├─ ○ enum:finance_status models/finance/enums/status.sql:1
└─ ○ macro:calculate_margin models/finance/macros/margin.py:4
```

The declarations are known, but they belong to a different folder branch. Here, the inspected
Expand Down Expand Up @@ -218,13 +222,13 @@ report sections are:
```console
$ sqb scope test:orders__completed_only --used-only
Used (1)
└─ ● enum:order_status [enum; descendant-public; expected_model through model:orders; members 4; type VARCHAR; role models/commerce/enums] models/commerce/enums/status.sql:1:1
└─ ● enum:order_status models/commerce/enums/status.sql:1

Relationship grants (1 of 1)
└─ ● enum:order_status [enum; descendant-public; expected_model through model:orders; members 4; type VARCHAR; role models/commerce/enums] models/commerce/enums/status.sql:1:1
└─ ● enum:order_status models/commerce/enums/status.sql:1
```

The compact reason `expected_model through model:orders` means **available through expected output
With `--verbose`, these rows show the reason `expected_model through model:orders`, which means **available through expected output
for model `orders`**. With multiple expected models, SQLBuild combines their eligible file-based
declarations into one deterministic set. Tests include macros in that union; scenarios include only
enums and constants. Neither receives declarations defined inside a model's `MODEL()` header.
Expand All @@ -244,19 +248,15 @@ Move preview
Destination: models/finance/orders.sql
Ownership root: models
Retained (2)
├─ ○ macro:add_tax [macro; project; global; params 1; role macros] macros/currency.py:1:1
└─ ○ macro:round_money [macro; project; global; params 1; role macros] macros/currency.py:5:1
├─ ○ macro:add_tax macros/currency.py:1
└─ ○ macro:round_money macros/currency.py:5
Gained (2)
├─ ○ enum:finance_status [enum; descendant-public; inherited_ancestor; members 3; type VARCHAR; role models/finance/enums] models/finance/enums/status.sql:1:1
└─ ○ macro:calculate_margin [macro; descendant-public; inherited_ancestor; params 2; role models/finance/macros] models/finance/macros/margin.py:4:1
├─ ○ enum:finance_status models/finance/enums/status.sql:1
└─ ○ macro:calculate_margin models/finance/macros/margin.py:4
Lost (3)
├─ ● constant:minimum_order_value [constant; descendant-public; inherited_ancestor; type integer; role models/commerce/constants] models/commerce/constants/limits.sql:1:1
├─ ○ enum:order_status [enum; descendant-public; inherited_ancestor; members 4; type VARCHAR; role models/commerce/enums] models/commerce/enums/status.sql:1:1
└─ ● macro:formatted_order_total [macro; descendant-public; inherited_ancestor; params 1; role models/commerce/macros] models/commerce/macros/orders.py:4:1
Private retained (0)
(none)
Relationship retained (0)
(none)
├─ ● constant:minimum_order_value models/commerce/constants/limits.sql:1
├─ ○ enum:order_status models/commerce/enums/status.sql:1
└─ ● macro:formatted_order_total models/commerce/macros/orders.py:4
Invalidated usages (2)
- constant:minimum_order_value
- macro:formatted_order_total
Expand Down Expand Up @@ -287,16 +287,12 @@ Scope chain
└─ project global (2)

Available (3 of 5, 2 collapsed)
├─ ○ constant:minimum_order_value [constant; descendant-public; inherited_ancestor; type integer; role models/commerce/constants] models/commerce/constants/limits.sql:1:1
├─ ○ enum:order_status [enum; descendant-public; inherited_ancestor; members 4; type VARCHAR; role models/commerce/enums] models/commerce/enums/status.sql:1:1
└─ ○ macro:formatted_order_total [macro; descendant-public; inherited_ancestor; params 1; role models/commerce/macros] models/commerce/macros/orders.py:4:1
├─ ○ constant:minimum_order_value models/commerce/constants/limits.sql:1
├─ ○ enum:order_status models/commerce/enums/status.sql:1
└─ ○ macro:formatted_order_total models/commerce/macros/orders.py:4
… 2 globals collapsed; run sqb scope --at models/commerce/returns/new_return.sql --globals all

Relationship grants (0 of 0)
(none)

Nearby unavailable (0 of 0)
(none)
● used by this resource ○ not used by this resource --verbose for scope details

Diagnostics (1)
ERROR S013 models/commerce/returns/new_return.sql: Runtime usage and relationship facts are unavailable for a prospective path
Expand Down Expand Up @@ -327,8 +323,6 @@ Scope folders
sqb scope model:orders --browse global/macros
sqb scope model:orders --list global/macros

Diagnostics (0)
(none)
Completeness: complete
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ Common questions:

Report sections: **Used**, **Scope chain** (owner folder, parents, project globals), **Available**
(globals collapsed; `--globals all` expands them), **Relationship grants** (tests and scenarios),
**Nearby unavailable**, **Diagnostics**. Use `--json` for machine-readable output.
**Nearby unavailable**, **Diagnostics**. Rows show each declaration's identity and `file:line`, and
empty optional sections are hidden; add `--verbose` for each declaration's access details and every
section. Use `--json` for machine-readable output.

Placement rules the scope report reflects:

Expand Down
2 changes: 2 additions & 0 deletions src/sqlbuild/cli/commands/_helpers/entry/dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ def dispatch_cli_command(*, args: CliNamespace, handlers: CliEntrypointHandlers)
paths=args.scope_paths,
json_output=args.json,
no_cache=args.no_cache,
verbose=args.verbose,
no_color=args.no_color,
)
)

Expand Down
1 change: 1 addition & 0 deletions src/sqlbuild/cli/commands/_helpers/entry/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ def _add_inspection_parsers(
)
scope_parser.add_argument("--json", action="store_true", default=False)
scope_parser.add_argument("--no-cache", action="store_true", default=False)
scope_parser.add_argument("--verbose", "-v", action="store_true", default=False)

debug_parser: argparse.ArgumentParser = subparsers.add_parser(CliCommand.DEBUG)
debug_parser.add_argument("--json", action="store_true", default=False)
Expand Down
23 changes: 15 additions & 8 deletions src/sqlbuild/cli/commands/_helpers/janitor_output/outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
)
from sqlbuild.executor.janitor.models import JanitorExecutionResult, JanitorPlan
from sqlbuild.presentation.classes.cli_style import CliStyle
from sqlbuild.presentation.main.count_noun import format_count_noun


def write_janitor_disabled(*, invocation: JanitorInvocation) -> None:
Expand Down Expand Up @@ -121,7 +122,11 @@ def write_janitor_completion(


def _deleted_message(*, result: JanitorExecutionResult) -> str:
archived_prefix: str = f"Archived {len(result.archived)} relations. " if result.archived else ""
archived_prefix: str = (
f"Archived {format_count_noun(count=len(result.archived), singular='relation')}. "
if result.archived
else ""
)
return archived_prefix + _deletion_summary(result=result)


Expand All @@ -138,19 +143,21 @@ def _deletion_summary(*, result: JanitorExecutionResult) -> str:
+ len(result.deleted_archives)
+ len(result.deleted_query_diff_artifacts)
)
objects: str = format_count_noun(count=deleted_object_count, singular="object")
pruned_state_count: int = len(result.pruned_direct_state) + len(result.pruned_virtual_state)
non_checkpoint_state_count: int = deleted_state_count - len(result.deleted_checkpoints)
if non_checkpoint_state_count or pruned_state_count:
pruned_state_label: str = (
"state tables" if result.pruned_virtual_state else "direct state tables"
"state table" if result.pruned_virtual_state else "direct state table"
)
return (
f"Deleted {deleted_object_count} objects, deleted {deleted_state_count} "
f"state items, and pruned {pruned_state_count} {pruned_state_label}."
f"Deleted {objects}, deleted "
f"{format_count_noun(count=deleted_state_count, singular='state item')}, and pruned "
f"{format_count_noun(count=pruned_state_count, singular=pruned_state_label)}."
)
if result.deleted_checkpoints:
return (
f"Deleted {deleted_object_count} objects and "
f"{len(result.deleted_checkpoints)} checkpoints."
checkpoints: str = format_count_noun(
count=len(result.deleted_checkpoints), singular="checkpoint"
)
return f"Deleted {deleted_object_count} objects."
return f"Deleted {objects} and {checkpoints}."
return f"Deleted {objects}."
7 changes: 6 additions & 1 deletion src/sqlbuild/cli/commands/_helpers/scope/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
ScopeReportFilters,
)
from sqlbuild.compiler.scopes.types import DeclarationKind, DiagnosticSeverity
from sqlbuild.presentation.main.supports_color import supports_color


def run_scope_command(
Expand Down Expand Up @@ -107,7 +108,11 @@ def run_scope_command(
output: str = (
serialize_scope_report(report=result)
if request.json_output
else render_scope_result(result=result, request=request)
else render_scope_result(
result=result,
request=request,
use_color=output_stream is None and not request.no_color and supports_color(),
)
)
stream.write(output)
return 1 if _failed(result=result) else 0
Expand Down
Loading
Loading