type: message

role: user

type: text

text: i think closing laptop disconnects ssh session which pauses llm excecution. right?


role: assistant

text:
Usually yes.

If your laptop is the SSH client and you close it, one of these happens:

- laptop sleeps
- Wi‑Fi drops
- SSH connection dies
- the shell gets a hangup
- the foreground LLM/process often stops or gets interrupted

What happens exactly depends on how the job was started:

- plain interactive shell: likely interrupted
- inside `tmux` or `screen`: keeps running on the server
- started with `nohup`, `systemd`, or as a background service: keeps running
- some tools survive disconnects poorly even if backgrounded, unless they were designed for it

For this machine, since `ballbox-first` is the server and workstation, the fix is usually:

- start long jobs in `tmux`
- or run them as a service/script detached from your SSH session

Quick rule:
- no `tmux`/detach => assume closing laptop kills the session
- with `tmux` => safe to reconnect later

If you want, I can give you the exact `tmux` workflow I’d use for long LLM runs on this Pi.

api: openai-codex-responses

provider: openai-codex

stopReason: stop


text: yes so set up simple workflow to avoid stopping terminal (including changing `bb` alias in laptop)


read

type: toolCall

bash

command: printf 'shell=%s\n' "$SHELL"; command -v bb || true; alias bb 2>/dev/null || true; rg -n "alias bb|function bb|bb\(" /home/sebas -g '!**/.git/**' 2>/dev/null | head -200

stopReason: toolUse


role: toolResult

toolName: read

text:
# Personal Agent

## Communication

- Follow the system communication defaults.
- Preserve user intent and technical accuracy.
- Keep responses brief, clear, and action-oriented unless the user asks for more detail.
- **Pending validation:** confirm this communication mode auto-loads in every new Pi session.

## Intent

- Prioritize business impact.
- Reframe technical work toward the real objective.
- Ask only questions that change scope, risk, or success criteria.
- Execute fully once clarified.
- Keep the main thread focused.
- Use subagents for recon, parallel work, or context isolation when useful.
- Use Agents Database as durable shared memory, not just search.
- Save high-signal preferences, decisions, project facts, reusable artifacts, and concise handoff context when likely useful beyond the current chat.
- Prefer a few strong memories over many noisy ones.
- This Pi config repo is the main place to evolve these rules going forward.

## Rules

- Irreversible actions require explicit confirmation.
- Never merge or push to main without user confirmation.
- Keep responses direct and low-noise.
- Keep code blocks, commands, commits, and PR text in normal technical form unless the user asks otherwise.
- Point out contradictions when they matter.

## Machine Context

- **This machine is `ballbox-first`.**
- Do **not** SSH into `ballbox-first` for normal local work. You are already on it.
- This Raspberry Pi is effectively both workstation and server for Sebas's day-to-day work.
- "el servidor" usually refers to this same machine unless the user clearly means another host.

## Server Details

- Hostname: `ballbox-first.emperor-ratio.ts.net`
- Tailscale IP: `100.116.176.16`
- 24/7 machine
- Nginx on `:80` routes to other local services
- `personal-agent` API: `http://100.116.176.16:8082/api/status`
- `agents-database` API: `http://100.116.176.16:8091/api/status`

## Playwright

- `playwright-mcp` CLI available.
- Prefer headless Chromium.
- Avoid Firefox/WebKit unless needed.

## Telegram

Required vars:
- `TELEGRAM_BOT_TOKEN`
- `TELEGRAM_CHAT_ID`

Notify command:
```bash
~/.agents/skills/telegram-notify/telegram-notify "Message"
```


text:
# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth

# append to the history file, don't overwrite it
shopt -s histappend

# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000

# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize

# If set, the pattern "**" used in a pathname expansion context will
# match all files and zero or more directories and subdirectories.
#shopt -s globstar

# make less more friendly for non-text input files, see lesspipe(1)
#[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"

# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
    debian_chroot=$(cat /etc/debian_chroot)
fi

# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
    xterm-color|*-256color) color_prompt=yes;;
esac

# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
force_color_prompt=yes

if [ -n "$force_color_prompt" ]; then
    if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
	# We have color support; assume it's compliant with Ecma-48
	# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
	# a case would tend to support setf rather than setaf.)
	color_prompt=yes
    else
	color_prompt=
    fi
fi

if [ "$color_prompt" = yes ]; then
    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w \$\[\033[00m\] '
else
    PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt

# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
    PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
    ;;
*)
    ;;
esac

# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
    test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
    alias ls='ls --color=auto'
    #alias dir='dir --color=auto'
    #alias vdir='vdir --color=auto'

    alias grep='grep --color=auto'
    alias fgrep='fgrep --color=auto'
    alias egrep='egrep --color=auto'
fi

# colored GCC warnings and errors
#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'

# some more ls aliases
#alias ll='ls -l'
#alias la='ls -A'
#alias l='ls -CF'

# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc package.

if [ -f ~/.bash_aliases ]; then
    . ~/.bash_aliases
fi

# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
if ! shopt -oq posix; then
  if [ -f /usr/share/bash-completion/bash_completion ]; then
    . /usr/share/bash-completion/bash_completion
  elif [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
  fi
fi
export PATH="$HOME/.local/bin:$PATH"
export PATH="$HOME/.npm-global/bin:$PATH"
export PI_CODING_AGENT_DIR=/mnt/rpi/pi-config
# opencode
export PATH=/home/sebas/.opencode/bin:$PATH
export PERSONAL_AGENT_SHARED_MEMORY_DB_PATH=~/agents-database/data/shared-agent-memory.sqlite3


text: ENOENT: no such file or directory, access '/home/sebas/.zshrc'


toolName: bash

text: shell=/bin/bash


stopReason: aborted

errorMessage: Operation aborted


text: actually should start tmux on ssh connect i think, to use on phone too (which cant have aliases)


command: ls -la /home/sebas/.ssh; printf '\n--- sshd_config include scan ---\n'; rg -n "^Include|^Match|ForceCommand|PermitTTY|AllowTcpForwarding|PermitUserEnvironment" /etc/ssh/sshd_config /etc/ssh/sshd_config.d/* 2>/dev/null || true; printf '\n--- current sshd effective snippets ---\n'; sshd -T 2>/dev/null | rg "forcecommand|permittty|allowtcpforwarding|permituserenvironment" || true

command: if [ -f /home/sebas/.bash_aliases ]; then sed -n '1,220p' /home/sebas/.bash_aliases; fi


text:
total 20
drwx------   2 sebas sebas 4096 Apr 10 15:21 .
drwx--x---+ 35 sebas sebas 4096 Apr 19 15:56 ..
-rw-------   1 sebas sebas  118 Dec  4 11:50 authorized_keys
-rw-------   1 sebas sebas 1546 Apr 17 09:08 known_hosts
-rw-r--r--   1 sebas sebas  568 Apr 10 15:20 known_hosts.old

--- sshd_config include scan ---
/etc/ssh/sshd_config:12:Include /etc/ssh/sshd_config.d/*.conf
/etc/ssh/sshd_config:90:#AllowTcpForwarding yes
/etc/ssh/sshd_config:95:#PermitTTY yes
/etc/ssh/sshd_config:99:#PermitUserEnvironment no
/etc/ssh/sshd_config:122:#	AllowTcpForwarding no
/etc/ssh/sshd_config:123:#	PermitTTY no
/etc/ssh/sshd_config:124:#	ForceCommand cvs server

--- current sshd effective snippets ---


text: (no output)


text:
Can set server-side auto-tmux on SSH login. That covers laptop and phone.

Need one scope choice first:
- interactive SSH only: auto-enter tmux when you open a shell
- all SSH sessions: risk breaking scp/rsync/git-over-ssh

Use interactive-only. Safe default.

phase: commentary

questionnaire

label: SSH tmux

prompt: Apply auto-tmux only for interactive SSH shells?

label: Yes

description: Safe. Normal shell logins enter tmux; scp/rsync/git-over-ssh keep working.

label: All SSH sessions

description: Not recommended. Can break non-interactive SSH use.

label: No

description: Skip server auto-tmux; only change laptop alias/workflow.


toolName: questionnaire

text: SSH tmux: user selected: 1. Yes

label: SSH tmux

prompt: Apply auto-tmux only for interactive SSH shells?

description: Safe. Normal shell logins enter tmux; scp/rsync/git-over-ssh keep working.

description: Not recommended. Can break non-interactive SSH use.

description: Skip server auto-tmux; only change laptop alias/workflow.


bash

command: tmux -V || true; test -f /home/sebas/.bash_aliases && sed -n '1,220p' /home/sebas/.bash_aliases || true


text: tmux 3.5a


text:
Plan:
1. add auto-tmux block to `~/.bashrc` for interactive SSH only
2. create `~/.bash_aliases` with a simple local helper if you want
3. give you laptop-side `bb` alias/function to remove later manual steps

Need the current `bb` definition on your laptop. I can’t see laptop files from here.

Fastest path:
- I patch server now
- you paste current laptop `bb` alias/function
- I rewrite it to just `ssh ...` and let server auto-enter tmux

Approve server change to `/home/sebas/.bashrc`?

questionnaire

label: Server change

prompt: Patch /home/sebas/.bashrc to auto-attach/create tmux on interactive SSH login?

value: yes

description: Recommended. Safe for normal SSH shells; non-interactive SSH stays untouched.

value: no

description: Do not modify server shell startup.


text: Server change: user selected: 1. Yes

label: Server change

prompt: Patch /home/sebas/.bashrc to auto-attach/create tmux on interactive SSH login?

description: Recommended. Safe for normal SSH shells; non-interactive SSH stays untouched.

description: Do not modify server shell startup.


edit

newText:
export PATH=/home/sebas/.opencode/bin:$PATH
export PERSONAL_AGENT_SHARED_MEMORY_DB_PATH=~/agents-database/data/shared-agent-memory.sqlite3

# Auto-start tmux for interactive SSH shells.
# Keeps long-running work alive if the client disconnects.
if [ -n "$SSH_CONNECTION" ] && command -v tmux >/dev/null 2>&1; then
    if [ -z "$TMUX" ]; then
        exec tmux new-session -A -s main
    fi
fi


toolName: edit

text: Successfully replaced 1 block(s) in /home/sebas/.bashrc.

diff:
     ...
 116 export PI_CODING_AGENT_DIR=/mnt/rpi/pi-config
 117 # opencode
 118 export PATH=/home/sebas/.opencode/bin:$PATH
 119 export PERSONAL_AGENT_SHARED_MEMORY_DB_PATH=~/agents-database/data/shared-agent-memory.sqlite3
+120
+121 # Auto-start tmux for interactive SSH shells.
+122 # Keeps long-running work alive if the client disconnects.
+123 if [ -n "$SSH_CONNECTION" ] && command -v tmux >/dev/null 2>&1; then
+124     if [ -z "$TMUX" ]; then
+125         exec tmux new-session -A -s main
+126     fi
+127 fi


text:
Done on server.

What changed
- `~/.bashrc` now does:
  - if session is interactive SSH
  - and not already inside tmux
  - `exec tmux new-session -A -s main`

Effect
- `ssh sebas@ballbox-first...` drops you into tmux session `main`
- reconnect from laptop or phone resumes same session
- closing laptop should no longer kill long terminal jobs running in tmux
- `scp`/`rsync`/non-interactive SSH should stay unaffected

Laptop `bb`
- now simplest is: make `bb` just plain SSH to this host
- no local tmux logic needed
- phone will work too because server handles tmux

Suggested laptop alias/function:
```bash
alias bb='ssh sebas@ballbox-first.emperor-ratio.ts.net'
```

If you want it more robust:
```bash
function bb() {
  ssh sebas@ballbox-first.emperor-ratio.ts.net "$@"
}
```

Why function is better
- supports `bb -t`
- supports `bb hostname`
- easier to extend later

Recommended workflow
- laptop: run `bb`
- phone: open SSH app, connect normally
- both land in tmux `main`
- detach if needed with `Ctrl-b d`
- list sessions: `tmux ls`
- attach manually: `tmux a -t main`

Caveat
- one shared tmux session means laptop and phone can both control the same terminal at once
- if you want separate sessions per device later, I can switch to:
  - default attach to latest
  - or create named sessions like `main`, `phone`, `work`

Next step
- paste your current laptop `bb` alias/function and I’ll rewrite it cleanly.


text:
controlling same session in phone+laptop is a feature, makes work portable
we could also have aliases for easy tmux session swapping (like /resume) or show me if already in tmux


bash

command:
python - <<'PY'
from pathlib import Path
p=Path('/home/sebas/.bash_aliases')
print('exists', p.exists())
if p.exists():
    print(p.read_text())
PY


text:
---
name: memory-write
description: Save a durable high-signal memory into agents-database
---

# memory-write

## Use When

- new information is likely useful beyond current chat or task
- saving a decision, preference, project fact, reusable source/artifact, or handoff summary
- user asks to remember something durable

## Do Not Use When

- content is trivial, redundant, ephemeral, or likely useless later
- content is secret unless user explicitly asked to store it

## Required Workflow

1. If useful context may already exist, optionally check first:
   - `curl -fsSL "${AGENTS_DB_API:-http://100.116.176.16:8091}/api/search?q=<query>&scope=global&scope=project&limit=10"`
2. Choose best memory type:
   - `profile`: stable user preference, priority, recurring constraint
   - `decision`: chosen approach or operating rule
   - `project`: durable project fact or invariant
   - `artifact`: reusable source, claim, reference, output
   - `episode`: concise session summary, blocked state, handoff context
3. Build a concise payload with:
   - required: `type`, `scope`, `title`, `content`
   - recommended when known: `subtype`, `project_id`, `repo_id`, `task_id`, `run_id`, `source_ref`, `origin_agent`, `url`, `domain`, `metadata`
4. Save it with the helper:
   - `~/.agents/skills/memory-write/memory-write --type <type> --scope <scope> --title "<title>" --content "<content>"`
5. Confirm the created memory id and what was stored.

## Quick Examples

```bash
~/.agents/skills/memory-write/memory-write \
  --type profile \
  --subtype working_preference \
  --scope global \
  --title "User prefers concise status updates" \
  --content "Sebas prefers short, direct updates with business impact first." \
  --source-ref "conversation:2026-04-17" \
  --origin-agent personal-agent

~/.agents/skills/memory-write/memory-write \
  --type decision \
  --subtype operating_rule \
  --scope global \
  --title "Agents should save durable high-signal memory opportunistically" \
  --content "Treat agents-database as shared durable memory, not only search." \
  --metadata '{"source":"conversation"}'
```

## Rules

- prefer one strong memory over many weak ones
- keep `title` short and searchable
- keep `content` factual and compact
- use `episode` for summaries, not for every chat turn
- include `url` and `domain` for web-derived artifacts when known
- include `project_id`, `repo_id`, `task_id`, and `run_id` when context exists


text: exists False


text:
Good. Shared session stays.

I’ll add:
- tmux-aware prompt marker so you can see when you’re inside tmux
- short aliases:
  - `/resume` → attach `main`
  - `/swap` → session chooser
  - `/newtmux <name>` → create/switch
  - `/tmuxls` → list sessions

One note:
- aliases starting with `/` are shell-valid, but a shell function is cleaner and more flexible
- I can still make them look slash-like

Approve patch to `~/.bash_aliases` and prompt tweak in `~/.bashrc`?

questionnaire

label: tmux helpers

prompt: Add tmux status marker and slash-style helper commands in shell config?

description: Add /resume, /swap, /newtmux, /tmuxls and show [tmux:<session>] in prompt.

label: Helpers only

description: Add helper commands, but do not change prompt.

description: Keep only the auto-tmux login behavior.


text: tmux helpers: user selected: 2. Helpers only

label: tmux helpers

prompt: Add tmux status marker and slash-style helper commands in shell config?

description: Add /resume, /swap, /newtmux, /tmuxls and show [tmux:<session>] in prompt.

description: Add helper commands, but do not change prompt.

description: Keep only the auto-tmux login behavior.


write

content:
# tmux helpers
alias /resume='tmux attach -t main || tmux new -s main'
alias /tmuxls='tmux ls'
alias /swap='tmux choose-tree -s'

/newtmux() {
  if [ -z "$1" ]; then
    echo "usage: /newtmux <session-name>"
    return 1
  fi
  tmux new-session -A -s "$1"
}

/tmuxwhere() {
  if [ -n "$TMUX" ]; then
    printf 'inside tmux: %s\n' "$(tmux display-message -p '#S' 2>/dev/null || echo unknown)"
  else
    echo 'not in tmux'
  fi
}

bash

command: ~/.agents/skills/memory-write/memory-write --type decision --subtype operating_rule --scope global --title "SSH sessions auto-enter tmux main" --content "On ballbox-first, interactive SSH logins auto-attach or create tmux session main via ~/.bashrc. Shared tmux session across laptop and phone is intentional for portable work. Added shell helpers in ~/.bash_aliases: /resume, /tmuxls, /swap, /newtmux, /tmuxwhere." --source-ref "conversation:2026-04-19" --origin-agent personal-agent


toolName: write

text: Successfully wrote 430 bytes to /home/sebas/.bash_aliases


text:
Traceback (most recent call last):
  File "/home/sebas/.agents/skills/memory-write/memory-write", line 86, in <module>
    main()
    ~~~~^^
  File "/home/sebas/.agents/skills/memory-write/memory-write", line 80, in main
    with urllib.request.urlopen(req, timeout=30) as r:
         ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/urllib/request.py", line 189, in urlopen
    return opener.open(url, data, timeout)
           ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/urllib/request.py", line 489, in open
    response = self._open(req, data)
  File "/usr/lib/python3.13/urllib/request.py", line 506, in _open
    result = self._call_chain(self.handle_open, protocol, protocol +
                              '_open', req)
  File "/usr/lib/python3.13/urllib/request.py", line 466, in _call_chain
    result = func(*args)
  File "/usr/lib/python3.13/urllib/request.py", line 1348, in http_open
    return self.do_open(http.client.HTTPConnection, req)
           ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/urllib/request.py", line 1323, in do_open
    r = h.getresponse()
  File "/usr/lib/python3.13/http/client.py", line 1430, in getresponse
    response.begin()
    ~~~~~~~~~~~~~~^^
  File "/usr/lib/python3.13/http/client.py", line 331, in begin
    version, status, reason = self._read_status()
                              ~~~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3.13/http/client.py", line 300, in _read_status
    raise RemoteDisconnected("Remote end closed connection without"
                             " response")
http.client.RemoteDisconnected: Remote end closed connection without response

Command exited with code 1


text:
Done.

Added in `~/.bash_aliases`
- `/resume` → attach `main`, or create it
- `/tmuxls` → list sessions
- `/swap` → tmux session/window chooser
- `/newtmux <name>` → create or attach named session
- `/tmuxwhere` → tells you if you’re in tmux and which session

Current setup
- SSH login: auto-enters `tmux main`
- laptop + phone: both can share same live session
- helper commands available in new shells

Use
```bash
/resume
/tmuxls
/swap
/newtmux scratch
/tmuxwhere
```

Notes
- because these are aliases/functions, they work only in shell, not inside programs
- `choose-tree` in `/swap` is best inside tmux; outside tmux it may fail or be less useful
- if you want, I can make `/swap` smart:
  - inside tmux → chooser
  - outside tmux → fuzzy attach/list fallback

Memory save failed
- agents-database connection dropped
- config changes still applied

For laptop `bb`
- keep it simple now:
```bash
function bb() { ssh sebas@ballbox-first.emperor-ratio.ts.net "$@"; }
```

If you want, next I can make the tmux helpers smarter and add a clear shell prompt marker only when inside tmux, without clutter.


text: remove helpers, ill use normal tmux commands to navigate


edit

oldText:
# tmux helpers
alias /resume='tmux attach -t main || tmux new -s main'
alias /tmuxls='tmux ls'
alias /swap='tmux choose-tree -s'

/newtmux() {
  if [ -z "$1" ]; then
    echo "usage: /newtmux <session-name>"
    return 1
  fi
  tmux new-session -A -s "$1"
}

/tmuxwhere() {
  if [ -n "$TMUX" ]; then
    printf 'inside tmux: %s\n' "$(tmux display-message -p '#S' 2>/dev/null || echo unknown)"
  else
    echo 'not in tmux'
  fi
}


text: Successfully replaced 1 block(s) in /home/sebas/.bash_aliases.

diff:
- 1 # tmux helpers
- 2 alias /resume='tmux attach -t main || tmux new -s main'
- 3 alias /tmuxls='tmux ls'
- 4 alias /swap='tmux choose-tree -s'
- 5
- 6 /newtmux() {
- 7   if [ -z "$1" ]; then
- 8     echo "usage: /newtmux <session-name>"
- 9     return 1
-10   fi
-11   tmux new-session -A -s "$1"
-12 }
-13
-14 /tmuxwhere() {
-15   if [ -n "$TMUX" ]; then
-16     printf 'inside tmux: %s\n' "$(tmux display-message -p '#S' 2>/dev/null || echo unknown)"
-17   else
-18     echo 'not in tmux'
-19   fi
-20 }


text:
Removed.

State now
- SSH still auto-enters `tmux main`
- helper aliases/functions are gone
- use normal tmux commands only

If you want, next step is just defining the laptop `bb` function cleanly.


text: will pi agent know its inside tmux? could be useful


text:
Yes.

Inside the shell, the main signal is:
```bash
$TMUX
```

If set, you’re in tmux. The agent can also get:
```bash
tmux display-message -p '#S'
```
to know the session name.

So on this Pi, if you SSH in and auto-land in tmux:
- shell processes see `TMUX`
- commands run from that shell can detect tmux
- an agent launched from that shell can know it’s in tmux

Useful cases
- show safer advice for detach/reconnect
- avoid starting nested tmux accidentally
- report current session name
- decide whether long jobs are already protected from SSH disconnects

Limits
- it only knows if its current shell/process inherited that environment
- a background service or non-shell tool may not have `TMUX`
- if I run tools in a separate non-interactive context, they may not reflect your exact terminal state

Quick checks
```bash
echo "$TMUX"
tmux display-message -p '#S'
```

If you want, I can add a tiny helper in the repo agent config so future shell-based workflows explicitly check `TMUX` before suggesting detach advice.