Browse Source

add goal G-011: optional per-console err channel with defined stderr semantics

punk::console models two channels per console ({in out}); its own
diagnostics fall back to raw puts stderr and subshell/backend/theme goals
(G-001/G-008/G-009) inherit the dual-channel assumption. G-011 defines err
as an attribute of the canonical {in out} console identity: {in out err}
specs accepted everywhere -console is, a nullable err field on
opunk::Console, err defaulting to process stderr for the default console
and to the console's out channel elsewhere, console-directed diagnostics
and an emit-to-err path, and cross-thread discovery via console_fact_get.
Ownership/fact/mode-cache keys stay canonical {in out}, so the G-007 work
is untouched.

Detail file records the gap analysis, the rejected shapes (err-in-identity
triple key, scope-boundary-only, uniform err==out, unset-means-none) and
the G-001 sequencing interaction.

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 1 week ago
parent
commit
4892acb501
  1. 6
      GOALS.md
  2. 89
      goals/G-011-console-stderr-semantics.md

6
GOALS.md

@ -109,3 +109,9 @@ Scope: src/modules/punk/repl-999999.0a1.0.tm, src/modules/punk/repl/codethread-9
Detail: goals/G-010-subshell-tree-navigation.md
Goal: subshells form a navigable tree - a subshell can be suspended rather than quit, listed, resumed, and the console switched to any live subshell in the tree (e.g. grandchild to grandparent, or across branches) with each subshell's console state re-applied via its G-008 state set, building on the non-nested subshell model of G-002.
Acceptance: from a grandchild subshell a single switch command reaches the grandparent without unwinding through the intermediate parent; switching between subshells on different branches preserves each subshell's session state and re-applies its console state set on activation; suspended subshells can be listed and resumed; `quit` still unwinds to the launching parent as today; a subshell whose switch commands are hidden/restricted cannot initiate switches.
### G-011 [proposed] Optional per-console err channel with defined stderr semantics
Scope: src/modules/punk/console-999999.0a1.0.tm, src/modules/opunk/console-999999.0a1.0.tm, src/modules/punk/repl-999999.0a1.0.tm
Detail: goals/G-011-console-stderr-semantics.md
Goal: a console optionally carries an err channel as an attribute of its canonical {in out} identity - {in out err} specs accepted everywhere -console is, err resolving to process stderr for the default console and to the console's out channel elsewhere - so diagnostics and emit-to-err are first-class per-console operations instead of raw puts stderr.
Acceptance: console_spec_resolve and every -console site accept an {in out err} spec (err optional; existing pair/instance-name/object spec forms unchanged); opunk::Console exposes the err channel (nullable, additive base-class change); an unset err resolves to stderr for the default console and to the console's own out channel otherwise; punk::console's own warnings/diagnostics emitted while operating on a resolvable console go to that console's err (raw puts stderr remains only where no console is in play); an emit-to-err path exists and is exercised by at least one real consumer (e.g punk::repl); the effective err is discoverable from any thread/interp via console_fact_get; ownership/fact/mode-cache keys remain canonical {in out}; the existing console test suites pass unchanged.

89
goals/G-011-console-stderr-semantics.md

@ -0,0 +1,89 @@
# G-011 Optional per-console err channel with defined stderr semantics
Status: proposed
Scope: src/modules/punk/console-999999.0a1.0.tm, src/modules/opunk/console-999999.0a1.0.tm, src/modules/punk/repl-999999.0a1.0.tm
Acceptance: console_spec_resolve and every -console site accept an {in out err} spec (err optional; existing pair/instance-name/object spec forms unchanged); opunk::Console exposes the err channel (nullable, additive base-class change); an unset err resolves to stderr for the default console and to the console's own out channel otherwise; punk::console's own warnings/diagnostics emitted while operating on a resolvable console go to that console's err (raw puts stderr remains only where no console is in play); an emit-to-err path exists and is exercised by at least one real consumer (e.g punk::repl); the effective err is discoverable from any thread/interp via console_fact_get; ownership/fact/mode-cache keys remain canonical {in out}; the existing console test suites pass unchanged.
## Context
punk::console models exactly two channels per console: {in out}, defaulting to {stdin stdout}.
For pure ANSI terminal control that is a faithful model - queries and responses share one
terminal connection by protocol design - but the goals collectively move punk::console from
"terminal control library" toward "session-wide console abstraction" (G-001 pluggable backends,
G-007 location transparency, G-008 scoped state, G-009 themed profiles), and there the missing
stderr stream becomes load-bearing:
- **Error/diagnostic routing is ad-hoc.** punk::console's own warnings (enableRaw/disableRaw
failures, query timeouts, reader-cooperation warnings, test_char_width failures - dozens of
sites) go to raw `puts stderr`, bypassing the abstraction the module provides. A console
backed by an ssh channel or tk widget (G-001) has no principled way to receive its own error
output: backend authors must either merge stderr into stdout or bypass punk::console for
errors, fragmenting the abstraction.
- **No console-level separation of result vs diagnostic streams.** The shellrun
run/runout/runerr/runx family preserves the stdout/stderr distinction at the process layer,
but once output enters the console the distinction collapses into the single out channel. A
subshell wanting to display a process's stderr differently (colour, prefix, separate pane)
must do so outside the console model - and a G-009 theme has no stderr stream to style.
- **Cross-interp discovery has no slot.** The G-007 fact store and ownership registry are keyed
by canonical {in out}; "what is my console's stderr?" has no fact to read, so location
transparency covers stdout-shaped operations only.
- **Raw-mode restore is asymmetric.** `previous_stty_state_stderr` is tracked for stty-state
restoration, but the model gives no way to address stderr as a console channel.
## Approach
**err is an attribute of a console, not part of its identity.** Console identity - the key for
the ownership registry (punk_console_owners), fact store (punk_console_facts) and mode cache
(punk_console_modecache) - remains the canonical {in out} pair: two consoles differing only in
err target are the same terminal, and keeping the key stable means the G-007 slices are
untouched.
1. **Spec grammar.** console_spec_resolve (and the -console option everywhere) accepts a
3-element {in out err} list. The 3-element slot is free in the existing grammar (pairs are
llength 2, instance names 1, voo object values >= 7). Existing spec forms are unchanged.
2. **Object layer.** ::opunk::Console gains a nullable err field beside o_in/o_out (additive
base-class change; subclasses without a stderr-equivalent leave it unset).
3. **Resolution defaults.** An unset err resolves to process stderr for the default console
{stdin stdout} (matching where its diagnostics go today) and to the console's own out
channel for any other console (single-stream backends merge, losing nothing they had).
4. **Diagnostics routing.** punk::console warnings/diagnostics raised while operating on a
resolvable console are emitted to that console's effective err. Raw `puts stderr` remains
only for contexts where no console is in play (e.g module-load-time warnings).
5. **Emit path.** A first-class emit-to-err form (shape decided at implementation time - e.g an
err-target variant of the opt_console_out convention) exercised by at least one real
consumer such as punk::repl, proving subshells can present stderr distinctly.
6. **Cross-thread discovery.** The effective err is exposed through console_fact_get so any
thread/interp of a session can answer "where do this console's diagnostics go" (G-007
location transparency extends to the err attribute).
## Alternatives considered
- **err in the identity key: canonical {in out err} triple** - rejected: conflates routing with
identity (a console does not become a different terminal when its diagnostic target changes),
and churns the G-007 registry/fact/cache keying and its tests for no modelling gain.
- **Scope-boundary documentation only (no third channel)** - rejected by the user for subshell
flexibility: G-001 backends and G-009 themes need a distinct diagnostic stream to be
first-class, and a parallel unmanaged diagnostics mechanism would fragment the abstraction.
(Had punk::console stayed a pure terminal-control library, this would have been the right
answer - the dual-channel model is correct for ANSI control itself.)
- **Uniform default err == out (including the default console)** - rejected: silently changes
where the default console's diagnostics land today (process stderr), breaking redirection
and 2>-style capture users rely on.
- **Unset err means none (error on emit-to-err)** - rejected: pushes fallback burden onto every
caller and makes diagnostics emission failure-prone exactly where it must be reliable.
## Notes
- **Channel-name locality.** Like in/out, an err channel name is thread-local for non-std
channels; the fact-store exposure inherits the owner-qualified keying that already handles
this for other facts. What is discoverable cross-thread is the owner-context name (and the
default-console case, where std names are process-wide).
- **G-001 interaction.** G-001's acceptance requires the base class unchanged *by G-001's own
work*; the nullable err field is an additive change made by this goal. If G-011 lands first,
G-001 subclasses simply inherit the field; sequencing either way is safe.
- **G-008/G-009 interaction.** Once err exists, a per-subshell re-route of diagnostics is
console state that a G-008 state set could capture; that is enrichment for those goals, not
part of this goal's acceptance.
- **Not in scope.** Channel transforms/filtering on err (shellfilter stacks), a separate err
reader, and any change to shellrun's process-layer stdout/stderr handling - this goal defines
the console-side slot they can target.
Loading…
Cancel
Save