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