A word rejected by an OPTIONAL argument's allocation screen (G-071
restricted-choice screen, or the basic int/double/bool/number/dict type
screen) that subsequently overflows the argument list now reports the
specific rejection instead of "Received more values than can be assigned":
- choiceviolation naming the word and allowed choices, with -badarg/-badval
and the final-validation message template (Leading/Trailing argument
attribution), for choice-screen rejections
- typemismatch naming the declared (normalized) type, with -badarg/-badval,
for basic-type screen rejections
Mechanism: private::get_dict_can_assign_value returns a 'rejection' record
(single-member clauses only; literal-category mismatches deliberately not
recorded); get_dict_form collects first-rejection-per-word from the three
allocation call sites (leader/value split trial, leaders loop, values loop)
into a per-form store; the two toomanyarguments raise sites prefer a
recorded rejection for the unassignable word. Genuinely-surplus words still
report toomanyarguments; words consumed by later slots parse unchanged;
multiform selection (rank_form_failures) untouched.
errorselection.test _GAP pins flipped to the pointed expectations (pre-fix
behaviour pinned in the previous commit). punk::args buildversion
0.12.0 -> 0.12.1.
Closeout: G-082 flipped achieved + archived (index -> GOALS-archive.md,
detail -> goals/archive/ with mechanism/verification/residue recorded);
G-073 note updated to point at the new rejection-record seam;
src/modules/AGENTS.md authoring-ergonomics bullet re-documented (-optional 0
workaround no longer needed on >= 0.12.1; make.tcl keeps it - it runs
against the bootsupport 0.12.0 snapshot). Project version 0.12.28 +
CHANGELOG.
Verified: full punk/args suite 228/224/0-failed (4 intended skips) and
punk/ns 57/57 under Tcl 9.0.3; errorselection.test 7/7 under Tcl 8.7;
make.tcl frobnicate still pointed; goals_lint clean.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
New proposed goal G-082: single-form parse error selection should prefer an
optional argument's specific choice/type rejection over the generic
"more values than can be assigned" overflow (regression vs punk::args 0.5.0
found while dogfooding in G-030; the -optional 0 workaround sites in make.tcl
and the AGENTS authoring-ergonomics bullet are flagged for revisiting when it
ships).
Sibling notes parked in existing proposed goals:
- G-028: make.tcl's name-only process-sweep misidentification (unrelated
same-named executables) - full-path matching rides the same
process-identification infrastructure as the locker report.
- G-025: punk-exe-hosted make.tcl runs the kit's pre-loaded module copies, not
bootsupport snapshots - a self-report surface for running-interp module
provenance would make the mixing observable.
- G-073: reciprocal relation to G-082 (both want the "surface the specific
rejection" seam in validation error reporting).
- G-075: synopsis renders the definition id rather than @cmd -name - consider
a fallback for non-command ids when reworking id rendering.
goals_lint: unchanged (only the pre-existing G-078/G-081 index-mirror findings).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Fixes the constructed-definition indent leak user review spotted in
'make.tcl help' output (@cmd -help Description first line rendered less
indented than continuations): HELPTEXTS and the inline shell/help/toplevel
help values are now block-form (first value line whitespace-only) with a bare
@normalize in every constructed definition, per G-045's preferred style. The
braced option fragments never leaked (literal block-shaped parts get
file-style normalization) and head-form values are contractually untouched by
@normalize. Also adds a define-time capability probe (punk::args::get_spec on
representative ids inside the guarded block) so a stale punk::args that
accepts the raw text but cannot resolve a mechanism (e.g pre-@normalize
snapshots) degrades to the plain fallback immediately instead of erroring at
first parse.
AGENTS infrastructure for future documentation work: src/modules/AGENTS.md
"Documentation And Comments" gains a "punk::args definition authoring
ergonomics" subsection (G-045 mechanisms: @normalize + block form for
constructed defs, -unindentedfields, -& record continuation, container
quoting rules; plus the G-030 lessons: explicit @form -synopsis for
script-level ids, -optional 0 for pointed choiceviolation errors);
src/AGENTS.md cross-references it and names ::punkboot::argdoc as the
in-tree constructed-def exemplar. Follow-up recorded in the G-030 archive.
Verified: flush Description rendering for toplevel/subcommand/shell/help
usage; dispatch, degraded mode (PUNKBOOT_PLAIN) and usage-error paths
re-verified; layout make.tcl copies re-synced via 'make.tcl bootsupport
-confirm 0' (punkcheck channel); goals_lint unchanged (only the pre-existing
G-078/G-081 index-mirror findings).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
All 15 make.tcl subcommands are punk::args-declared ((script)::punkboot +
(script)::punkboot::<sub>, built from single-source summary/help/opt dicts with
shared option fragments and explicit @form -synopsis). Bare make.tcl / -help,
'help ?subcommand?' and '<subcommand> -help' render tabled usage; invalid
flags/subcommands raise punk::args usage errors (exit 1) with unambiguous
prefix resolution; 'shell' is a declared passthrough.
Prompt policy: new -confirm 0|1 (default 1) on every confirmation point
(bootsupport minor-staleness gate, vfscommonupdate REPLACE, kit-type mismatch).
Default prompts only on an interactive terminal and aborts fast with guidance
on non-interactive stdin (::punkboot::lib::stdin_is_interactive; Tcl 8.6 lacks
the -inputmode probe and is assumed interactive). -confirm 0 proceeds
(staleness, REPLACE) or skips the mismatched kit. The piped-y agent workflow is
retired; the old vfscommonupdate decline half-abort (printed done, exit 0) now
exits 1.
Degrade rule: punk::args/punk::ansi/textblock boot requires guarded; definition
load caught with fallback to a self-contained scan (-k/-dirty-abort/-confirm
supported) + plain punkboot_gethelp (availability NOTE factored into
punkboot_availability_note, shared by both paths); rendering degrades
independently of parsing (errorstyle minimal / plain help when the ANSI stack
is unhealthy); PUNKBOOT_PLAIN=1 forces degraded mode. punk::args joins the
bootstrap-tracked _runtime_deps staleness set (parsing contract only - the
rendering stack degrades instead; contract docs updated in
src/bootsupport/AGENTS.md + src/modules/AGENTS.md, workflow docs in
src/AGENTS.md + src/tests/shell/AGENTS.md).
Verified: usage/help/error paths, vfscommonupdate fail-fast and -confirm 0 runs
with closed stdin, staleness flag route live (args 0.5.0->0.12.0 tripped the
gate), degraded-mode help/check/info, full 'project -k -confirm 0' kit build.
Dogfooding finding recorded in the goal file: punk::args 0.12.0 reports generic
overflow where 0.5.0 reported choiceviolation for optional-choice mismatches
(worked around with required leaders; candidate error-selection improvement).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
New punk::args::formcheck <id> ?-return dict|summary?: reports the form pairs
of a multiform definition that some argument list could cleanly match
simultaneously (the multipleformmatches inputs). Static pairwise pass over the
resolved FORMS enumerates each form's positional word-slot chains (leaders then
values; -optional and ?-wrapped clause-member branching; -multiple capped;
options excluded - order-free), screens aligned equal-length chains per
position (discriminator words via choiceword_match, type witnesses from a
small table), and CONFIRMS every candidate witness arglist with a real
single-form parse against both forms (parse_status - no raise, no
user-supplied words, no define/resolve cost). Findings are therefore witnessed
- discriminated form pairs cannot false-alarm; misses (exotic types,
option-requiring forms, enumeration caps) are the documented conservative
direction. Classes: type_weakness (discriminator aligned with a permissive
non-validating type - any/none/string/ansistring/globstring/expr/script) vs
structural (forms genuinely share an argument shape).
New @form key -overlapallowed <formname-list> sanctions a KNOWN overlap on
either pair member: the finding reports with sanctioned 1 and leaves the
result's unsanctioned list (the actionable/gate subset). Parse behaviour is
never affected; unknown form names are rejected at definition resolve
(end-of-forms cycle). @form directive doc updated.
tclcore moduledoc 0.3.4: the documented after cancelid/cancelscript overlap
(runtime-liveness ambiguity, 0.3.0 record) sanctioned on the cancelid form -
::after now reports zero unsanctioned findings while 'after cancel <id-shaped>'
still raises multipleformmatches. ::lseq deliberately unsanctioned: formcheck
reports range/start_count (witness {1 count 1}) AND range/count (witness
{1 by 1}) - both real, both rooted in the expr-typed end slot, kept visible
pending an expr syntax-validating type (G-069/G-070).
Tests: new args/formcheck.test (7) - no-finding cases (parse withid/withdef
pair, afterish/sharedform fixtures), class+sanction fixture, unknown-form
rejection, -return summary, ::lseq/::after pins with witnesses re-verified,
sanction parse-neutrality. punk::args suite 210/210, punk::ns 57/57.
G-074 achieved and archived (acceptance review in the detail file); G-055
verification gate gains the formcheck step (unsanctioned must be empty for
regenerated multiform commands).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
An interactive shell orphaned by its hosting console dying (killed
conhost/terminal) previously spun ~2 cores forever: the Tcl 9 windows
console driver never delivers the dead-console state to the script as a
fileevent (tclWinConsole.c ConsoleEventProc only notifies on buffered
data) while its reader thread busy-loops on the persistent channel error,
so the script level is completely blind and nothing ever closes the
channel.
Fix: repl::console_watchdog - a self-rescheduling liveness poll (default
5s, repl::console_watchdog_ms; read-only GetConsoleMode probe via
chan configure -inputmode) armed by repl::start only for a tcl9 console
input channel serving the process-default console on windows. On probe
failure it closes the input channel (stopping the driver's reader
thread) and finishes the repl via the normal eof done-path; app-punkshell
then finds no console reopenable and exits cleanly. Piped, foreign-console
and tcl 8.6 inputs never arm it. repl::start's post-vwait reader
deregistration now tolerates a watchdog-closed channel.
Verification (2026-07-12): kill procedure post-fix exits the orphan in
1.5-2.0s with no traceback (src mode and rebuilt kits punkbi/punk91/
punk902z); 25s live-console soak shows no spurious trigger, CPU delta 0;
piped stdin unaffected; repl consolebackends 3/3 and punk::console suites
88 pass / 1 skip; root cause verified against plain tclsh 9.0.3.
Upstream ticket filed: https://core.tcl-lang.org/tcl/tktview/f10d91c2d3
Included:
- goals: G-039 flipped achieved 2026-07-12 and archived (index entry to
GOALS-archive.md, detail file to goals/archive/ with full root-cause,
repro and verification record); reference sweep adds the eof done-value
producer note to G-038
- src/modules/punk/AGENTS.md: watchdog contract documented
- project version 0.12.1 -> 0.12.2 + CHANGELOG entry
- vfs payload sync: _vfscommon.vfs punk/repl 0.4.0 -> 0.5.0
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Finalises the G-062 achieved flip staged by the prior session (punkshell
0.12.1): LICENSE.txt (BSD-2-Clause, Julian Marcel Noble 2023-2026) at the
repo root, README/punkproject.toml/AGENTS.md license declarations, index
record to GOALS-archive.md and detail file to goals/archive/.
Archive-loss prevention, from the G-015 incident (its achieved-status
verification parenthetical was silently dropped by the 2026-07-11 two-tier
restructure's Status-line normalisation; the text is restored to the G-015
archive file's Notes with provenance):
- root AGENTS.md 'Doc Restructures': deleted-line conservation check before
committing bulk doc migrations, structured-field-normalisation hazard
(relocate prose to body sections first), moves separated from edits
- goals/AGENTS.md: lintable header field grammar (one line per field, bare
Status values), flip-time verification evidence recorded in the body, the
archive move content-identical, and a new archive-time reference sweep:
pending-tense mentions of the archived goal updated in the live tier, and
actionable archived notes pointed to from the affected live goal's Notes
- GOALS.md maintenance rules: achieved-flip clause extended to require the
evidence write-up and the reference sweep
Retroactive reference sweep over all 12 archived goals (25 live detail
files): pending-tense fixes (G-002 G-008 G-013 G-016 G-017 G-021 G-022
G-031 G-042 G-044 G-050 - G-017's Goal/Acceptance annotated as
gate-satisfied, contract meaning unchanged), forward pointers pushed from
archived notes (G-062 license-seeding follow-on -> G-012/G-027, G-037
vendorlib/vfs drift semantics -> G-024, G-058 static-baseline mechanism ->
G-005), and one 'Archived-goal references' Notes bullet per remaining
referencing file so every live mention of an archived goal is resolvable
without consulting the archive index.
scriptlib/developer/goals_lint.tcl (new, plain tclsh): validates detail-file
header grammar, strict status values in both tiers, orphan rules in both
directions, Status/Scope mirror consistency, plus an informational (never
fatal) warning for live files referencing an archived goal with no
achieved/archive marker - baseline now zero warnings. Root AGENTS.md
scriptlib/developer ownership note widened to cover developer utilities.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Acceptance verified (consolebackends.test child-process drivers): repls run
interactively through ssh-socket and tk-widget ::opunk::Console subclasses
with size/at_eof/can_respond answered by the overrides, base class and
punk::console unchanged. Detail file records increment 2 and the non-gating
residue (per-run output batching, punk/0-only junction wiring, raw-mode
untested on foreign consoles, process-global console state -> G-008).
Project version 0.10.3 -> 0.11.0 with changelog entry (new user-visible
shell capability: repl::init -console).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- GOALS.md collapsed to summary entries (ID/status/title/Scope/detail pointer): 104.6KB -> 23KB so a full read stays cheap; Scope lines kept verbatim so path-grep goal discovery still works
- canonicality split: index canonical for ID/status/title/Scope, detail file canonical for Goal:/Acceptance:; all 61 detail-file headers reconciled from the index (fixes stale copies, e.g. G-036 detail acceptance predated the achieved rework), bodies preserved untouched
- detail files created for the three index-only goals G-017, G-034, G-054; every goal now has one
- 10 achieved goals (G-007 G-015 G-036 G-037 G-040 G-046 G-049 G-054 G-058 G-059) archived: summary records appended to GOALS-archive.md, detail files moved to goals/archive/; archive-on-flip replaces the ~30-achieved-entries trigger
- GOALS.md preamble/rules reworked: read workflow (read index in full, then detail files of scope-intersecting goals before editing), contract spans both tiers with proposal-first unchanged, next-free-ID counts the archive, achieved flip judged against the detail-file Acceptance and includes archiving
- progress convention codified from G-001 increment-1 practice: index entries carry status only, never progress; incremental progress lives in the detail file's ## Progress section (landed/remaining); achieved flip requires the remaining-work list resolved - a partial increment never flips a goal
- goals/AGENTS.md rewritten for the split (required 4-field header, one detail file per goal, orphan rules covering archive/); root AGENTS.md Child DOX Index + Repo-wide Notes updated; GOALS-archive.md preamble updated
- stale detail-file paths updated to goals/archive/ in src/lib/app-punkscript/punkscript.tcl (comment), runtimebash_wsl.test, wslprobe.tcl and the G-042 detail file; propagated copies (root lib/, src/vfs/_vfscommon.vfs, src/_build) left to refresh via normal build steps
- no project version bump: governance docs and source comments only, no user-visible shell behaviour
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Harness-agnostic technical project goal system. GOALS.md holds the canonical one-line index with format spec, status taxonomy, maintenance rules, and an agent-authoring workflow so agents asked to 'help me write a goal for X' ask the right questions. GOALS-archive.md is the achieved-goals archive stub. goals/AGENTS.md is the DOX child doc covering naming (G-<id>-<slug>.md), when a detail file is warranted, and the no-orphans/archive rule. goals/archive/.gitkeep tracks the archive dir.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Assisted-by: harness=opencode; primary-model=unknown; api-location=unknown