Skip to content

feat: ctx pad undo, out-of-band audit channel (ctxctl), OS-native memory pressure#104

Merged
josealekhine merged 9 commits into
mainfrom
feat/pad-undo-snapshot
May 29, 2026
Merged

feat: ctx pad undo, out-of-band audit channel (ctxctl), OS-native memory pressure#104
josealekhine merged 9 commits into
mainfrom
feat/pad-undo-snapshot

Conversation

@josealekhine

Copy link
Copy Markdown
Member

Summary

This branch lands four independent pieces of work that grew together
on a single feature branch:

  1. ctx pad undo — a snapshot-on-mutate safety net for the
    encrypted scratchpad.
  2. An out-of-band audit channel, shipped as a separate
    maintainer-only binary (ctxctl)
    rather than inside the
    user-facing ctx binary.
  3. OS-native memory-pressure detection in the resource hook,
    replacing broken occupancy-percentage triggers.
  4. Typed, text-free audit errors with rendering moved to the
    ctxctl edge.

Each is described below. The audit channel evolved within the branch
(it was first built inside ctx, then extracted into ctxctl); the
net state delivered to main is described here, not the
commit-by-commit churn.

What's in this PR

1. ctx pad undo — scratchpad safety net (Phase 1)

Spec: specs/pad-undo-snapshot.md

  • Every destructive ctx pad operation snapshots the prior pad blob
    to .context/scratchpad.history/ before overwriting.
  • New ctx pad undo subcommand restores the most recent snapshot.
  • Snapshots are the existing pad bytes byte-for-byte (no
    re-encryption), so plaintext and encrypted modes get the same net.
  • Bounded retention ring: 20 snapshots or 30 days, whichever trips
    first. Prune failures degrade to a warning; the safety net never
    blocks a write.
  • undo is itself a snapshotting mutation, so ctx pad undo; ctx pad undo acts as a redo. Empty history prints a friendly message and
    exits 0 (quiet on fresh projects).
  • Phase 2 (--list / --to / --prune / --clear, .ctxrc
    retention tuning, skill/recipe docs) is tracked separately.

2. Out-of-band audit channel via ctxctl

Specs: specs/audit-channel.md, specs/ctxctl-bootstrap.md

The audit channel decouples discipline enforcement from the in-band
commit cadence: a separate Claude Code session runs an audit skill,
drops a structured report into .context/audit/<kind>.md, and a
UserPromptSubmit hook verbatim-relays it on the next turn.

It lives in ctxctl, a maintainer/contributor binary kept out of
the shipped ctx binary, so end users never carry an audit hook they
have no producer for.

  • Separate Go module at tools/ctxctl/ (module
    github.com/ActiveMemory/ctx/tools/ctxctl); its go.mod requires
    the ctx module via replace ../.., and the repo-root go.work
    links both. ctx's go.mod never requires ctxctl, so ctx can
    never import it.
  • Commands: ctxctl audit list | show | dismiss plus the
    ctxctl audit-relay hook.
  • ctxctl owns its user-facing English as plain Go constants (no YAML
    i18n / desc engine).
  • _ctx-surface-audit skill + docs/recipes/audit-channel.md show
    the pattern (ctx dogfoods an internal auditor over its own
    internal/ tree).
  • Makefile: make ctxctl, make install-ctxctl,
    make reinstall-ctxctl (installs to /usr/local/bin/ctxctl).
  • Import-graph guard (internal/compliance/ctxctl_isolation_test.go)
    asserts cmd/ctx depends on no internal/ctxctl/... package and
    that the shipped hooks.json carries no audit hook.

3. OS-native memory pressure

No dedicated spec — correctness fix; rationale in .context/DECISIONS.md (2026-05-27)

Replaces the broken occupancy-percentage DANGER triggers
(swap_used/total, memory_used/total) with the kernel's own
pressure signal:

  • macOS: sysctl kern.memorystatus_vm_pressure_level.
  • Linux: /proc/pressure/memory PSI (some.avg10 >= 10 ->
    Warning, full.avg10 >= 10 -> Danger).
  • Other platforms: report PressureSupported=false (Windows
    detection filed as a follow-up).
  • The dead, always-OK doctor swap row and its orphaned constants are
    removed; PSI thresholds are named in config/stats for one-place
    retuning.

4. Typed audit errors

Spec: specs/ctxctl-bootstrap.md

internal/ctxctl/err/audit returns typed, text-free errors
(ReadReportError, ParseReportError, WriteDismissalError,
ReadDismissalError, UnknownIDError, plus ErrIDRequired /
ErrNoFrontmatter / ErrUnterminatedFrontmatter sentinels). The
user-facing English moved to tools/ctxctl, which renders the errors
at the command edge through a sole printer (SilenceErrors on the
root, mirroring ctx's internal/write/err.With). This brings the
implementation into full alignment with the spec's "all ctxctl text
lives under tools/ctxctl" rule.

Docs

  • docs/recipes/audit-channel.md + recipes index: the channel is
    maintainer-only via ctxctl, with a repo-local hook.
  • New "Maintainer Tooling: ctxctl" section in
    docs/home/contributing.md documenting build/install.
  • Rendered docs site (site/**) and Atom feed regenerated.

Testing

  • make lint: 0 issues (ctx module) and 0 issues (ctxctl
    module).
  • make test: green (ctx module); tools/ctxctl module tests pass.
  • go build clean on both modules.
  • Behavioral coverage: pad snapshot/undo/retention; audit
    list/show/dismiss/relay (incl. dismissal-digest re-surfacing and
    stale-report prefixing); audit error rendering (unknown id, missing
    id, malformed-report parse path); per-platform pressure parsing.

Notes for reviewers

  • The audit channel is not in the shipped ctx binary — it is
    ctxctl, a separate module. Reviewing the net tree is easier than
    the add-then-extract commit sequence.
  • tools/ctxctl is a second module; CI / local checks must cover it
    in addition to the main module.
  • .gitignore un-ignores go.work / go.work.sum (now tracked for
    the workspace) and ignores the built ctxctl binaries.

Follow-ups (tracked in .context/TASKS.md)

  • Realign the installed plugin's hooks.json with the cwd-anchored
    binary (fixes a per-prompt help-dump from version skew) — high.
  • ctx system <unknown>: emit a verbatim relay instead of a silent
    help dump + exit 0 — medium.
  • Windows-native memory-pressure detection — medium/exploratory.
  • ctx <add> --json ingest (design parked in
    specs/ctx-add-json-ingest.md) — medium.
  • ctx pad undo Phase 2 flags + retention tuning.

Every destructive `ctx pad` operation now writes the prior pad
blob to .context/scratchpad.history/ before overwriting, and a
new `ctx pad undo` subcommand restores the most recent snapshot.
Snapshots are the existing pad bytes byte-for-byte (no
re-encryption), so plaintext and encrypted modes both get the
safety net the same way. Retention is a bounded ring: 20
snapshots or 30 days, whichever trips first. Prune failures
degrade to a warn; the safety net never blocks a write.

The undo command is itself a mutation that snapshots first, so
`ctx pad undo; ctx pad undo` yields a redo. Empty history prints
a friendly message and exits 0 (cron-quiet on fresh projects).

Wired through the existing i18n pipeline: new DescKeys in
internal/config/embed/text/{err_pad,pad}.go with matching
translations in internal/assets/commands/text/{errors,write}.yaml.
Warn formats live in internal/config/warn (cfgWarn.PadHistory*)
per project convention.

Phase 2 (--list, --to, --prune, --clear flags + .ctxrc retention
tuning + skill/recipe doc updates) tracked separately under the
same TASKS.md entry.

Also bundled (session bookkeeping; no separate spec since these
inherit this commit per spec-trailer-discipline §bundle-rule):
- TASKS.md completion annotations for 7 already-shipped items
  (lines 393 ConstantTimeCompare, 406 input validation, 427
  connect listen, 442 ctx backup deprecation, 482 GLOSSARY
  append, 543 ctx-architecture-failure-analysis) plus the
  skip-with-reason annotation for line 463 (publish
  architecture docs) and the line-543 structural cleanup.

Tests: 6 new cases in internal/cli/pad/pad_test.go covering
first-write-no-snapshot, snapshot-preserves-exact-bytes,
undo-restores-pre-mutation, undo-is-itself-snapshotted (redo),
empty-history-exits-zero, plaintext-mode round-trip.

Spec: specs/pad-undo-snapshot.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
The previous commit (6bcaf88) shipped `ctx pad undo` and the
snapshot safety net but labeled SKILL.md and recipe updates as
"Phase 2" — which is exactly the deferral the Constitution
forbids ("I can create a follow-up task"). User caught this at
review. This commit closes the gap and installs a persistent
rule so future agents see the requirement at session start.

User-facing surface caught up:
- internal/assets/claude/skills/ctx-pad/SKILL.md: add `undo` to
  the command-mapping table and a dedicated Execution entry
  documenting redo-by-double-undo and empty-history exit shape.
- docs/recipes/scratchpad-with-claude.md: add `ctx pad undo`
  to the commands table and a new "If You Delete the Wrong
  Thing" section covering snapshot-on-mutate semantics,
  redo-by-double-undo, key-loss caveat, and retention bounds.

Persistent rule (CONVENTIONS.md → "User-Facing Surface
Completeness"): enumerates the docs surfaces that must be
updated in the same commit as any user-facing change
(commands.yaml + examples.yaml + SKILL.md + integrations
skills + recipes + docs/cli pages) and explicitly forbids
"Phase 2" deferral of docs. CONVENTIONS.md loads into agent
context at session start, so the rule is in front of every
future agent's eyes.

Captured context:
- DECISIONS.md: snapshot lives at the store.WriteEntriesWithIDs
  choke point (not per-subcommand), with byte-for-byte copy of
  the existing pad blob (no re-encryption), so plaintext and
  encrypted modes share the safety net.
- LEARNINGS.md: audit gates around new packages —
  TestNoMixedVisibility forces <name>_internal.go splits,
  TestNoMagicStrings requires warn formats in
  internal/config/warn/, TestDocCommentStructure demands full
  Parameters/Returns blocks on every helper.

TASKS.md: Phase 1 entry flipped to [x] with commit ref
(6bcaf88); Phase 2 unchanged.

Spec: specs/pad-undo-snapshot.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
Captures the design for moving discipline enforcement off the
in-band code path (CONVENTIONS prose that didn't survive the
pad-undo Phase 1 commit's tunnel vision) onto the only
discipline channel in this codebase that empirically does
survive agent tunnel vision: verbatim relay.

Shape:
- An out-of-band auditor (a different Claude Code session,
  human-triggered, runs on the user's plan-billed CC instead
  of API to avoid per-commit cost) invokes a skill like
  /ctx-surface-audit.
- The skill drops a structured report at
  .context/audit/<kind>.md (yaml frontmatter + verbatim body).
- A UserPromptSubmit hook `ctx system checkaudit` reads the
  audit directory and verbatim-relays each not-yet-dismissed
  report in the same ┌─ ... ─┐ envelope as `ctx remind`,
  `ctx system checkknowledge`, and the journal/knowledge
  notices.
- Dismissal mirrors `ctx remind`: `ctx audit list / show /
  dismiss / dismiss --all`.

Central trust boundary: the auditor refuses to run inside a
session with uncommitted working-tree changes to the audit
target range — the implementer cannot grade their own
homework. Fresh-context judgment is the point.

Phase 1 ships the channel + the first skill
(/ctx-surface-audit). Phase 2 adds auto-dismissal on
detected resolution and sibling audit skills
(/ctx-spec-trailer-audit, /ctx-capture-audit, ...).

Bundled here as the third commit on feat/pad-undo-snapshot
because the spec grew directly out of the doc-gap fixup in
commit 77fa01f — the audit channel is the structural fix
for the failure mode that commit only papered over with a
CONVENTIONS.md prose rule.

Spec: specs/audit-channel.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
…hook (Phase 1a)

Stand up the audit channel sketched in specs/audit-channel.md:
the structural substrate that lets out-of-band auditor sessions
(a separate Claude Code instance, run on the user's plan to
avoid per-commit API cost) drop structured reports into
`.context/audit/<kind>.md`, and that surfaces those reports to
the next in-session interaction via the only discipline channel
in this codebase that empirically survives agent tunnel vision:
verbatim relay.

Phase 1a deliverables:

- `ctx audit` CLI: `list` (default), `show ID`, `dismiss
  [ID...]` or `dismiss --all`. Dismissals are bound to the
  report digest at dismiss time so a fresh audit run with new
  findings re-surfaces the report. Mirrors `ctx remind` shape
  and registers in internal/bootstrap/group.go under the
  sessions group.
- `ctx system check-audit` UserPromptSubmit hook: reads every
  report under `.context/audit/`, skips status:clean and
  dismissed-against-current-digest, renders each remaining
  body inside the standard nudge box (auditRender package),
  and prefixes reports older than [cfgAudit.StalenessAge] (30
  days) with a STALE marker. Uses the same nudge.LoadAndEmit
  machinery as check-reminder.
- Report format: YAML frontmatter (kind/status/commit-range/
  generated-at/generator/digest) + verbatim body. Parsed by
  internal/cli/audit/core/parse with sentinel
  ErrNoFrontmatter / ErrUnterminatedFrontmatter in
  internal/err/audit.
- Persistence: reports at `.context/audit/<id>.md`, dismissal
  ledger at `.context/audit/.dismissed.json` (NOT under
  `.context/state/` — a user nuking state should not silently
  re-surface dismissed audits, per spec Open Question #2).
- Full i18n plumbing: 8 error keys, 4 writer keys, 7
  check-audit hook keys, plus matching descriptors in
  commands.yaml / examples.yaml / flags.yaml / hooks.yaml /
  registry.yaml.
- Tests: 8 CLI tests (list/show/dismiss happy paths +
  unknown-id + no-ids-no-all + fresh-digest-resurfaces); 4
  parser tests (round-trip + sentinel errors + body
  byte-preservation); 5 hook tests (no-leak invariant +
  silent-on-clean + relays-findings-body + silent-when-
  dismissed + STALE-prefix-on-old-report).

Phase 1b (separate commit, per the User-Facing Surface
Completeness convention added in 77fa01f): the first auditor
skill (/ctx-surface-audit SKILL.md with refuse-on-dirty-tree
guard) + recipe touch-ups. Phase 2 (separate spec / commits):
auto-dismissal on detected resolution, sibling audit skills
(/ctx-spec-trailer-audit, /ctx-capture-audit), and stale-
report graceful escalation.

Bundled as the fourth commit on feat/pad-undo-snapshot because
this work grew directly from the doc-gap fixup in 77fa01f —
the audit channel is the structural fix for the tunnel-vision
failure mode that commit only papered over with a CONVENTIONS
prose rule.

Spec: specs/audit-channel.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
Complete the audit channel's user-facing surface, honoring the
User-Facing Surface Completeness convention (added 77fa01f):
a new user-facing capability is not done until its skill and
recipe land in the same body of work.

- internal/assets/claude/skills/ctx-surface-audit/SKILL.md:
  the first audit-family skill. Scans a git ref range
  (default main..HEAD) for user-facing surfaces — new
  subcommands, flags, behavior changes — that landed without
  matching SKILL.md / recipe / docs-cli coverage, and writes
  a structured report to .context/audit/surface.md. Central
  trust boundary: refuses to run in a session with a dirty
  worktree for the audit range (the implementer cannot grade
  their own homework); no override flag by design. Documents
  the report frontmatter shape, per-surface coverage checks
  (SKILL.md table → recipes → docs/cli → integrations), and
  the write-don't-remediate boundary.

- docs/recipes/audit-channel.md: end-to-end recipe — why a
  separate session (fresh-context judgment + plan-vs-API cost
  shape), the five-step workflow, digest-bound dismissal,
  retention/staleness, when to run, and the future
  audit-skill family (/ctx-spec-trailer-audit,
  /ctx-capture-audit).

- docs/recipes/index.md: register the recipe under Agents and
  Automation.

This is itself a live test of the channel: a /ctx-surface-audit
run against this branch should now report clean for the
`ctx audit` surface, since SKILL coverage (this skill) and
recipe coverage (this recipe) both exist.

Spec: specs/audit-channel.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
- TASKS.md: add the out-of-band audit channel entry with Phase 1a
  and Phase 1b marked [x] (commits aefce51, 71c3dfa) and Phase 2
  (auto-dismissal, sibling audit skills, settings.local.json hook
  wiring) pending.
- DECISIONS.md: record the architectural choice to put discipline
  enforcement on the verbatim-relay channel and run the auditor
  out-of-band — the rationale (relay survives tunnel vision; fresh-
  context judgment; cost on plan not API) and consequences (generic
  kind-agnostic channel, digest-bound dismissal, no automated
  trigger in Phase 1).
- LEARNINGS.md: extend the audit-gates entry with the fuller gate
  catalog hit while landing a whole new CLI command + hook
  (TestNoMagicValues, TestNoCmdPrintOutsideWrite, TestNoNakedErrors
  incl. sentinels, TestTypeFileConvention, TestCmdDirPurity,
  TestNoLiteralMdExtension, TestDocGoSubcommandDrift,
  TestDescKeyYAMLLinkage, TestRegistryCount, and the staticcheck
  QF1012 vs TestNoUncheckedFmtWrite tension).

Spec: specs/audit-channel.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
The audit channel shipped into the ctx binary in aefce51 is
maintainer tooling in the wrong home: the check-audit
UserPromptSubmit hook would fire on every prompt for every end
user, taxing them with filesystem reads for a feature they will
never produce reports for, and `ctx audit` bloats the user
command surface. The auditor (_ctx-surface-audit) only audits
ctx's own internal layout.

This spec stands up cmd/ctxctl — the maintainer/contributor
binary planned in TASKS.md Phase BT since 2026-03 but never
built because its build/release-script inhabitants were each
blocked or deferred — with the audit channel as a cleaner,
self-contained first inhabitant.

Key decisions captured:
- Same module (cmd/ctxctl), not a separate go.mod at
  tools/ctx/ctxctl. A separate module cannot cleanly import the
  parent's internal/ (the ~25 audit files); same-module still
  keeps audit out of the ctx binary via the import graph (only
  ctxctl's main imports it). Separate-go.mod's only win is dep
  isolation, which the audit channel does not need.
- Refines the Phase-BT rule "hooks must call ctx": shipped
  product hooks call ctx; repo-local dev hooks (ctx's own
  gitignored settings) may call ctxctl. The audit-relay hook
  is the latter.
- Migration moves wiring (cobra registration + hook entry) out
  of ctx; the internal logic packages stay put and are reused.

The deliberately-dirty internal/assets/claude/hooks/hooks.json
edit in the working tree is the burned bridge — left dirty as a
forcing function. This spec is where that trail ends: the
migration removes check-audit from the shipped hooks.json and
re-homes it as a repo-local ctxctl call. The implementation
tree is intentionally left mid-migration; only the plan
(this spec + the TASKS.md tracker) is committed here.

Spec: specs/ctxctl-bootstrap.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
- DECISIONS.md: cmd/ctxctl lives in the same Go module (not a
  separate go.mod) — binary-level import-graph isolation keeps
  audit out of the ctx binary while reusing internal/ verbatim;
  separate go.mod's only win (dep isolation) isn't needed yet.
- LEARNINGS.md: skill shipping location — _ctx- prefix in
  .claude/skills/ is repo-internal, internal/assets/claude/skills/
  is bundled and shipped; the ship-vs-repo-internal question
  generalizes to ctx-vs-ctxctl commands and shipped-vs-local hooks.
- handovers/: session handover for the next agent — implement
  specs/ctxctl-bootstrap.md; the 4-file dirty tree is an
  intentional burned-bridge forcing function, not forgotten work.

The migration implementation files (hooks.json, _ctx-surface-audit
SKILL.md, audit-channel recipe, recipes index) remain deliberately
uncommitted.

Spec: specs/ctxctl-bootstrap.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
Branch wrap-up commit for feat/pad-undo-snapshot (pad-undo Phase 1
shipped earlier on this branch). It bundles several arcs developed
together under the "we own the branch, one commit" plan, plus a few
unrelated items captured at the user's request. Grouped by area, with
the spec each serves.

ctxctl: separate maintainer module (per specs/ctxctl-bootstrap.md)
  The out-of-band audit channel leaves the shipped `ctx` binary
  entirely. Six logic trees (cli/audit, system/cmd/checkaudit,
  system/core/audit, config/audit, err/audit, write/audit) were
  relocated under internal/ctxctl/... with imports rewritten. A new
  separate Go module lives at tools/ctxctl/ (module path
  github.com/ActiveMemory/ctx/tools/ctxctl; its go.mod requires the
  ctx module via `replace ../..`; the repo-root go.work links both),
  so the ctx module never requires ctxctl and can never import it.
  tools/ctxctl wires `ctxctl audit list|show|dismiss` plus the
  `ctxctl audit-relay` hook (renamed from check-audit). The audit
  channel's English lives as plain Go constants under tools/ctxctl
  (no YAML i18n, no desc engine); the ctx-side audit YAML keys and
  DescKey* constants were deleted. The Makefile gains ctxctl,
  install-ctxctl and reinstall-ctxctl. An import-graph guard
  (internal/compliance/ctxctl_isolation_test.go) asserts cmd/ctx
  depends on no internal/ctxctl/... package and that the shipped
  hooks.json carries no check-audit. The repo-local UserPromptSubmit
  hook invokes `ctxctl audit-relay`.

Typed audit errors (per specs/ctxctl-bootstrap.md)
  internal/ctxctl/err/audit now returns typed, text-free errors
  (ReadReportError, ParseReportError, WriteDismissalError,
  ReadDismissalError, UnknownIDError, plus the ErrIDRequired,
  ErrNoFrontmatter and ErrUnterminatedFrontmatter sentinels). The
  user-facing English moved to tools/ctxctl, which renders the errors
  at the command edge through a sole printer (SilenceErrors on the
  root, mirroring ctx's internal/write/err.With). This closes the one
  place ctxctl text still lived in internal/, bringing the code into
  full alignment with the spec.

OS-native memory pressure (correctness fix; no dedicated spec, see
DECISIONS.md 2026-05-27)
  Replaces the broken occupancy-percentage DANGER triggers
  (swap_used/total, memory_used/total) with the kernel's own pressure
  signal. macOS reads kern.memorystatus_vm_pressure_level; Linux
  parses /proc/pressure/memory PSI (some.avg10 >= 10 -> Warning,
  full.avg10 >= 10 -> Danger); other platforms report
  PressureSupported=false. The dead, always-OK doctor swap row and its
  orphaned constants are removed. PSI thresholds are named in
  config/stats for one-place retuning.

check-anchor-drift cleanup (per specs/experiments/acdl-session-start.md)
  Primary-source archaeology proved check-anchor-drift was a
  deliberately-retired feature (deleted in fc7db22), not a phantom;
  its stale commands.yaml row is pruned. Follow-up tasks were filed:
  plugin hooks.json version-skew fix, `ctx system` unknown-subcommand
  verbatim relay, and Windows memory-pressure exploration.

Docs
  docs/recipes/audit-channel.md and the recipes index reframe the
  channel as maintainer-only via ctxctl with a repo-local hook; a new
  "Maintainer Tooling: ctxctl" section in docs/home/contributing.md
  documents build and install (make ctxctl / install-ctxctl /
  reinstall-ctxctl). The rendered docs site (site/**) and feed were
  regenerated.

Specs added
  specs/ctx-add-json-ingest.md (design for `ctx <add> --json` ingest,
  parked) and specs/experiments/acdl-session-start.md.

Tooling and context (dogfooding; unrelated items bundled per request)
  .gitignore un-ignores go.work/go.work.sum (now tracked for the
  workspace) and ignores the built ctxctl binaries (/ctxctl,
  tools/ctxctl/ctxctl). CLAUDE.md and AGENTS.md gain a GitNexus
  code-intelligence section. TASKS, DECISIONS, LEARNINGS and
  CONVENTIONS were updated.

Verification: both modules pass `make lint` (0 issues) and `make test`
(green); `go build` is clean on each.

Spec: specs/ctxctl-bootstrap.md
Spec: specs/experiments/acdl-session-start.md
Signed-off-by: Jose Alekhinne <jose@ctx.ist>
@josealekhine josealekhine self-assigned this May 29, 2026
@josealekhine josealekhine requested a review from bilersan as a code owner May 29, 2026 01:31
@josealekhine josealekhine merged commit f36b19e into main May 29, 2026
16 checks passed
@josealekhine josealekhine deleted the feat/pad-undo-snapshot branch May 29, 2026 01:36
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.

1 participant