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
24 changes: 23 additions & 1 deletion dev/philosophy/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,31 @@ end
Why: visible origin, safe refactors (only the `using ..X` line changes), no accidental
shadowing, same rule for external and sibling symbols.

## Naming conventions

Three visibility levels, signalled by the name itself:

- `symbol` — **public**: exported via `export`; part of the documented API.
- `_symbol` — **private helper**: unexported; internal implementation detail.
Accessible via qualified path (`Module._symbol`) but not advertised.
- `__symbol` — **default value**: unexported; provides a replaceable semantic
default (e.g. `__display() = true`). The double underscore distinguishes it
from a plain helper: it is a *default* that a higher-level layer may override,
not merely a utility function.

```julia
export build_thing # public

function _validate(x) # private helper
...
end

__verbose()::Bool = false # default value (replaceable by extension or subtype)
```

## Two-level exports

- **Submodule**: `export` for its public API; internals (`_helper`) unexported.
- **Submodule**: `export` for its public API; internals (`_helper`, `__default`) unexported.
- **Package (top-level)**: **no** `export`. Load submodules with `using .Submodule`;
users reach them via `Package.Submodule.sym`.

Expand Down
94 changes: 83 additions & 11 deletions dev/planning.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ to align with the human before touching code, not to produce a 50-step document.
## When to write a plan

Before any task that:

- touches more than one file,
- changes a public interface, or
- involves a non-obvious design decision.
Expand All @@ -16,7 +17,7 @@ Write the plan, share it, wait for confirmation before starting.

## Template

```
````markdown
# Plan: <short title>

## What and why
Expand All @@ -34,32 +35,74 @@ Write the plan, share it, wait for confirmation before starting.
Steps:
1. …
2. …
Checkpoint: run `test/suite/<group>` via MCP. All green before moving on.
Checkpoint:

```bash
# MCP (preferred)
get_test_command test_args=["suite/<group>"]
then generate_report

# Fallback
julia --project=@. -e 'using Pkg; Pkg.test(; test_args=["suite/<group>"])' 2>&1 | tee /tmp/<branch>_phaseA.log
grep -E "Error|Fail|Test Summary" /tmp/<branch>_phaseA.log
```

All green before moving on.

### Phase B — <name>

Steps:
1. …
Checkpoint: run `test/suite/<group>` + full suite.
Checkpoint:

```bash
# MCP (preferred)
get_test_command test_args=["suite/<group1>", "suite/<group2>"]
then generate_report

# Final phase only: full suite
julia --project=@. -e 'using Pkg; Pkg.test()' 2>&1 | tee /tmp/<branch>_final.log
grep -E "Error|Fail|Test Summary" /tmp/<branch>_final.log
```


## Human checkpoints

- ⛔ Ask before first commit.
- ⛔ Ask before pushing to a shared branch.
- ⛔ Ask before deleting files that were not created in this task.
- ⛔ Ask if a design decision arises that was not in the plan.

## Out of scope

<List things explicitly not done in this task.>
```
````

---

## Rules for filling in the template

**Phases** — one phase = one coherent unit that can be tested independently. A phase
ends with a test checkpoint (run the affected test suite via the MCP `get_test_command`
tool, then `generate_report`). Never skip the checkpoint.
ends with a test checkpoint. Never skip the checkpoint.

**Test checkpoints** — always use the MCP `get_test_command` tool with `test_args` to
run only the affected test groups, then `generate_report` to parse the result. If MCP
is unavailable, fall back to:

```bash
# Phase-specific (preferred during intermediate phases)
julia --project=@. -e 'using Pkg; Pkg.test(; test_args=["suite/<group>"])' \
2>&1 | tee /tmp/<branch>_<phase>.log
grep -E "Error|Fail|Test Summary" /tmp/<branch>_<phase>.log

# Full suite (mandatory for the last phase only)
julia --project=@. -e 'using Pkg; Pkg.test()' \
2>&1 | tee /tmp/<branch>_final.log
grep -E "Error|Fail|Test Summary" /tmp/<branch>_final.log
```

Never run the full suite for intermediate phases — it is too slow and hides errors.

**Steps** — file-level granularity. "Edit `src/X/y.jl` to add method `f`" not "implement
the feature". Concrete enough that a step is either done or not.
Expand All @@ -70,14 +113,23 @@ missed.

**Docstrings** — always the last step of the last phase, once the API is stable.

**Code snippets** — include short code snippets directly in the plan to guide the
implementer. This applies to:

- *Source changes*: show the before/after signature or struct definition.
- *Call-site updates*: show the exact old and new lines when renaming symbols.
- *Test patterns*: show the test structure (fake type + testset skeleton) when the
test pattern is non-obvious.
Snippets should be *concise shapes*, not full implementations.

**Out of scope** — explicit. Prevents scope creep and clarifies what the human should
not expect after the plan is executed.

---

## Concrete example structure (abbreviated)

```
````markdown
# Plan: rename content → dynamics trait

## What and why
Expand All @@ -97,23 +149,43 @@ names the axis. Purely mechanical, no behavior change.
Steps:
1. Rename src/Traits/content.jl → dynamics.jl; update all names inside.
2. Update include path in src/Traits/Traits.jl; update exports.
Checkpoint: test/suite/traits — all green.
Checkpoint:

```bash
get_test_command test_args=["suite/traits"] # MCP
# fallback: julia --project=@. -e 'using Pkg; Pkg.test(; test_args=["suite/traits"])' 2>&1 | tee /tmp/rename_traits.log
```

### Phase B — Adapt consumers (Configs, Solutions)

Steps:

1. Update src/Configs/* (content_trait → dynamics_trait, value names).
2. Update src/Solutions/building.jl.
Checkpoint: test/suite/configs + test/suite/solutions — all green.
Checkpoint:

```bash
get_test_command test_args=["suite/configs", "suite/solutions"] # MCP
```

### Phase C — Tests + docs

Steps:

1. Rename and update test/suite/traits/test_content.jl → test_dynamics.jl.
2. Update docs/api_reference.jl file list.
Checkpoint: full suite green.
Checkpoint (full suite — last phase):

```bash
get_test_command # MCP, no test_args = full suite
# fallback: julia --project=@. -e 'using Pkg; Pkg.test()' 2>&1 | tee /tmp/rename_final.log
```

## Human checkpoints

- ⛔ Ask before commit after Phase C.

## Out of scope

Parametrizing AbstractSystem/AbstractFlow by the dynamics trait (see action_plan.md Phase C/D).
```
````
6 changes: 3 additions & 3 deletions src/Descriptions/complete.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ ERROR: AmbiguousDescription: the description (:f,) is ambiguous / incorrect
Context: description completion
```

See also: [`CTBase.Descriptions.compute_similarity`](@ref), [`CTBase.Descriptions.find_similar_descriptions`](@ref), [`CTBase.Descriptions.format_description_candidates`](@ref), [`CTBase.Exceptions.AmbiguousDescription`](@ref)
See also: [`CTBase.Descriptions._compute_similarity`](@ref), [`CTBase.Descriptions._find_similar_descriptions`](@ref), [`CTBase.Descriptions._format_description_candidates`](@ref), [`CTBase.Exceptions.AmbiguousDescription`](@ref)
"""
function complete(list::Symbol...; descriptions::Tuple{Vararg{Description}})::Description
n = length(descriptions)
Expand All @@ -70,8 +70,8 @@ function complete(list::Symbol...; descriptions::Tuple{Vararg{Description}})::De

if maximum(table[:, 2]) == 0
# Find similar descriptions for helpful suggestions
similar_descs = find_similar_descriptions(list, descriptions; max_results=5)
all_candidates = format_description_candidates(descriptions; max_show=10)
similar_descs = _find_similar_descriptions(list, descriptions; max_results=5)
all_candidates = _format_description_candidates(descriptions; max_show=10)

# Build contextual suggestion
suggestion = if !isempty(similar_descs)
Expand Down
8 changes: 4 additions & 4 deletions src/Descriptions/similarity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ julia> CTBase.Descriptions.compute_similarity((:x, :y), (:a, :b))
0.0
```
"""
function compute_similarity(desc1::Description, desc2::Description)::Float64
function _compute_similarity(desc1::Description, desc2::Description)::Float64
if isempty(desc1) || isempty(desc2)
return 0.0
end
Expand Down Expand Up @@ -62,7 +62,7 @@ julia> CTBase.Descriptions.find_similar_descriptions((:a,), descriptions)
"(:a, :c)"
```
"""
function find_similar_descriptions(
function _find_similar_descriptions(
target::Tuple{Vararg{Symbol}},
descriptions::Tuple{Vararg{Description}};
max_results::Int=5,
Expand All @@ -73,7 +73,7 @@ function find_similar_descriptions(

# Compute similarities
similarities = [
(compute_similarity(target, desc), string(desc)) for desc in descriptions
(_compute_similarity(target, desc), string(desc)) for desc in descriptions
]

# Sort by similarity (descending) and take top results
Expand Down Expand Up @@ -118,7 +118,7 @@ julia> CTBase.Descriptions.format_description_candidates(descriptions; max_show=
"(:x, :y)"
```
"""
function format_description_candidates(
function _format_description_candidates(
descriptions::Tuple{Vararg{Description}}; max_show::Int=5
)::Vector{String}
if isempty(descriptions)
Expand Down
20 changes: 10 additions & 10 deletions src/Exceptions/display.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Filters out Julia stdlib.
# Returns
- `Vector`: Filtered stacktrace frames
"""
function extract_user_frames(st::Vector)
function _extract_user_frames(st::Vector)
user_frames = filter(st) do frame
file_str = string(frame.file)
# Keep frames that are NOT from Julia stdlib or exception display internals
Expand All @@ -68,7 +68,7 @@ Display an error in a user-friendly format with clear sections and user code loc
- `io::IO`: Output stream
- `e::CTException`: The exception to display
"""
function format_user_friendly_error(io::IO, e::CTException)
function _format_user_friendly_error(io::IO, e::CTException)
#println(io, "\n" * "━"^70)
_print_ansi_styled(io, "Control Toolbox Error\n", :red, true)
#println(io, "─"^28)
Expand Down Expand Up @@ -232,7 +232,7 @@ function format_user_friendly_error(io::IO, e::CTException)
end

# Add user code location
user_frames = extract_user_frames(stacktrace(catch_backtrace()))
user_frames = _extract_user_frames(stacktrace(catch_backtrace()))
if !isempty(user_frames)
println(io, "📍 In your code:")
# Show up to 3 most relevant user frames
Expand Down Expand Up @@ -262,7 +262,7 @@ Custom error display for IncorrectArgument.
Shows user-friendly format with enriched information.
"""
function Base.showerror(io::IO, e::IncorrectArgument)
return format_user_friendly_error(io, e)
return _format_user_friendly_error(io, e)
end

"""
Expand All @@ -271,7 +271,7 @@ end
Custom error display for PreconditionError.
"""
function Base.showerror(io::IO, e::PreconditionError)
return format_user_friendly_error(io, e)
return _format_user_friendly_error(io, e)
end

"""
Expand All @@ -280,7 +280,7 @@ end
Custom error display for NotImplemented.
"""
function Base.showerror(io::IO, e::NotImplemented)
return format_user_friendly_error(io, e)
return _format_user_friendly_error(io, e)
end

"""
Expand All @@ -289,7 +289,7 @@ end
Custom error display for ParsingError.
"""
function Base.showerror(io::IO, e::ParsingError)
return format_user_friendly_error(io, e)
return _format_user_friendly_error(io, e)
end

"""
Expand All @@ -298,7 +298,7 @@ end
Custom error display for AmbiguousDescription.
"""
function Base.showerror(io::IO, e::AmbiguousDescription)
return format_user_friendly_error(io, e)
return _format_user_friendly_error(io, e)
end

"""
Expand All @@ -307,7 +307,7 @@ end
Custom error display for ExtensionError.
"""
function Base.showerror(io::IO, e::ExtensionError)
return format_user_friendly_error(io, e)
return _format_user_friendly_error(io, e)
end

"""
Expand All @@ -316,5 +316,5 @@ end
Custom error display for SolverFailure.
"""
function Base.showerror(io::IO, e::SolverFailure)
return format_user_friendly_error(io, e)
return _format_user_friendly_error(io, e)
end
5 changes: 4 additions & 1 deletion src/Extensions/Extensions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,10 @@ function run_tests(; kwargs...)
return run_tests(TestRunnerTag(); kwargs...)
end

# Export public API (only user-facing functions, tags are internal)
# Export public API
export automatic_reference_documentation, postprocess_coverage, run_tests
export AbstractDocumenterReferenceTag, DocumenterReferenceTag
export AbstractCoveragePostprocessingTag, CoveragePostprocessingTag
export AbstractTestRunnerTag, TestRunnerTag

end # module
Loading
Loading