feat: tenant templates, backup guards, and prep-my-visit PDF renderer#4
Merged
Conversation
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>
5a60964 to
0cf344e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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`):
Design priorities (these constrain every template):
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).
(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.
before commit. GitHub's hard limit is 100MB; the tighter budget catches
problems at commit time and keeps clone-from-backup fast.
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.
`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.
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
consolidation) - left for a separate focused review
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.