• .claude/skills/javascript/SKILL.md

    From Rob Swindell (on Debian Linux)@1:103/705 to Git commit to main/sbbs/master on Tuesday, July 21, 2026 21:53:27
    https://gitlab.synchro.net/main/sbbs/-/commit/8c2fba616c53be965f97ca60
    Modified Files:
    .claude/skills/javascript/SKILL.md
    Log Message:
    javascript skill: document the 512-byte line cap on readAll/readln

    File.readAll() and File.readln() both default to maxlen=512. Because readAll
    is a loop over readln, and readln is fgets(buf, len+1, fp), an over-long line is SPLIT rather than truncated: fgets leaves the remainder in the stream, so
    it comes back as the next array element. The array quietly desynchronizes
    from the file's lines, nothing throws, and f.error stays 0.

    Hit this reading a 20MB data/gitpush.jsonl whose largest payload is ~160KB: a scan for the longest line reported exactly 512 bytes, and JSON.parse threw on the fragments. Only files whose lines all fit in 512 bytes behave correctly, which is what makes it look intermittent.

    Also note that load('json_lines.js') is the right tool for .jsonl and
    .ndjson, but that its own max_line_len default is 4096 -- still too small for records this size, so it has to be passed explicitly.

    Three baseline agents given the un-amended skill all got this right, but each did it by reading js_file.cpp first; the section is here to save that detour, and to carry the json_lines.js default, which none of them had reason to hit. --- 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 Tuesday, July 28, 2026 02:32:26
    https://gitlab.synchro.net/main/sbbs/-/commit/74911bb1379078e52a52f4ca
    Modified Files:
    .claude/skills/javascript/SKILL.md
    Log Message:
    javascript skill: document the ini's "key : value" string literals

    A key separated from its value by a COLON rather than an equals sign makes
    the value a C string literal: xpdev/ini_file.c's key_name() runs it through c_unescape_str(). So an .ini can carry Ctrl-A attribute codes, CP437 bytes
    and control characters directly, spelled as ctrl/text.dat spells them, and
    no JS-side unescape function is needed. Written up because I was about to hand-roll one.

    Records the escape set (\xNN hex, \NNN decimal -- not octal, unless built
    with C_UNESCAPE_OCTAL_SUPPORT), which readers unescape (iniGetString and
    the named-string-list readers behind File.iniGetObject()), that the
    separator is whichever of = or : comes first on the line -- so dir = C:\roms
    is unaffected and the two forms mix freely -- that typed reads still split
    on : but skip the unescape, and that : preserves trailing whitespace where
    = trims it. Verified against a live install with jsexec.

    Sits beside the existing trailing-comment trap in the same section, which
    still applies: a ; comment after a string value is not stripped under
    either separator.

    Co-Authored-By: Claude Opus 5 (1M context) <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, August 09, 2026 07:19:11
    https://gitlab.synchro.net/main/sbbs/-/commit/850de49a4c13adc69c000667
    Modified Files:
    .claude/skills/javascript/SKILL.md
    Log Message:
    javascript skill: a modified header must be read unexpanded

    put_msg_header() refuses a header fetched with field expansion, and the
    error it gives -- "Message header has 'expanded fields'" -- describes
    the header rather than the call that produced it, so it reads as "this
    message cannot be rewritten" instead of "your read was wrong". Since
    expansion is the default, the obvious spelling of a read-modify-write
    fails and the fix is not in the failing call.

    Documents the expand argument on get_msg_header(), notes that the same
    applies to headers taken from get_all_msg_headers(), and says to leave expansion on for read-only callers -- it is what resolves the fields
    they want. Also points at setting the field at save_msg() time, which
    avoids the round trip where the original write is yours.

    Sits with the sibling traps the section already carries: remove_msg()
    silently returning false when handed a header object, and the lazy-field gotcha. All three are calls that fail for a reason the symptom points
    away from.

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)