diff --git a/.gitignore b/.gitignore index cc0b0d7..ab70af3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ inst/doc docs /doc/ /Meta/ +*.html +Rplots.pdf diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..c617b04 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,1409 @@ +# flooded + +Portable floodplain delineation from DEM and stream network using the Valley Confinement Algorithm (VCA). Built on `terra` and `sf` for R-native raster processing. Bring your own DEM and streams; works anywhere. + +## Repository Context + +**Repository:** NewGraphEnvironment/flooded +**Primary Language:** R (package) +**Version:** 0.1.0 +**License:** MIT + +## Ecosystem + +`flooded` is the floodplain-delineation engine in a family of packages: + +- **[drift](https://github.com/NewGraphEnvironment/drift)** — fetch, classify, and summarize lateral habitat using flooded output +- **[fly](https://github.com/NewGraphEnvironment/fly)** — interactive mapping and layer toggle for drift/flooded results +- **[diggs](https://github.com/NewGraphEnvironment/diggs)** — Shiny app front-end for fly + +Pipeline flow: `flooded` (delineate) → `drift` (classify) → `fly` (map) → `diggs` (Shiny UI) + +## Architecture + +``` +R/ + fl_valley_confine.R — top-level wrapper (one-call delineation) + fl_flood_model.R — bankfull regression → flood depth + fl_flood_surface.R — cost-distance flood surface + fl_flood_assemble.R — combine flood + slope masks + fl_flood_trim.R — remove disconnected patches + fl_valley_poly.R — raster → sf polygon conversion + fl_cost_distance.R — weighted cost-distance from streams + fl_mask*.R — slope/distance masking helpers + fl_patch*.R — patch connectivity and removal + fl_stream_rasterize.R — burn streams into DEM grid +vignettes/ + valley-confinement.Rmd — full walkthrough with bundled test data + stac-dem.Rmd — fetch DEM from STAC catalog +tests/testthat/ — unit tests for each fl_* function +``` + +## Key Patterns + +- All exported functions prefixed `fl_` +- `terra::rast` and `sf::st_read` for spatial I/O +- `terra::terraOptions(threads = N)` for parallel raster ops +- Bankfull regression requires both `upstream_area` (ha) and `precip` (mm) +- Optional `whitebox` dependency for advanced hydro operations + +<\!-- BEGIN SOUL CONVENTIONS — DO NOT EDIT BELOW THIS LINE --> + +# Agent Teams Orchestration + +Checklist for effectively running Claude Code agent teams. Agent teams coordinate multiple Claude Code instances working in parallel with shared tasks and inter-agent messaging. + +**Source:** [code.claude.com/docs/en/agent-teams](https://code.claude.com/docs/en/agent-teams) + +**Status:** Experimental. Disabled by default. + +## Before You Start + +- [ ] **Enable the feature flag** in `settings.json` or environment: + ```json + { "env": { "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1" } } + ``` +- [ ] **Choose display mode** — `in-process` (default, any terminal) or split panes (requires tmux or iTerm2). Set `teammateMode` in `settings.json` or pass `--teammate-mode`. +- [ ] **Install tmux** if using split panes (`brew install tmux`). Not supported in VS Code terminal, Windows Terminal, or Ghostty. +- [ ] **Pre-approve common permissions** in your permission settings before spawning teammates — permission prompts bubble up to the lead and create friction. + +## When to Use Teams (vs. Subagents) + +Use agent teams when teammates need to **talk to each other** — research debates, competing hypotheses, cross-layer coordination. Use subagents when you just need focused workers that report back results. + +**Good fit:** parallel code review (security + performance + tests), investigating competing bug hypotheses, new modules that don't share files, research from multiple angles. + +**Bad fit:** sequential tasks, edits to the same file, simple work where coordination overhead exceeds benefit. + +## Starting a Team + +- [ ] **Give enough context in the spawn prompt** — teammates don't inherit the lead's conversation history. Include file paths, architecture context, and specific focus areas. +- [ ] **Size tasks right** — too small = overhead wasted, too large = risk of divergence. Aim for self-contained units with clear deliverables. 5-6 tasks per teammate keeps everyone productive. +- [ ] **Specify models explicitly** if you want cost control (e.g., "Use Sonnet for each teammate"). +- [ ] **Ensure teammates own different files** — two teammates editing the same file leads to overwrites. + +## During Execution + +- [ ] **Use delegate mode** (Shift+Tab) to prevent the lead from implementing tasks itself instead of waiting for teammates. +- [ ] **Monitor and steer** — check progress, redirect approaches that aren't working. Don't let a team run unattended too long. +- [ ] **Message teammates directly** — Shift+Up/Down in in-process mode, click pane in split mode. Give additional instructions or ask follow-ups. +- [ ] **If the lead starts coding instead of delegating**, tell it: "Wait for your teammates to complete their tasks before proceeding." + +## Quality Gates + +- [ ] **Require plan approval** for risky work — teammates plan in read-only mode until the lead approves. Give criteria: "only approve plans that include test coverage." +- [ ] **Use hooks** for automated enforcement: + - `TeammateIdle` — exit code 2 sends feedback and keeps the teammate working + - `TaskCompleted` — exit code 2 prevents completion and sends feedback + +## Cleanup (Critical) + +- [ ] **Shut down all teammates first** — ask the lead: "Ask the researcher teammate to shut down." Teammates finish their current action before stopping. +- [ ] **Then clean up the team** — tell the lead: "Clean up the team." This removes shared resources. Cleanup fails if teammates are still running. +- [ ] **Always clean up via the lead** — teammates should never run cleanup (their team context may not resolve correctly). +- [ ] **Check for orphaned tmux sessions** after cleanup: + ```bash + tmux ls + tmux kill-session -t + ``` + +## Known Limitations + +- **No session resumption** — `/resume` and `/rewind` don't restore in-process teammates. After resuming, tell the lead to spawn new teammates. +- **Task status can lag** — teammates sometimes fail to mark tasks complete, blocking dependents. Manually check and update if stuck. +- **One team per session** — clean up before starting a new team. +- **No nested teams** — only the lead can manage the team. +- **Lead is fixed** — can't promote a teammate or transfer leadership. +- **Permissions set at spawn** — all teammates inherit the lead's mode. Change individually after spawning if needed. + +## Quick Reference + +| Action | How | +|--------|-----| +| Cycle teammates | Shift+Up/Down | +| View teammate session | Enter on selected teammate | +| Interrupt teammate | Escape while viewing | +| Toggle task list | Ctrl+T | +| Enable delegate mode | Shift+Tab | +| Force in-process mode | `claude --teammate-mode in-process` | + +# Bookdown Conventions + +Standards for bookdown report projects across New Graph Environment. + +## Template Repos + +These are the canonical references. Child repos inherit their structure and patterns. + +- [mybookdown-template](https://github.com/NewGraphEnvironment/mybookdown-template) — General-purpose bookdown starter +- [fish_passage_template_reporting](https://github.com/NewGraphEnvironment/fish_passage_template_reporting) — Fish passage reporting template + +When in doubt, match what the template does. When the template and production repos disagree, production wins — update the template. + +## Project Structure + +``` +project/ +├── index.Rmd # Master config, YAML params, setup chunks +├── _bookdown.yml # book_filename, output_dir: "docs" +├── _output.yml # Gitbook, pagedown, pdf_book config +├── 0100-intro.Rmd # Chapter numbering: 4-digit, 100s increment +├── 0200-background.Rmd +├── 0300-methods.Rmd +├── 0400-results.Rmd +├── 0500-*.Rmd # Discussion/recommendations +├── 0800-appendix-*.Rmd # Appendices (site-specific in fish passage) +├── 2000-references.Rmd # Auto-generated from .bib +├── 2090-report-change-log.Rmd # Auto-generated from NEWS.md +├── 2100-session-info.Rmd # Reproducibility +├── NEWS.md # Changelog (semantic versioning) +├── scripts/ +│ ├── packages.R # Package loading (renv-managed) +│ ├── functions.R # Project-specific functions +│ ├── staticimports.R # Auto-generated from staticimports pkg +│ ├── setup_docs.R # Build helper +│ └── run.R # Local build (gitbook + PDF) +├── fig/ # Figures (organized by chapter or type) +├── data/ # Project data +├── docs/ # Rendered output (GitHub Pages) +├── renv.lock # Locked dependencies +└── .Rprofile # Activates renv +``` + +## Setup Chunk Pattern + +Every `index.Rmd` follows this setup sequence. Order matters. + +```r +# 1. Gitbook vs PDF switch +gitbook_on <- TRUE + +# 2. Knitr options +knitr::opts_chunk$set( + echo = identical(gitbook_on, TRUE), # Show code only in gitbook + message = FALSE, warning = FALSE, + dpi = 60, out.width = "100%" +) +options(scipen = 999) +options(knitr.kable.NA = '--') +options(knitr.kable.NAN = '--') + +# 3. Source in order: packages → static imports → functions → data +source('scripts/packages.R') +source('scripts/staticimports.R') +source('scripts/functions.R') +``` + +Responsive settings by output format: + +```r +# Gitbook +photo_width <- "100%"; font_set <- 11 + +# PDF (paged.js) +photo_width <- "80%"; font_set <- 9 +``` + +## YAML Parameters + +Parameters live in `index.Rmd` frontmatter (not a separate file). Child repos override by editing these values. + +```yaml +params: + repo_url: 'https://github.com/NewGraphEnvironment/repo_name' + report_url: 'https://www.newgraphenvironment.com/repo_name/' + update_packages: FALSE + update_bib: TRUE + gitbook_on: TRUE +``` + +Fish passage repos add project-specific params (`project_region`, `model_species`, `wsg_code`, update flags for forms). These are project-specific — don't add them to the general template. + +## Chunk Naming + +Embed context and purpose in chunk names. The principle is universal; the codes are project-specific. + +**Pattern:** `{type}-{system}-{description}` + +| Type | Examples | +|------|---------| +| Tables | `tab-kln-load-int-yr`, `tab-sites-sum`, `tab-wshd-196332` | +| Figures | `plot-wq-kln-quadratic`, `map-interactive`, `map-196332` | +| Photos | `photo-196332-01`, `photo-196332-d01` (dual layout) | + +## Cross-References + +Bookdown auto-prepends `fig:` or `tab:` to chunk names. + +- **Tables:** `Table \@ref(tab:chunk-name)` +- **Figures:** `Figure \@ref(fig:chunk-name)` + +No `fig:` or `tab:` prefix in the chunk label itself — bookdown adds it. + +## Table Caption Workaround + +Interactive tables (DT) can't use standard bookdown captions. Use the `my_tab_caption()` function from `staticimports.R`. + +**Pattern:** Separate `-cap` chunk from table chunk. + +```r +# Caption chunk — must use results="asis" +{r tab-sites-sum-cap, results="asis"} +my_caption <- "Summary of fish passage assessment procedures." +my_tab_caption() +``` + +```r +# Table chunk — renders the DT +{r tab-sites-sum} +data |> my_dt_table(page_length = 20, cols_freeze_left = 0) +``` + +`my_tab_caption()` auto-grabs the chunk label via `knitr::opts_current$get()$label` and wraps it in HTML caption tags that bookdown can cross-reference. + +## Photo Layout + +Separate prep chunk (find the file) from display chunk (render it). + +```r +# Prep — find the photo +{r photo-196332-01-prep} +my_photo1 <- fpr::fpr_photo_pull_by_str(str_to_pull = 'ds_typical_1_') +my_caption1 <- paste0('Typical habitat downstream of PSCIS crossing ', my_site, '.') +``` + +```r +# Gitbook — full width +{r photo-196332-01, fig.cap=my_caption1, out.width=photo_width, eval=gitbook_on} +knitr::include_graphics(my_photo1) +``` + +```r +# PDF — side by side with 1% spacer +{r photo-196332-d01, fig.show="hold", out.width=c("49.5%","1%","49.5%"), eval=identical(gitbook_on, FALSE)} +knitr::include_graphics(my_photo1) +knitr::include_graphics("fig/pixel.png") +knitr::include_graphics(my_photo2) +``` + +## Bibliography + +**`references.bib` is auto-generated — never edit it manually.** On each build, `rbbt::bbt_write_bib()` scans all `.Rmd` files for `@citekey` references, pulls the BibTeX from Zotero's Better BibTeX, and overwrites `references.bib`. Any manual additions will be lost on the next build. + +To add a reference: add it to the shared Zotero group library, use its BBT citation key (`@key`) in the `.Rmd` text, and build. rbbt handles the rest. + +```yaml +bibliography: "`r rbbt::bbt_write_bib('references.bib', overwrite = TRUE)`" +biblio-style: apalike +link-citations: no +``` + +When `update_bib: FALSE` in params, the build uses the existing `references.bib` without regenerating — useful for offline builds or CI where Zotero isn't running. + +Auto-generate package citations: + +```r +knitr::write_bib(c(.packages(), 'bookdown', 'knitr', 'rmarkdown'), 'packages.bib') +``` + +Use `nocite:` in YAML to include references not cited in text. + +## Acknowledgement & AI Disclosure + +`index.Rmd` contains two separate front-matter sections after the setup chunks: + +### Acknowledgement {.front-matter .unnumbered} + +Three parts, in order: + +1. **Personal connection to land** (template-level, same across all reports): + > At New Graph Environment, we understand our well-being as inseparable from the health of the land and waters we work within. When we care for ecosystems, we care for ourselves and for the communities connected to them. This relationship is not metaphorical — it is the foundation of our practice. + +2. **Colonial acknowledgement** (template-level): + > Modern civilization has a long journey ahead to acknowledge and address the historic and ongoing impacts of colonialism... + +3. **Territorial acknowledgement** (project-specific, must be edited per report): Name the Nations, governance systems, watersheds, and species relevant to the project. Do not use a generic office-location acknowledgement — tie it to the territory where the work happens. See the Wedzin Kwa chinook example for the pattern. + +4. **Funding and partners** (project-specific). + +### AI Disclosure + +Do not use a `#` heading for the disclosure — this creates a separate chapter page in gitbook. Instead, add it to the YAML `date:` field so it renders in the title block: + +```yaml +date: | + | + | Version X.X.X DRAFT `r format(Sys.Date(), "%Y-%m-%d")` + | + | *Claude Sonnet 4.6 (Anthropic) assisted with literature synthesis, drafting, and technical writing. All scientific interpretation, data analysis, and conclusions are the responsibility of the authors.* +``` + +**Wording principle:** Be accurate about what the LLM did. It assisted with drafting and synthesis — it did not make scientific interpretations or conclusions. Do not say "independently verified by the authors" (redundant) or attribute "ecological assessments" to the LLM. + +For regulatory/EGBC-stamped work, use the extended disclaimer from `soul/research/20260212_ai_disclosure_research.md`. See NewGraphEnvironment/mybookdown-template#89. + +## Conditional Rendering (Gitbook vs PDF) + +A single boolean `gitbook_on` controls output format throughout. + +```r +# Show only in gitbook +{r map-interactive, eval=gitbook_on} + +# Show only in PDF +{r fig-print-only, eval=identical(gitbook_on, FALSE)} + +# Conditional inline content +`r if(identical(gitbook_on, FALSE)) knitr::asis_output("This report is available online...")` + +# Page breaks for PDF only +`r if(gitbook_on){knitr::asis_output("")} else knitr::asis_output("\\pagebreak")` +``` + +## Versioning and Changelog + +Reports use MAJOR.MINOR.PATCH versioning with a `NEWS.md` changelog. + +**Version in `index.Rmd` YAML:** +```yaml +date: | + | + | Version 1.1.0 DRAFT `r format(Sys.Date(), "%Y-%m-%d")` +``` + +**NEWS.md format:** +```markdown +## 1.1.0 (2026-02-17) + +- Add feature X +- Fix issue Y ([Issue #N](https://github.com/Org/repo/issues/N)) +``` + +**Auto-append as appendix** via `my_news_to_appendix()` in `staticimports.R`: +```r +news_to_appendix(md_name = "NEWS.md", rmd_name = "2090-report-change-log.Rmd") +``` + +**Convention:** +- Bump version in `index.Rmd` and add NEWS entry for every commit to main that changes report content +- Tag releases: `git tag -a v1.1.0 -m "v1.1.0: Brief description"` +- MAJOR: structural changes, new chapters, methodology changes +- MINOR: new content, figures, tables, discussion sections +- PATCH: prose fixes, corrections, formatting + +## COG Viewer Embedding + +Always use `ngr::ngr_str_viewer_cog()` — never hardcode viewer iframes. + +```r +knitr::asis_output(ngr::ngr_str_viewer_cog("https://bucket.s3.us-west-2.amazonaws.com/ortho.tif")) +``` + +The function includes a cache-busting `?v=` parameter. Bump `v` in the function default when `viewer.html` has breaking changes. + +## Dependency Management + +Use `renv` for reproducible package management: +- `.Rprofile` activates renv on startup +- `renv::restore()` installs from lockfile +- `renv::snapshot()` updates lockfile after adding packages +- Use `pak::pak("pkg")` to install (not `install.packages`) + +## Known Drift + +Production repos (2024-2025) have drifted from templates in these areas. When working in a child repo, match what that repo does, not the template: + +- **Script naming in `02_reporting/`** — older repos use `tables.R`, `0165-read-sqlite.R`; newer repos use numbered `0130-tables.R`. Follow the repo you're in. +- **Removed packages** — `elevatr`, `rayshader`, `arrow` removed from production but still in template. +- **`staticimports::import()` call** — some repos skip it and source `staticimports.R` directly. +- **Hardcoded vs parameterized years** — older repos hardcode years in file paths; newer repos use `params$project_year`. Prefer parameterized. + +# Cartography + +## Style Registry + +Use the `gq` package for all shared layer symbology. Never hardcode hex color values when a registry style exists. + +```r +library(gq) +reg <- gq_reg_main() # load once per script — 51+ layers +``` + +**Core pattern:** `reg$layers$lake`, `reg$layers$road`, `reg$layers$bec_zone`, etc. + +### Translators + +| Target | Simple layer | Classified layer | +|--------|-------------|-----------------| +| tmap | `gq_tmap_style(layer)` → `do.call(tm_polygons, ...)` | `gq_tmap_classes(layer)` → field, values, labels | +| mapgl | `gq_mapgl_style(layer)` → paint properties | `gq_mapgl_classes(layer)` → match expression | + +### Custom styles + +For project-specific layers not in the main registry, use a hand-curated CSV and merge: + +```r +reg <- gq_reg_merge(gq_reg_main(), gq_reg_read_csv("path/to/custom.csv")) +``` + +Install: `pak::pak("NewGraphEnvironment/gq")` + +## Map Targets + +| Output | Tool | When | +|--------|------|------| +| PDF / print figures | `tmap` v4 | Bookdown PDF, static reports | +| Interactive HTML | `mapgl` (MapLibre GL) | Bookdown gitbook, memos, web pages | +| QGIS project | Native QML | Field work, Mergin Maps | + +## Key Rules + +- **`sf_use_s2(FALSE)`** at top of every mapping script +- **Compute area BEFORE simplify** in SQL +- **No map title** — title belongs in the report caption +- **Legend over least-important terrain** — swap legend and logo sides when it reduces AOI occlusion. No fixed convention for which side. +- **Four-corner rule** — legend, logo, scale bar, keymap each get their own corner. Never stack two in the same quadrant. +- **Bbox must match canvas aspect ratio** — compute the ratio from geographic extents and page dimensions. Mismatch causes white space bands. +- **Consistent element-to-frame spacing** — all inset elements should have visually equal margins from the frame edge +- **Map fills to frame** — basemap extends edge-to-edge, no dead bands. Use near-zero `inner.margins` and `outer.margins`. +- **Suppress auto-legends** — build manual ones from registry values +- **ALL CAPS labels appear larger** — use title case for legend labels (gq `gq_tmap_classes()` handles this automatically via `to_title()` fallback) + +## Self-Review (after every render) + +Read the PNG and check before showing anyone: + +1. Correct polygon/study area shown? (verify source data, not just the bbox) +2. Map fills the page? (no white/black bands) +3. Keymap inside frame with spacing from edge? +4. No element overlap? (each in its own corner) +5. Legend over least-important terrain? +6. Consistent spacing across all elements? +7. Scale bar breaks appropriate for extent? + +See the `cartography` skill for full reference: basemap blending, BC spatial data queries, label hierarchy, mapgl gotchas, and worked examples. + +## Land Cover Change + +Use [drift](https://github.com/NewGraphEnvironment/drift) and [flooded](https://github.com/NewGraphEnvironment/flooded) together for riparian land cover change analysis. flooded delineates floodplain extents from DEMs and stream networks; drift tracks what's changing inside them over time. + +**Pipeline:** + +```r +# 1. Delineate floodplain AOI (flooded) +valleys <- flooded::fl_valley_confine(dem, streams) + +# 2. Fetch, classify, summarize (drift) +rasters <- drift::dft_stac_fetch(aoi, source = "io-lulc", years = c(2017, 2020, 2023)) +classified <- drift::dft_rast_classify(rasters, source = "io-lulc") +summary <- drift::dft_rast_summarize(classified, unit = "ha") + +# 3. Interactive map with layer toggle +drift::dft_map_interactive(classified, aoi = aoi) +``` + +- Class colors come from drift's shipped class tables (IO LULC, ESA WorldCover) +- For production COGs on S3, `dft_map_interactive()` serves tiles via titiler — set `options(drift.titiler_url = "...")` +- See the [drift vignette](https://www.newgraphenvironment.com/drift/articles/neexdzii-kwa.html) for a worked example (Neexdzii Kwa floodplain, 2017-2023) + +# Code Check Conventions + +Structured checklist for reviewing diffs before commit. Used by `/code-check`. +Add new checks here when a bug class is discovered — they compound over time. + +## Shell Scripts + +### Quoting +- Variables in double-quoted strings containing single quotes break if value has `'` +- `"echo '${VAR}'"` — if VAR contains `'`, shell syntax breaks +- Use `printf '%s\n' "$VAR" | command` to pipe values safely +- Heredocs: unquoted `<" && pwd)"` +- After moving scripts, verify `../` depth still resolves correctly +- Usage comments should match actual script location + +### Silent Failures +- `|| true` hides real errors — is the failure actually safe to ignore? +- Empty variable before destructive operation (rm, destroy) — add guard: `[ -n "$VAR" ] || exit 1` +- `grep` returning empty silently — downstream commands get empty input + +### Process Visibility +- Secrets passed as command-line args are visible in `ps aux` +- Use env files, stdin pipes, or temp files with `chmod 600` instead + +## Cloud-Init (YAML) + +### ASCII +- Must be pure ASCII — em dashes, curly quotes, arrows cause silent parse failure +- Check with: `perl -ne 'print "$.: $_" if /[^\x00-\x7F]/' file.yaml` + +### State +- `cloud-init clean` causes full re-provisioning on next boot — almost never what you want before snapshot +- Use `tailscale logout` not `tailscale down` before snapshot (deregister vs disconnect) + +### Template Variables +- Secrets rendered via `templatefile()` are readable at `169.254.169.254` metadata endpoint +- Acceptable for ephemeral machines, document the tradeoff + +## OpenTofu / Terraform + +### State +- Parsing `tofu state show` text output is fragile — use `tofu output` instead +- Missing outputs that scripts need — add them to main.tf +- Snapshot/image IDs in tfvars after deleting the snapshot — stale reference + +### Destructive Operations +- Validate resource IDs before destroy: `[ -n "$ID" ] || exit 1` +- `tofu destroy` without `-target` destroys everything including reserved IPs +- Snapshot ID extraction: use `--resource droplet` and `grep -F` for exact match + +## Security + +### Secrets in Committed Files +- `.tfvars` must be gitignored (contains tokens, passwords) +- `.tfvars.example` should have all variables with empty/placeholder values +- Sensitive variables need `sensitive = true` in variables.tf + +### Firewall Defaults +- `0.0.0.0/0` for SSH is world-open — document if intentional +- If access is gated by Tailscale, say so explicitly + +### Credentials +- Passwords with special chars (`'`, `"`, `$`, `!`) break naive shell quoting +- `printf '%q'` escapes values for shell safety +- Temp files for secrets: create with `chmod 600`, delete after use + +## R / Package Installation + +### pak Behavior +- pak stops on first unresolvable package — all subsequent packages are skipped +- Removed CRAN packages (like `leaflet.extras`) must move to GitHub source +- PPPM binaries may lag a few hours behind new CRAN releases + +### Reproducibility +- Branch pins (`pkg@branch`) are not reproducible — document why used +- Pinned download URLs (RStudio .deb) go stale — document where to update + +## General + +### Diff Hygiene +- Every changed line should trace to the task — no drive-by cleanups +- New files need: are they gitignored if sensitive? executable if scripts? +- Deleted files: check for references in other files, docs, READMEs + +### Documentation Staleness +- Moving/renaming scripts: update CLAUDE.md, READMEs, usage comments +- New variables: update .tfvars.example +- New workflows: update relevant README + +# Communications Conventions + +Standards for external communications across New Graph Environment. + +[compost](https://github.com/NewGraphEnvironment/compost) is the working repo for email drafts, scripts, contact management, and Gmail utilities. These conventions capture the universal principles; compost has the implementation details. + +## Tone + +Three levels. Default to casual unless context dictates otherwise. + +| Level | When | Style | +|-------|------|-------| +| **Casual** | Established working relationships | Professional but warm. Direct, concise. No slang. | +| **Very casual** | Close collaborators with rapport | Colloquial OK. Light humor. Slang acceptable. | +| **Formal** | New contacts, senior officials, formal requests | Full sentences, no contractions, state purpose early. | + +**Collaborative, not directive.** Acknowledge their constraints: + +- **Avoid:** "Work these in as makes sense for your lab" +- **Better:** "If you're able to work these in when it fits your schedule that would be really helpful" + +## Email Workflow + +Draft in markdown, convert to HTML at send time via gmailr. See compost for script templates, OAuth setup, and `search_gmail.R`. + +**File naming:** `YYYYMMDD_recipient_topic_draft.md` + `YYYYMMDD_recipient_topic.R` + +**Key gotchas** (documented in detail in compost): +- Gmail strips `