• .claude/skills/javascript/SKILL.md

    From Rob Swindell (on Debian Linux)@1:103/705 to Git commit to main/sbbs/master on Thursday, June 04, 2026 19:01:13
    https://gitlab.synchro.net/main/sbbs/-/commit/ee6689ca9a399aa3c5ad1cc6
    Modified Files:
    .claude/skills/javascript/SKILL.md
    Log Message:
    javascript skill: document js.on_exit() scope + forced-termination semantics

    js.on_exit() evaluates its handler string in the script's GLOBAL scope (it compiles the string against the global object), so a handler function nested in another function is unreachable and throws silently at exit. It also fires on forced termination (operator-terminate / SIGTERM), which -- unlike a thrown exception -- does NOT run catch/finally (verified empirically via SIGTERM + file markers). New 'Script lifecycle: exit handlers' section covers the global-scope requirement, the finally-vs-on_exit distinction, and the out-of-process testing caveat.

    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)
  • From Rob Swindell (on Debian Linux)@1:103/705 to Git commit to main/sbbs/master on Thursday, June 04, 2026 19:01:13
    https://gitlab.synchro.net/main/sbbs/-/commit/a915d2d944ed7b316e36e030
    Modified Files:
    .claude/skills/javascript/SKILL.md
    Log Message:
    javascript skill: correct js.on_exit scope (door child-scope vs jsexec)

    The earlier note said on_exit evaluates "in the GLOBAL scope" -- true only for jsexec/login/timed-event modules (host evaluates against js_glob and recurses child scopes). Doors and most bbs.exec/;exec/js.exec invocations run in a fresh child js_scope (exec.cpp:595) and the host evaluates on_exit against THAT scope (exec.cpp:701) with no recursion. Registration captures the scope where js.on_exit is called -- top level = js_scope, inside a function = that function's call object -- so a handler registered inside main() is filed where a
    door's EvalOnExit(js_scope) never looks and silently never runs, even though the
    same code passes under jsexec (which recurses). This shipped as a live bug.

    Rewrite: register on_exit at top level (not inside a function); prefer try/finally for clean-unwind cleanup (a door disconnect is a clean unwind) and use on_exit only as the forced-terminate backstop; note that a jsexec wrapper does not reproduce a door's on_exit scope, so finally is the dependable path.

    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)
  • From Rob Swindell (on Debian Linux)@1:103/705 to Git commit to main/sbbs/master on Saturday, June 06, 2026 15:23:28
    https://gitlab.synchro.net/main/sbbs/-/commit/90570a43c9e774e639c3ab04
    Modified Files:
    .claude/skills/javascript/SKILL.md
    Log Message:
    skills/javascript: document inkey timed input + inactivity model

    Add a "Timed / non-blocking key input, and the inactivity model"
    subsection to the input/output coverage: console.inkey(mode, timeout)
    semantics (timeout in ms; K_NONE returns "" vs K_NUL returns null on
    timeout; 1-char string on a key), the getkey-enforces-idle /
    inkey-doesn't trap, and the live console inactivity properties (max_getkey_inactivity, getkey_inactivity_warning, last_getkey_activity). Verified against inkey.cpp, getkey.cpp, js_console.cpp, scfglib1.c (NOINP=0x0100, default max_getkey_inactivity=300).

    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)
  • From Rob Swindell (on Debian Linux)@1:103/705 to Git commit to main/sbbs/master on Sunday, June 07, 2026 18:41:23
    https://gitlab.synchro.net/main/sbbs/-/commit/e9e5884dba659e4eec2e8ee0
    Modified Files:
    .claude/skills/javascript/SKILL.md
    Log Message:
    skills/javascript: load() caller-scope trap + uselect/printfile/directory gotchas

    Lessons captured from Synchronet door work:
    - load('file') runs in the CALLER's scope, so its top-level vars become locals of
    whatever function called load() -- a top-level / on_exit handler can't see a
    value load()ed inside main(); capture it into a reachable scope (the silent
    "quetzal is not defined" ReferenceError).
    - console.uselect: the title is auto-prefixed with "Select " (pass "a Game", not
    "Select a Game"); the display call's number argument is the DEFAULT item index.
    console.line_counter = 0 discards a pending auto-pager prompt before a clear. - console.printfile renders Ctrl-A codes + ANSI/CP437 by default; P_PCBOARD is
    only for PCBoard @X codes (it would misread a literal @).
    - directory() defaults to GLOB_MARK, so directory entries come back with a trailing
    '/' (self-identifying). The CWD is process-global (always ctrl/, since sbbs is
    multi-threaded) -- which is why no chdir is exposed to BBS JS; build absolute
    paths from js.exec_dir / system.*_dir.

    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)
  • From Rob Swindell (on Debian Linux)@1:103/705 to Git commit to main/sbbs/master on Thursday, June 11, 2026 15:38:02
    https://gitlab.synchro.net/main/sbbs/-/commit/4804fef3b8d8cbf70a850169
    Modified Files:
    .claude/skills/javascript/SKILL.md
    Log Message:
    javascript skill: document terminal-control abstraction + K_CTRLKEYS vs K_EXTKEYS

    Two lessons from the Z-Machine v6 door work:

    - Terminal control sequences (cursor show/hide, etc.) are abstracted in
    ansiterm_lib.js (CSI ?25h/l via ansiterm.send) -- reach for that before
    hand-writing escape codes.

    - Function/cursor key input: inkey/getkey pre-translate the arrow/Home/End
    escapes into control codes (TERM_KEY_*), conflating them with Ctrl-letters.
    K_EXTKEYS still yields those conflated codes and has no F-key codes; K_CTRLKEYS
    passes control keys through AND leaves the escape sequences RAW so you can parse
    the cterm forms yourself (ESC[A/B/C/D, ESC[11~..24~) and keep arrows/F-keys
    distinct. getbyte() is the raw alternative but drops idle-disconnect + UTF-8.

    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)
  • From Rob Swindell (on Debian Linux)@1:103/705 to Git commit to main/sbbs/master on Monday, July 06, 2026 20:44:06
    https://gitlab.synchro.net/main/sbbs/-/commit/371c3e49b542335fac8e210c
    Modified Files:
    .claude/skills/javascript/SKILL.md
    Log Message:
    javascript skill: five API gotchas learned from real module work

    - MsgBase.remove_msg(): passing a header object silently returns false
    with an empty last_error; pass the message number/offset/ID.
    - bbs.text() accepts the string identifier (via text_id_map), and a
    text.dat format string can carry more %-specifiers than the name
    suggests (NodeMsgFmt has three, including the message body) -- count
    them in ctrl/text.dat before format()ing, or the leftover %s renders
    literally.
    - bbs.compare_ars() evaluates the current user only; use the User
    object's compare_ars() to test any user (e.g. other nodes' useron),
    plus how to find a program's execution_ars and enumerate in-use nodes.
    - Emit line breaks with console.newline() rather than a hand-written
    "\r\n", same terminal-correctness principle as console.creturn().
    - Don't copy P_NOPAUSE onto printfile() calls reflexively: it disables
    the default pagination, which is only right when the surrounding flow
    provides the pause.
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)