Skip to main content
PCSalt logo
YouTube GitHub
Back to Workflow
Workflow · 7 min read

A Practical Claude Code Workflow — Memory Hygiene, Context Pipelines, and a Self-Hosted Notes Viewer

The Claude Code workflow I actually use day-to-day — keeping global instructions short, allowlisting safe read commands so research is unattended, and saving every analysis to a localhost-served notes archive that feeds the next session.


When people share their “Claude Code tips,” it’s usually a prompt template or a clever one-liner. That’s fine, but it caps out fast. The real lever isn’t the prompt — it’s the context the model has when it reads the prompt. Context is what makes a session feel like a senior pair, and a thin context is what makes the same model feel like an overconfident intern.

This post is the workflow I’ve settled into after a few months of daily use — what I keep in memory, what I allowlist for permissions, and (the part that matters most) why I save every substantive analysis to disk and serve it back to myself over localhost.

The trap with prompt-only thinking

Claude Code does carry a memory layer across sessions — ~/.claude/CLAUDE.md for global instructions, the auto-memory under ~/.claude/projects/<slug>/memory/, the CLAUDE.md checked into the repo. Last week’s chat history is gone, but the memory mechanisms persist. They are exactly the surfaces this post is about cultivating.

The trap is treating Claude Code as a prompt box and ignoring that memory layer. When you do that, every session effectively starts from zero. You re-explain the project, the conventions, the constraints, the deadlines. You correct the same mistakes. You re-ask questions you already answered last week — not because the model “forgot,” but because nothing you wrote to it ever made it into a place that loads again next time.

After enough of this you notice the pattern: the work isn’t in the prompt — it’s in everything around the prompt. The model is great at synthesising what’s in its window; you have to put the right things in that window, every time, and the only way to scale that is to make it automatic.

So the workflow stops being “write better prompts” and becomes “build a better context pipeline.”

The loop I actually use

   ┌──────────────────────┐
   │       CONTEXT        │   global instructions
   │  (instructions +     │   project memory
   │   memory + prior     │   prior saved analyses
   │   analyses)          │
   └──────────┬───────────┘


   ┌──────────────────────┐
   │         PLAN         │   "what would you do?"
   └──────────┬───────────┘


   ┌──────────────────────┐
   │        REVIEW        │ ◄──── iterate ─┐
   └──────────┬───────────┘                │
              │                            │
              ▼                            │
   ┌──────────────────────┐                │
   │   REFINE / CLARIFY   │ ───────────────┘
   └──────────┬───────────┘


   ┌──────────────────────┐
   │       ★ SAVE ★       │   analysis → ~/research/docs
   │  (the addition)      │   (next session will read this)
   └──────────┬───────────┘


   ┌──────────────────────┐
   │       EXECUTE        │
   └──────────────────────┘

The first five boxes are the conventional Claude Code flow. The one I added is SAVE — before any code lands, the agreed-on analysis goes into a persistent notes archive. The next time I open a fresh session on the same topic, that archive is part of the context. The loop closes.

The rest of this post is the three pieces that make this loop work: memory hygiene, read permissions, and the save habit.

Memory hygiene — global instructions vs auto-memory

Claude Code’s memory isn’t one thing. There are at least two homes that matter day-to-day:

  • Global instructions~/.claude/CLAUDE.md. Loaded on every session, in every project. This is where your persona, your code style rules, your global “do this / never do that” guardrails live.
  • Auto-memory~/.claude/projects/<project-slug>/memory/. Per-project. Indexed via a MEMORY.md file that’s auto-loaded; the indexed entries are the actual memory bodies, kept in sibling .md files. This is where project-specific facts accumulate — who is doing what, why a decision was made, what to avoid.

There are more places memory lives than these two (skill state, agent-specific memory, slash-command artifacts). I’m covering those in a separate post — for this one, treat global + auto-memory as the working pair.

Two rules I’ve settled on for both:

1. Keep it short. When global instructions or MEMORY.md get long, the model starts to miss things in them — exactly like a human reading a wall of text. The signal-to-noise drops. Every line you add dilutes the lines that were already there. Treat the instruction files like a tight checklist, not a manual.

2. Move detail behind pointers. Instead of writing five paragraphs about the API conventions in MEMORY.md, write one line: See api-guidelines.md for the conventions, and put the detail in the sibling file. The model knows to fetch when relevant; you keep the always-loaded surface lean.

The test I use: if I removed a line from global instructions, would a future session get noticeably worse? If no, the line shouldn’t be there.

Read permissions for unattended research

Half the value of Claude Code shows up when I can step away for ten minutes and come back to find research already done. That only works if the agent can read things without stopping every thirty seconds to ask “may I read this file?”

My work directories are stable:

~/workspace/backend/<repo>
~/workspace/android/<repo>

So I allowlist reads under ~/workspace/** once, and never get prompted for them again. I also allowlist read-only gh (GitHub CLI) commands so the agent can pull issues, PR descriptions, and repo metadata without my involvement.

Drop this into ~/.claude/settings.json:

{
  "permissions": {
    "allow": [
      "Read(~/workspace/**)",
      "Bash(gh issue list:*)",
      "Bash(gh issue view:*)",
      "Bash(gh pr list:*)",
      "Bash(gh pr view:*)",
      "Bash(gh pr diff:*)",
      "Bash(gh repo view:*)",
      "Bash(gh api repos/*:*)"
    ]
  }
}

A few things worth saying out loud:

  • Read(~/workspace/**) is broad but safe. It’s read-only. The agent can scan, grep, open files — but cannot write or delete. Writes still prompt.
  • The gh allowlist is read-only by design. No gh pr create, no gh issue close, no gh pr merge. Those should always require an explicit prompt — they affect shared state.
  • I do not allowlist Bash(*) or anything like it. That would defeat the point of the prompt-on-action design.

With this minimal set, “go research how this module is wired and come back with a plan” stops needing me in the room.

Closing the loop — saving every analysis

This is the part I find most people miss.

Every substantive analysis — investigations, designs, post-mortems, architecture reviews — gets written to disk in a fixed location with a fixed shape. The location is ~/research/docs/<category>/. The shape is a markdown file with frontmatter:

---
title: <human title>
date: 2026-01-04T09:00:00+05:30
category: <slug — same as the folder>
query: |
  <the literal question I asked>
decisions: |
  <what we agreed on, what we scoped out, why>
---

# <title>

<full analysis>

The frontmatter does three jobs:

  • date with timezone gives me a sortable, unambiguous chronology — useful when I’m asking “what did we decide about X before the deadline shifted?”
  • query preserves the literal prompt that triggered the analysis. Future-me can re-evaluate whether the conclusion still applies to a slightly different question.
  • decisions is the TL;DR — what was decided AND what was explicitly out of scope. The second half saves more time than the first.

The categories are free-form lowercase folders — ci-cd, android, api-guidelines, meta, workflow, whatever fits. Reuse a folder when the topic overlaps; create a new one when it genuinely doesn’t.

To make all of this automatic, I have it in global instructions: every substantive analysis must be saved to ~/research/docs/<category>/<date>_<slug>.md with the frontmatter above, before any code lands. The agent now does it without prompting.

Why ~/research and not ~/Documents

A small but load-bearing decision: I keep this archive at ~/research/docs/, not ~/Documents/.

~/Documents is private space — personal letters, scans, things I don’t want to mentally classify as “work.” Pulling it into the same mental folder as code reviews and architecture notes felt wrong, so I split. ~/research/ is the work brain; ~/Documents/ is the life brain.

The other reason is technical. The docs/ subdirectory is served by a tiny background process on 127.0.0.1:6202, so the archive isn’t just files on disk — it’s a live, searchable, chronologically-indexed website that updates the moment I save a file. New analysis lands → viewer sees it within a second → no editor needed.

I’m not going to cover that server here — it deserves its own post, and gets one: Localhost Markdown Viewer for Research Notes. Read that one if you want the architecture and the launchd setup.

Alternatives to explore

The principle — saved analyses, served locally, fed back as context — doesn’t depend on my exact tooling. A few off-the-shelf options worth trying:

  • MkDocs Material — biggest plugin ecosystem; the safe default if you want a polished, themeable site with search and tags out of the box.
  • Docusaurus — heavier (React-based), best if you also want versioned docs or a public-facing knowledge base later.
  • Quartz — purpose-built for digital gardens with [[wiki-link]] semantics. Great fit if you want to cross-reference analyses heavily.
  • Obsidian + Local REST API plugin — full GUI, zero server code, less scriptable. Good if you’d rather not touch infra.
  • Astro content collections — the same engine this blog runs on. Slightly ironic but elegant if you already use Astro.
  • Just VS Code’s markdown preview — honest fallback. If you’re not sure you’ll keep the habit, don’t build infra yet.

Pick whichever one you’ll actually use. The viewer doesn’t matter; the saving matters.

Where everything lives — a reference

Here’s the full map of the surfaces this workflow touches, so you can wire your own without hunting:

SurfacePathWhat it holdsLoaded when
Global instructions~/.claude/CLAUDE.mdPersona, code style, global do/don’t rulesEvery session, every project
Auto-memory index~/.claude/projects/<project-slug>/memory/MEMORY.mdOne-line pointers to memory bodiesEvery session in that project
Auto-memory bodies~/.claude/projects/<project-slug>/memory/*.mdThe actual remembered factsFetched when relevant
Project instructions<repo>/CLAUDE.mdRepo-specific rules and conventionsSessions inside that repo
Permissions~/.claude/settings.jsonAllowlisted Read / Bash patternsPer session
Research archive~/research/docs/<category>/<date>_<slug>.mdSaved analyses with frontmatterRead on demand by agent
Viewerhttp://127.0.0.1:6202/viewer/Browser UI for the archiveWhen I open it

The TL;DR of the whole workflow in one line: keep the always-loaded surfaces lean, allowlist the safe read commands, and never let an analysis die in a session window — write it to disk.

If you only take one habit from this post, take the saving habit. Everything else is tuning.