Skip to content

P0: background-server lifecycle + ownership records (#199), honest cancel labels (#728), 8 KB skill cap (#730), #729 tests - #732

Merged
justrach merged 4 commits into
mainfrom
fix/p0-issues
Sep 3, 2026
Merged

P0: background-server lifecycle + ownership records (#199), honest cancel labels (#728), 8 KB skill cap (#730), #729 tests#732
justrach merged 4 commits into
mainfrom
fix/p0-issues

Conversation

@justrach

@justrach justrach commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Closes #199, closes #730, addresses #728, adds the regression tests requested in #729.

#199 — background servers get an idle lifecycle, a pin, an ownership record, and a CLI (ADR 0062)

  • A background job that writes nothing and is read by nobody for 30 min gets one dim notice; after 2 h its whole process group is stopped and the job stays listed as idle-stop with its command kept. /jobs restart <id> reruns it in the same cwd. GRAFF_JOB_IDLE_WARN_MINS / GRAFF_JOB_IDLE_STOP_MINS tune it (0 = off).
  • /jobs keep <id> pins a job: exempt from the idle stop, and retained at session end (its pipes go to a detached drainer so the server never sees EPIPE) instead of killed. /jobs stop, /jobs unkeep too.
  • Every job writes ~/.codegraff/jobs/<pid>.json (leader pid + start identity, owner session, command, cwd, start time). graff servers lists them with state, age, owner alive/gone, listening ports (live lsof by process group), command; graff servers stop <pid> signals a group only while the leader's start identity still matches; graff servers prune drops dead records. Nothing graff did not start appears there.
  • /jobs shows age, pid, ports, pinned/idle-stop state. An idle stop is a step-boundary notice, not an idle-TUI auto-turn wake.
  • graff worktree … moved from jobs.zig to worktree_cmd.zig (file cap); callers unchanged.

#728 — a cancel with no recorded source is the harness's, not the user's (ADR 0063)

  • Every setter of the cancel flag records its source. A turn that ends cancelled with no recorded source is labelled [response ended early: cancelled by the harness, not the user] in the transcript, chrome, and --json error, and the trace notes interrupted source=… either way.
  • The raw stdin scanner swallows OSC replies (a terminal's colour/title answer) instead of reading their leading ESC as a keypress.
  • The injected job-exit notice in the report's sequence was already removed by fix(prompt,jobs): narrate in-band, ask via ask_user, no wake for an exit already read (ADR 0061) #727 (no wake for an exit the model has read). The false interrupt itself could not be reproduced from the report; the labelling and trace note make the next occurrence attributable.

#730 — SKILL.md over 8 KB stays in the catalog

  • The catalog head read used a failing limit instead of a truncating read, so any playbook over 8 KB was silently dropped. Regression test loads a 12 KB skill in full.

#729 — rendered-anchor regression tests

  • The reported target does not reproduce on the shipped GUI paths (strong parses before linkification). DOM-level tests now pin anchor text, href, and the open-link argument for a bold-wrapped URL. Adds @happy-dom/global-registrator as a gui dev dependency.

Tests: unit suite 1932 pass / 1 skip; tier 1 green; zig 0.16 + CI-pinned 0.17 + Linux and Windows cross-builds clean; gui bun test 208 pass.

The catalog read used readFileAlloc(.limited(head_cap)), which fails with
StreamTooLong past the cap instead of truncating, so every playbook over
8 KB was silently dropped and `skill <name>` could not load it. Read the
head with a truncating readFile; the named load still reads the whole file.
…'s (#728)

Every esc_cancel setter goes through cancel_source.cancel(source). Mainloop
labels a turn cancelled with no recorded source as ended early by the
harness instead of "[response interrupted by user]", and notes the source
in the trace either way. The raw stdin scanner swallows OSC replies (a
terminal's colour or title answer) instead of reading their ESC as a
keypress. ADR 0063.
)

A background job silent and unread for 30m gets one notice; after 2h its
process group is stopped and the job stays listed with its command, and
/jobs restart reruns it. /jobs keep pins a job: no idle stop, and at
session end it is retained (pipes handed to a detached drainer) instead
of killed. Every job writes ~/.codegraff/jobs/<pid>.json with the leader's
pid and start identity; graff servers lists them with age, owner, and
listening ports, and stops one only while that identity still matches.
GRAFF_JOB_IDLE_WARN_MINS / GRAFF_JOB_IDLE_STOP_MINS tune the policy. The
graff worktree commands moved to worktree_cmd.zig (file cap). ADR 0062.
#729)

The reported target could not be reproduced on the shipped GUI paths; these
DOM-level tests pin the rendered anchor text, href, and open-link argument
for a bold-wrapped URL. Adds @happy-dom/global-registrator as a dev
dependency for DOM tests.
@justrach
justrach merged commit 7066bbb into main Sep 3, 2026
6 checks passed
@justrach
justrach deleted the fix/p0-issues branch September 3, 2026 06:43
justrach added a commit that referenced this pull request Sep 3, 2026
…Streamdown and prompt recall

Both sides rewrote the native harness after the 285 tag: origin landed
follow-tail scrolling, the prompt queue, frame-rate ACP paint and the
ChatBubbles/ChatEmpty split; this branch landed the Streamdown renderer
and ArrowUp prompt recall. Resolution keeps origin's harness and scroll
model, the Streamdown Markdown.tsx (its remark pipeline supersedes the
hand parser's ordered-list and caret fixes), and threads the recall
history through the composer and the empty state.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SKILL.md playbooks larger than 8 KB are silently dropped from the catalog Auto-pause and stop Codegraff-started localhost servers after inactivity

1 participant