Named verbosity channels: selective output with -v CHANNEL
Problem
The current verbosity system (-v, -vv, -vvv) is a single numeric dial — turning it up reveals everything at that level. Users can't selectively enable one category of verbose output without getting all the others.
Motivating example: When running a comparison search, the output only shows variable bindings:
C:\code\Prime-Square-Sum>prime-square-sum.py --expr "for_any tri(n) == trisum(m)" --max n:50 --max m:50
Found: m=3, n=2
Found: m=4, n=2
Found: m=5, n=2
Found: m=10, n=36
Users often want to see what values satisfied the comparison — e.g., tri(2) = 3 == trisum(3) = 3 — to understand the relationship, not just the variable assignments. But there's no way to get this without enabling -v, which also dumps expression parsing, variable enumeration, bounds, timing, and other noise.
Proposed solution
Extend the verbosity system to support named channels alongside numeric levels:
# Numeric levels (existing behavior, unchanged)
-v # level 1 — expression, variables, bounds, timing
-vv # level 2 — config, algorithm selection
-vvv # level 3 — debug
# Named channels (new)
-v vals # show computed LHS/RHS values on match output
-v parse # show parse tree details
-v iter # show iteration progress
-v timing # show timing breakdowns
-v config # show configuration resolution
-v algorithm # show algorithm selection details
-v hint # show all hints regardless of context
# Combined: numeric level + specific channels
-vv vals # level 2 + vals channel
-v vals timing # level 1 + vals + timing channels
Channel activation rules
A message with emit(level, msg, channel='foo') displays if ANY of:
- Numeric verbosity >= level (existing behavior)
- Channel
'foo' is explicitly enabled via -v foo
--quiet is NOT set
This means -v vals at verbosity level 0 would show ONLY the vals channel output (plus level-0 messages), without triggering all the level-1 noise.
First use case: vals channel
The vals channel would display computed LHS and RHS values alongside match output:
# Default (no -v)
Found: m=3, n=2
# With -v vals
Found: m=3, n=2 → tri(2) = 3 == trisum(3) = 3
# With -v vals (does_exist, single match)
Found: n=7 → primesum(7,2) = 666 == 666
Implementation for vals: find_matches() would need to evaluate LHS and RHS independently (currently it only evaluates the full comparison as a boolean) and include them in the yield dict (e.g., __lhs_value__, __rhs_value__). format_match() would then check if vals channel is enabled and append the value display.
Current channel inventory
These channels already exist internally (used in emit() calls) but are not user-accessible:
| Channel |
Current usage |
Level |
parse |
Expression string display |
1 |
iter |
Variables, bounds |
1 |
timing |
Match count, elapsed time |
1 |
config |
Config loading, sieve settings, silence hint |
1-2 |
algorithm |
Algorithm selection, memory limits |
2 |
hint |
Contextual tips and suggestions |
0 |
progress |
Match count during long runs |
1 |
general |
Default/uncategorized |
varies |
vals |
NEW — computed LHS/RHS values |
1 |
CLI design
The -v flag currently uses action='count'. To support named channels, parsing would need to change:
# Option A: Extend -v to accept optional channel names
parser.add_argument('-v', '--verbose', nargs='*', action='append')
# -v → [None] (level 1)
# -v -v → [None, None] (level 2)
# -v vals → ['vals'] (level 0 + vals channel)
# -vv vals → parsed as level 2 + vals
# Option B: Separate flag for channels
parser.add_argument('-v', '--verbose', action='count', default=0)
parser.add_argument('--show', action='append', metavar='CHANNEL')
# -v --show vals
# --show vals --show timing
# Option C: Combined syntax with colon
parser.add_argument('-v', '--verbose', action='append')
# -v (level 1)
# -v:vals (enable vals channel)
# -vv:vals (level 2 + vals)
Option A is cleanest for users but requires custom argument parsing. Option B is simplest to implement. Option C mirrors common logging conventions.
OutputManager changes
class OutputManager:
def __init__(self, verbosity=0, quiet=False, channels=None, file=None):
self.verbosity = verbosity
self.quiet = quiet
self.enabled_channels = set(channels or [])
# ...
def emit(self, level, message, *, channel='general', **kwargs):
if self.quiet:
return
if self.verbosity < level and channel not in self.enabled_channels:
return
text = message.format(**kwargs) if kwargs else message
print(text, file=self.file)
Documentation
This feature warrants its own documentation page (docs/verbosity.md or docs/output-channels.md) explaining:
- The numeric level system and what each level shows
- Available named channels and what they control
- Combined usage patterns
- Examples for common workflows (debugging, value inspection, performance analysis)
- How channels interact with
--quiet and --format json
Acceptance criteria
Related issues
Analysis
See 2026-02-10__16-08-44__full-postmortem_issue-63-dynamic-cli-help-defaults.md for the session context where this feature was identified.
Named verbosity channels: selective output with
-v CHANNELProblem
The current verbosity system (
-v,-vv,-vvv) is a single numeric dial — turning it up reveals everything at that level. Users can't selectively enable one category of verbose output without getting all the others.Motivating example: When running a comparison search, the output only shows variable bindings:
Users often want to see what values satisfied the comparison — e.g.,
tri(2) = 3 == trisum(3) = 3— to understand the relationship, not just the variable assignments. But there's no way to get this without enabling-v, which also dumps expression parsing, variable enumeration, bounds, timing, and other noise.Proposed solution
Extend the verbosity system to support named channels alongside numeric levels:
Channel activation rules
A message with
emit(level, msg, channel='foo')displays if ANY of:'foo'is explicitly enabled via-v foo--quietis NOT setThis means
-v valsat verbosity level 0 would show ONLY the vals channel output (plus level-0 messages), without triggering all the level-1 noise.First use case:
valschannelThe
valschannel would display computed LHS and RHS values alongside match output:Implementation for vals:
find_matches()would need to evaluate LHS and RHS independently (currently it only evaluates the full comparison as a boolean) and include them in the yield dict (e.g.,__lhs_value__,__rhs_value__).format_match()would then check if vals channel is enabled and append the value display.Current channel inventory
These channels already exist internally (used in
emit()calls) but are not user-accessible:parseitertimingconfigalgorithmhintprogressgeneralvalsCLI design
The
-vflag currently usesaction='count'. To support named channels, parsing would need to change:Option A is cleanest for users but requires custom argument parsing. Option B is simplest to implement. Option C mirrors common logging conventions.
OutputManager changes
Documentation
This feature warrants its own documentation page (
docs/verbosity.mdordocs/output-channels.md) explaining:--quietand--format jsonAcceptance criteria
OutputManagersupportsenabled_channelssetemit()checks channel membership as alternative to numeric levelvalschannel implemented as first named channelfind_matches()yields__lhs_value__and__rhs_value__for comparison matchesformat_match()appends value display when vals channel is active-v/-vv/-vvvbehavior unchangedRelated issues
Analysis
See
2026-02-10__16-08-44__full-postmortem_issue-63-dynamic-cli-help-defaults.mdfor the session context where this feature was identified.