Skip to content

feat: tenant templates, backup guards, and prep-my-visit PDF renderer#4

Merged
pswider merged 3 commits into
mainfrom
feat/tenant-templates-and-guards
Jun 4, 2026
Merged

feat: tenant templates, backup guards, and prep-my-visit PDF renderer#4
pswider merged 3 commits into
mainfrom
feat/tenant-templates-and-guards

Conversation

@pswider

@pswider pswider commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

Lands three coherent pieces of work that together move Tula toward
golden-image deployability, hardens the backup pipeline, and adds the
PDF-generation layer to prep-my-visit.

All three commits are additive over current `main`. No edits to
existing skill structure, no force-push, no history rewrites. PHI-clean,
ASCII-clean (ran `scripts/strip-ai-chars.mjs` against all new files
including `.template` extensions).

Commits

1. `feat(templates): tenant-templates layer with tiered memory model`

21 net-new files establishing the golden-image memory layer every new
tenant inherits when its agent first comes online.

```
templates/
README.md # tier model + placeholder vocab
AGENTS.template.md # L1: tier discipline taught here
MEMORY.template.md # L1: always-loaded, <80 lines, pointers
USER.template.md # L1: per-tenant identity
profile.template.yaml # L2: structured per-tenant facts
scripts/BACKUP-RUNBOOK.template.md # operator runbook with placeholders
memory/
_index.md.template # L2 catalog: the front door
health-snapshot.template.md # L2 topical
stewardship.template.md
skills.template.md
YYYY-MM-DD.template.md # L3 example
infra/
backups.template.md
coding-agents.template.md
search-tools.template.md
voice-tts.template.md
parked-bugs.template.md
known-issues.template.md

scripts/tenant-template/ # provision / deprovision / cloud-init
```

Tier model (documented in `templates/README.md`):

  • L1 always loaded -> small, pointers only, target <80 lines
  • L2 load on demand via `memory_search` / `memory_get`
  • L3 raw daily notes, written same-day
  • L4 archive: historically true, no longer actionable, never deleted

Design priorities (these constrain every template):

  • Lifetime portability: plain markdown + one YAML, zero proprietary formats
  • Honesty about gaps: empty section = "we don't know this yet", never invented
  • Tier discipline: L1 must stay small or the whole design fails
  • Index-as-front-door: a fresh agent finds everything via `memory/_index.md`
  • Stewardship-frame-not-optional: every tenant declares how they relate to their data

2. `feat(agent-backup): privacy guard, large-file guard, exclude npm/`

Three defense-in-depth additions to the backup pipeline. ASCII-only per
house style. Applied to the renamed `agent-backup.sh` (post `aria -> agent`
rename).

  • PURGE adds 'npm': OpenClaw plugins that bundle native binaries
    (Claude Code SDK, codex-acp) install 200MB+ files under
    `~/.openclaw/npm/projects//node_modules/`. GitHub rejects any
    single file >100MB without LFS. Without this exclusion, the next hourly
    push after such a plugin install fails with `remote: error: pre-receive
    hook declined` and the backup silently stops working.
  • Large-file guard (exit code 2): refuses to stage any file >50MB
    before commit. GitHub's hard limit is 100MB; the tighter budget catches
    problems at commit time and keeps clone-from-backup fast.
  • Privacy guard (exit code 4): before push, queries the remote's
    visibility via `gh repo view --json visibility` and refuses to push if
    the result is not PRIVATE. Catches the failure mode where a hand-toggle
    in the GitHub UI would leak state.

Exit-code documentation in the header is updated accordingly.

3. `feat(prep-my-visit): add PDF renderer and example brief fixtures`

Additive only - no changes to existing SKILL.md, reference modules,
validators, or eval suite. Main's current prep-my-visit design stays
intact.

  • `scripts/render_visit_brief.py` (910 lines, WeasyPrint): produces
    `provider.pdf` + `patient.pdf` from an IPS Composition Bundle plus a
    labs JSON. Self-contained: zero imports from other reference modules,
    Python stdlib + WeasyPrint only.
  • `scripts/.gitignore`: excludes `pycache` from the scripts dir.
  • `references/examples/{cardiology-followup, pcp-annual, urgent-same-day}.{json,labs.json}`:
    three synthetic visit-prep packages covering specialist follow-up,
    primary-care annual, and urgent same-day. Synthetic patients
    (Robert Johnson, Maria Chen, Alex Smith), synthetic clinicians,
    synthetic tenant IDs.

Verified end-to-end against main's existing 5-reference-module
layout. All three scenarios produce both PDFs cleanly.

What this PR explicitly does NOT include

  • Cadence-driven auto-delivery of PDFs
  • Email delivery layer (planned for a follow-up PR after design review)
  • Restructured reference modules from the parallel v3 design (5 -> 3
    consolidation) - left for a separate focused review
  • Restructured eval task suite from v3 (15 -> 18 tasks, different naming
    pattern) - same reason

Verification

```bash

bash syntax + dry-run still works

bash -n scripts/agent-backup.sh
bash scripts/agent-backup.sh --help

template tree is self-consistent

node scripts/strip-ai-chars.mjs --dry templates/ scripts/tenant-template/

(reports 0 changes - already ASCII clean)

PDF renderer smoke test

for f in cardiology-followup pcp-annual urgent-same-day; do
python3 skills/prep-my-visit/scripts/render_visit_brief.py \
--bundle skills/prep-my-visit/references/examples/$f.json \
--labs skills/prep-my-visit/references/examples/$f.labs.json \
--out /tmp/test/$f
done

(all six PDFs render: 3 provider + 3 patient)

```

Origin note

This work was originally landed on the `depaulify` branch earlier in
the day. That branch's history was rewritten unrelatedly (a runbook
scrub via `git filter-repo`) which broke shared ancestry with `main`.
`main` also moved forward today through several independent commits
(aria -> agent rename, lookout publish, ASCII sweep, an independent
prep-my-visit design). Rebasing across all those boundaries was
higher-risk than abandoning `depaulify` and re-applying the unique-
valuable subset cleanly on top of current `main`, which is what this
PR is.

`depaulify` is preserved at tag `archived/depaulify-pre-replan-2026-06-04`
in case the history is ever needed.

pswider added 3 commits June 4, 2026 22:52
Introduce the golden-image memory layer every new Tula tenant inherits
when its agent first comes online. Replaces the single-file memory
template the repo had at templates/MEMORY.template.md with a full
tiered structure that mirrors how a healthy long-lived agent organizes
its memory in practice.

Layout (all plain markdown + one YAML, zero proprietary formats):

  templates/
    README.md                                 explains the tier model + placeholder vocab
    AGENTS.template.md                        L1: tier discipline taught here
    MEMORY.template.md                        L1: always-loaded, target <80 lines, pointers not detail
    USER.template.md                          L1: per-tenant identity
    profile.template.yaml                     L2: structured per-tenant facts (location, units, etc)
    scripts/BACKUP-RUNBOOK.template.md        L2/operator: backup runbook with placeholders
    memory/
      _index.md.template                      L2 catalog: the front door for discovery
      health-snapshot.template.md             L2 topical: clinical detail shape
      stewardship.template.md                 L2 topical: data-stewardship frame
      skills.template.md                      L2 topical: installed skills catalog
      YYYY-MM-DD.template.md                  L3 example: daily-note shape
      infra/
        backups.template.md                   L2 infra: backup architecture
        coding-agents.template.md             L2 infra: ACP harness wiring
        search-tools.template.md              L2 infra: web/X search providers
        voice-tts.template.md                 L2 infra: TTS config
        parked-bugs.template.md               L2 infra: bugs to fix later
        known-issues.template.md              L2 infra: recurring problems + fixes

Plus scripts/tenant-template/ (provision/deprovision/cloud-init) for
the golden-image build pipeline that consumes these templates.

Tier model (documented in templates/README.md):
  L1 always loaded -> small, pointers only
  L2 load on demand via memory_search / memory_get
  L3 raw daily notes, written same-day
  L4 archive: historically true, no longer actionable, never deleted

Placeholder vocab uses {{UPPER_SNAKE}} consistent with existing
templates/MEMORY.template.md precedent.

All files ASCII-clean per house style; ran scripts/strip-ai-chars.mjs
against the entire new tree.

Designed to be inherited by every future tenant deployment, so the
priorities are: lifetime portability, honesty about gaps (never invent
content to fill a template), tier discipline (L1 must stay small or
the design fails), index-as-front-door, stewardship-frame-not-optional.

Co-authored work landed via Claude Code on branch depaulify earlier
2026-06-04; re-applied here cleanly on top of current main after that
branch's history diverged from main following an unrelated history
rewrite. No PHI, no tenant-personal content, no leaked operator paths.

Signed-off-by: Paul J. Swider <pswider@realactivity.com>
Three defense-in-depth additions to the backup pipeline. All ASCII-
clean per house style.

1. PURGE adds 'npm' (the per-plugin npm project root). OpenClaw plugins
   that bundle native binaries (Claude Code SDK, codex-acp) install
   200MB+ files under ~/.openclaw/npm/projects/<plugin>/node_modules/.
   GitHub rejects any single file >100MB without LFS. Without this
   exclusion, the next hourly push after such a plugin install fails
   with 'remote: error: pre-receive hook declined' and the backup
   silently stops working. The npm directory is fully regenerable via
   'openclaw plugins install <name>', so it has no business in a state
   backup.

2. Large-file guard (exit code 2). Refuses to stage any file >50MB
   before commit. GitHub's hard limit is 100MB but we set a tighter
   budget so problems are caught at commit time (cheap) rather than
   push time (silent failure mode above) and so clone-from-backup
   stays fast on slow links. Any future plugin that drops big
   binaries somewhere new fails loud, lists the offenders with sizes,
   and tells the operator to add the path to PURGE.

3. Privacy guard (exit code 4). Before push, queries the remote's
   visibility via 'gh repo view --json visibility' and refuses to
   push if the result is not PRIVATE. Catches the failure mode where
   the operator hand-toggles repo visibility in the GitHub UI and
   the next hourly backup leaks state to the world. Soft-warns and
   skips when gh is not installed or the remote is not github.com,
   so the script stays useful in non-github deployments.

Exit-code documentation in the header is updated accordingly.

Originally landed on the depaulify branch as aria-backup.sh changes
before today's aria -> agent rename; re-applied here against the
post-rename script using the AGENT_REPO_DIR variable name.

Signed-off-by: Paul J. Swider <pswider@realactivity.com>
Additive only - no changes to existing SKILL.md, reference modules,
validators, or eval suite. Mains current prep-my-visit design stays
intact.

Adds:
  scripts/render_visit_brief.py (910 lines)
    WeasyPrint-based renderer for provider + patient PDF views from
    an IPS Composition Bundle plus a labs JSON. Self-contained: no
    imports from other reference modules, no runtime dependencies
    beyond Python stdlib and WeasyPrint. Produces full-bleed brand
    bands, gray Epic-style section bars, structural homage to chart
    design (not trade-dress copying). Brand discipline: uniform
    single-ink wordmark, burgundy reserved for accent rule + High
    pills + story rule + review callout, clinical blue for the
    appointment date only.

  scripts/.gitignore
    Excludes Python __pycache__ from the skill scripts dir.

  references/examples/cardiology-followup.json + .labs.json
  references/examples/pcp-annual.json + .labs.json
  references/examples/urgent-same-day.json + .labs.json
    Three synthetic visit-prep packages covering the main cadence
    archetypes (specialist follow-up, primary care annual, urgent
    same-day). Tenant identifiers are TULA-100423, TULA-204815,
    TULA-330914. Patient names are Robert Johnson, Maria Chen,
    Alex Smith - fully synthetic. Used to smoke-test the PDF
    renderer and as eval seed data.

Validation (run from repo root):
  python3 skills/prep-my-visit/scripts/render_visit_brief.py \
    --bundle skills/prep-my-visit/references/examples/<name>.json \
    --labs skills/prep-my-visit/references/examples/<name>.labs.json \
    --out <dir>

Verified end-to-end against mains existing reference module layout
(5 modules, not the consolidated 3 from a parallel design). All
three scenarios produce provider.pdf + patient.pdf cleanly.

Origin: this code was authored on the unmerged feat/prep-my-visit-skill
branch in early June. Cherry-picking the full branch onto current
main was blocked by an unrelated history rewrite this morning that
broke shared ancestry. This commit ports only the additive subset
on top of mains current prep-my-visit (which has 5 reference modules
and 15 eval tasks). The other v3 changes (SKILL.md description,
consolidated reference modules, restructured eval suite) are NOT
included here - those represent a different design choice and
deserve a separate focused review.

Signed-off-by: Paul J. Swider <pswider@realactivity.com>
@pswider pswider force-pushed the feat/tenant-templates-and-guards branch from 5a60964 to 0cf344e Compare June 4, 2026 22:54
@pswider pswider merged commit 52d95e9 into main Jun 4, 2026
1 of 3 checks passed
@pswider pswider deleted the feat/tenant-templates-and-guards branch June 4, 2026 22:56
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