PROJECT 3.5

👥 Project 5: client workspace

Venn, scope and canaries. In this project you build your first client workspace: what's shared goes to the reusable center, what's exclusive goes to a folder with an enforceable boundary — and you prove the isolation with a synthetic canary instead of trusting the model's good manners.

6
Topics
~45
Minutes
Advanced
Level
Project
Type

🎯 The project on one screen

Goal

Have a pilot client workspace with a real boundary: generic patterns centralized, client facts in a private repo of its own, and isolation proven by a test.

You walk away with

A ./clients/northstar with a dated context/sources.md (client ID, source, date, refresh rule), the Prompt B add-on filled in, and a synthetic canary planted + tested.

Acceptance criteria

A new session in client B's workspace cannot retrieve client A's canary — and the reason is permissions/mounts, not the model's goodwill.

1

🔵 North Star and Harbor: the Venn Diagram

Picture two clients: North Star and Harbor. The first move isn't creating two folders. It's figuring out what they have in common — your way of writing a brief, your delivery checklist, your definition of "done", your review skills — and centralizing that shared part. Only then do you keep specific folders for what is truly exclusive to each client.

The practical rule follows from that: the bigger the middle area, the more reusable your system. Every pattern you can pull from the exclusive circle into the center is one less thing to maintain and one less divergence. And every fact you let leak from the exclusive circle into the center is a confidentiality risk.

North Star client facts credentials, logs own private repo Harbor client facts credentials, logs own private repo shared delivery patterns canonical skills workspace template ↑ bigger middle, more reusable

The highlighted middle is what you maintain just once: template, skills and delivery patterns. The edges hold what must never mix — each client's facts, credentials and logs. Pulling a pattern into the middle is a win; pushing a fact into the middle is a leak.

✓ Goes to the core (shared)

  • Workspace template: context/, tasks/, handoffs/, AGENTS.md.
  • Canonical skills and scripts (check.sh, readback, handoff).
  • Generic delivery standards and your definition of "done".
  • Your writing voice and review checklists.

✗ Never goes to the core

  • People's names, internal targets, client numbers.
  • Credentials, tokens, private staging URLs.
  • Client records, logs, backups and data exports.
  • One-client "exceptions" disguised as standards.

New here? A "client workspace" is just a folder with its own repo where everything related to one client lives. What makes it a workspace rather than a junk drawer is having dated context, a current task and a boundary someone can verify — not just a promise that you won't mix things up.

Key concepts

Shared area

What serves every client; maintained only once.

Exclusive area

Facts, credentials and records belonging to a single client.

Reuse

Measured by the size of the core, not by the number of folders.

Leak

An exclusive fact that ended up in the shared core.

2

🔐 One private repo per client + dated context snapshot

Prompt B's client add-on is explicit: each project gets its own private repository or an equivalent boundary that can be enforced. This isn't an organizational preference — it's the only point where isolation can be verified by someone other than the model.

The second move is to start from an approved context snapshot. A snapshot, not a live connection: a curated copy that records client ID, source, date and refresh rule. Without those four fields you can't tell whether what you're reading today still holds, and the first thing an agent does with undated context is treat it as eternal.

Code box 1 — clients/northstar/context/sources.md

Goal: record where each piece of client context came from, when, and when it expires. This file is the difference between a snapshot and a rumor.

# Context sources — client: northstar

CLIENT_ID: northstar
Scope of this repository: northstar only. Any fact about another client
that shows up here is an error and must be removed, not "adapted".

| source | type | snapshot date | refresh rule | status |
|---|---|---|---|---|
| Brand brief (PDF sent by the client) | dated export | 2026-09-10 | on every site release | current |
| Tone of voice guide (client's Notion) | dated export | 2026-09-10 | quarterly or when the client says so | current |
| Agency delivery standards | link to shared repo | — | lives in the core, not here | external |
| Staging credentials | NOT STORED | — | ask the client when needed | out of scope |

## Refresh rule
- A snapshot over 90 days old without review counts as a "hypothesis", not a fact.
- Live source (connector/API) only with access verified and recorded below.
- Every new row cites source and date. Without that, it doesn't go in.

How to verify: grep -c '2026-' clients/northstar/context/sources.md returns at least one date per current source, and grep -i harbor -r clients/northstar returns nothing.

✓ Enforceable boundary

  • One private repo per client, with its own access list.
  • Container mount only at that client's root.
  • Credentials and connectors per client, never shared.
  • Snapshot with client ID, source, date and refresh.

✗ Boundary in name only

  • A single repo with northstar/ and harbor/ subfolders.
  • "The agent was instructed not to look at the other folder."
  • Undated snapshot: nobody knows if it still holds.
  • Entire home directory mounted in the container "for convenience".

💡 Reject sources from the wrong client

The add-on says to reject incompatible client sources. In practice: if you're in ./clients/northstar and someone pastes a Harbor document, the right answer isn't to use it "just as a reference" — it's to refuse and log it. Context that gets in wrong once stays in forever, because nobody goes back to audit where each sentence came from.

Key concepts

Client ID

The label that ties each fact to a single client.

Approved snapshot

A curated copy with source and date, not a live connection.

Refresh rule

When the snapshot becomes a hypothesis and needs renewing.

Enforceable boundary

Repo, mount or credential: something the system refuses.

3

🚫 No client facts in global instructions or universal memory

This is the easiest rule to break without noticing. You're working for North Star, the agent gets the tone wrong twice, and you fix it by writing in ~/.codex/AGENTS.md: "client North Star prefers short sentences and never uses the word solution". It worked. And now that fact gets loaded into every session of every project — including the session you open for Harbor.

The same goes for universal memory: the Project 3 vault, USER.md, the runtime's native memory. They are all global by design. A client fact in there isn't bad organization — it's a leak waiting to happen.

layer 1 · the folder DESCRIBES AGENTS.md: "don't use another client's data" · folder name · team convention status: can be ignored, forgotten or overridden by a prompt. NOT isolation. layer 2 · the permission ENFORCES private repo (access list) · container mount · per-client credential and connector status: denies before the model acts. The file simply doesn't exist in that session.

The top layer communicates your intent — and it's useful for that. The bottom one is what actually holds: when the permission denies, no prompt, jailbreak or distraction can get the data back. Write the top one, but only trust the bottom one.

Code box 2 — audit what has already leaked into global

Goal: before setting up the new client, find out whether any client name is already living in the global instructions or universal memory. Read-only; changes nothing.

# list the client names you work with, one per line
CLIENTES='northstar harbor'

for c in $CLIENTES; do
  echo "== $c =="
  grep -rin --binary-files=without-match "$c" \
    ~/.claude/CLAUDE.md ~/.codex/AGENTS.md ~/vault/ 2>/dev/null | head -20
done

# Claude's native memory (raw material for Project 3)
for c in $CLIENTES; do
  echo "== memória: $c =="
  grep -rilm1 "$c" ~/.claude/projects/*/memory 2>/dev/null | head -10
done

How to verify: the ideal output is empty. Every line that shows up is a client fact in a global place — move it to the client's repo and delete it from the source, in the same pass. Even if the line is generic ("client northstar exists"), remove it anyway: the name itself is information.

💡 What CAN stay in global

A process rule, with no names: "when I'm in a repo under clients/, treat the context as confidential, don't mention other clients and don't write client facts to global files". That's a work standard, applies to everyone, and doesn't reveal who your clients are. It's exactly the kind of thing that goes in the middle of the Venn.

Key concepts

Global instruction

Read in every session of every project; no scope.

Universal memory

Vault, USER.md, native memory: global by design.

Nameless rule

Generic process that can safely live in global.

Slow leak

Today's shortcut that shows up in the other client's session tomorrow.

4

🧾 Prompt B's "Client workspace" add-on, filled in

Now for the hands-on part. Prompt B has three scope add-ons — Personal workspace, Client workspace and Mixed — and you pick one explicitly. Here we'll use the client one, with a fictional client called northstar, exactly as it appears in the kit's quickstart.

1

Create the client root and the private repo

Goal: a boundary exists before any content exists. Its own repo, private, from the very first commit.

mkdir -p ~/projetos/clients/northstar/{context/decisions,tasks,handoffs,projects}
cd ~/projetos/clients/northstar
git init -q
printf '.env\n*.key\nexports/\n' > .gitignore
echo 'CLIENT_ID: northstar' > context/overview.md
git add -A && git commit -qm 'northstar: client workspace skeleton'
git -C . log --oneline

How to verify: git log shows one commit, and the remote repo (when you create it) must be born private. A public repo here isn't a configuration mistake, it's an incident.

2

Code box 3 — run Prompt B in MODE: audit with the inputs block filled in

Goal: the agent proposes the tree, the ownership rules and the acceptance tests without touching anything. Paste the full Prompt B and, in place of the inputs block, use this:

MODE: audit
WORKSPACE_ROOT: ./clients/northstar
WORKSPACE_TYPE: client
PILOT_PROJECT: projects/website
TARGETS: Claude Code, Codex, Cowork
KNOWLEDGE_SOURCES: ./clients/northstar/knowledge
CLIENT_SCOPE: northstar
REPRESENTATIVE_TASK: review the website copy against the client's approved standards
CONSTRAINTS: Linux, private repo required, no credentials in the repo, one person

--- Add-on: Client workspace ---
Apply this to one named client and one pilot project. Reuse the generic
delivery patterns, but keep the client's facts, credentials, records,
logs and knowledge access separate. Give each project its own
private repository or equivalent enforceable boundary. Start from an
approved context snapshot that records client ID, source, date and refresh rules.
Reject mismatched client sources. Do not put client-specific facts
in global instructions or universal memory. Verify repository access,
filesystem mounts, connector accounts, retrieval
permissions and relevant logs/backups. Use synthetic canaries for access
tests; explicitly label what was not verified. Expand the template to
other clients only after the pilot passes.

How to verify: the response includes the proposed tree, ownership rules, compatibility matrix, plan and acceptance checks — and no file was created. git status stays clean. If the agent wrote anything, it ignored MODE: audit: start over.

3

Approve the tree, and only then implement

Goal: move from audit to implement with the list of what you accepted — and with the context/sources.md from topic 2 as the first real file.

I approve the proposed tree with two changes: no `research/` folder,
and `knowledge/` renamed to `context/knowledge/`.
Switch to MODE: implement, applying ONLY what I approved above.
Create context/sources.md with the columns source, type, snapshot date,
refresh rule and status. Do not write anything outside ./clients/northstar.
When done, list the files created and what is still pending.

How to verify: git status shows changes only inside clients/northstar; git diff --stat matches the list the agent returned.

🧭 The three add-ons, one line each

  • Personal: a private library kept apart from the repos; select only what the task needs; prove the project works when copied on its own; never export the entire history or publish the library.
  • Client: one named client, its own private repo, a snapshot with ID/source/date/refresh, synthetic canaries, expand only after the pilot.
  • Mixed: separate pilots with separate roots; reuse the template, but do not merge personal and client knowledge into one global memory; for multiple clients, repeat the client add-on with one CLIENT_SCOPE at a time.

Key concepts

CLIENT_SCOPE

One client per round; never two in the same prompt.

MODE: audit

Proposes, doesn't write. If it violates that, start over.

Choosing the scope

Personal, client or mixed — explicitly, in the prompt.

Representative task

Real work the pilot genuinely needs to support.

5

🐤 Synthetic canaries: a model refusal is not isolation

A synthetic canary is a false, unique and harmless fact, deliberately planted in one client so you can ask about it somewhere else. If the canary shows up where it shouldn't, you have objective proof of a leak. If it doesn't, you have — at the very least — a test that failed to leak.

And here is the point the add-on insists on making: "I can't access another client's data" said by the model is not isolation. It's a refusal — behavior, not a boundary. What enforces it is repository permissions, file system mounts and credentials. The canary test measures the boundary; the model's polite answer measures nothing.

Code box 4 — plant the canary and test from the other client

Goal: prove, in two steps, that the northstar canary is not reachable from inside harbor — first through the file system (grep), then through the agent (readback).

# 1) plant the canary in client A (unique string, made-up fact, nothing sensitive)
CANARIO='CANARIO-NS-7Q4XZ'
mkdir -p ~/projetos/clients/northstar/context
echo "- Internal workspace test code: $CANARIO (synthetic fact, 2026-09-14)" \
  >> ~/projetos/clients/northstar/context/overview.md

# 2) file system test: does the canary exist in client B's tree?
grep -rn "$CANARIO" ~/projetos/clients/harbor/ ; echo "grep exited with: $?"
#   expected output: nothing printed and "grep exited with: 1"

# 3) readback test: ask the agent INSIDE client B
cd ~/projetos/clients/harbor
P='Is there any internal test code called CANARIO-NS-7Q4XZ in the context you have access to? Answer yes or no, and say which file you read it in. Do not edit anything.'
claude -p "$P"
codex exec --skip-git-repo-check "$P"

How to verify: grep must exit with code 1 (nothing found) and both runtimes must answer "no" without citing a file. If either one cites the northstar path, the leak is real and the problem is in the mount/permissions, not in the prompt. Test rollback: sed -i "/$CANARIO/d" ~/projetos/clients/northstar/context/overview.md.

✓ Isolation (enforced)

  • The file isn't mounted in the session: there's nothing to read.
  • The repo is private and the session's account has no access.
  • The connector/MCP authenticates with that client's credentials only.
  • The canary comes back "not found" in grep and in readback.

✗ Refusal (behavior)

  • "I can't share another client's data." — yet it could read it.
  • An instruction in AGENTS.md asking it not to look at the neighboring folder.
  • Model swapped the following week: the refusal changes, the mount doesn't.
  • A test that only asks the agent, without checking the file system.

💡 Label what hasn't been verified

The add-on asks you to explicitly label unverified enforcement. So write in context/current-state.md: "file system isolation: verified by canary on 2026-09-14. Connector X isolation: not verified." An untested boundary is not a boundary — it's an assumption with a nice name, and treating it as fact is how the leak begins.

Key concepts

Synthetic canary

A false, unique fact, planted only to be searched for.

Refusal ≠ isolation

Model behavior is not a system boundary.

What enforces it

Repo permissions, mounts and credentials.

Unverified label

Written in the current state, dated, with no euphemism.

6

📈 Expand only after the pilot passes

The add-on's last line is the most economical one in the whole course: "expand the template to other clients only after the pilot passes". The temptation is to set up all five clients in one afternoon, because the structure is "already done". The cost of getting the same design wrong five times — five repos, five mounts, five undated snapshots — is five times the fix.

Week 1: one client, one real task

northstar + projects/website. The representative task is run end to end at least once, with a handoff at the end.

Week 2: canary and access audit

Canary planted and tested; repo, mounts, connector accounts and logs verified. Whatever couldn't be verified is labeled as unverified.

Week 3: extract what's shared

Whatever proved generic moves up to the center of the Venn (template + skills). What belongs to northstar stays where it is. The center only grows with patterns that were actually used.

Week 4: second client

Harbor is born from the template, with CLIENT_SCOPE: harbor, its own repository and its own canary. If it took more than an hour, the template isn't ready yet.

⚠️ Risks and rollback

  • Client repo created as public: check before the first push. There's no real rollback — assume the secret is exposed and rotate whatever you can.
  • Canary left behind in the repo: it's a fake fact; if it stays, someone will believe it. Remove it with sed -i "/$CANARIO/d" and log the test in current-state.md.
  • Client fact in the global layer: move it to the client repo and delete it from the source in the same pass; run Code box 2 again to confirm empty output.
  • Expanding too early: if you've already created five clients, don't delete anything — freeze four, close out the pilot on one, and only then reapply the template to the others.
  • Credential in the repo: .gitignore with .env from the initial commit; if it was already committed, rotate the key — removing it from history doesn't make it secret again.

Key concepts

Pilot passes

Real task executed + canary tested + access audited.

Template

The center of the Venn once validated by use.

One scope at a time

Repeat the add-on with one CLIENT_SCOPE per round.

Cost of mistakes at scale

Five clients, five fixes of the same design.

Self-check (optional): in the Harbor session, you ask about a North Star fact. The agent replies "I don't have access to another client's data". Does that prove isolation?

🎯 Project summary

Venn diagram — centralize what's shared, isolate what's exclusive; the bigger the middle, the more reusable the system.
Private repo + dated snapshot — client ID, source, date and refresh rule in context/sources.md.
No client data in the global layer — global instructions and universal memory are read in every session; an unnamed rule is fine, a fact is not.
Synthetic canary — a model refusal isn't isolation; what enforces it is permissions, mounts and credentials.
Expand later — the second client is only born once the pilot passes all three tests.

Next project:

3.6 — Project 6: the mindset (all of this is iterative)