Browse Source
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
2 changed files with 95 additions and 0 deletions
@ -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…
Reference in new issue