4 Commits
Author SHA1 Message Date
jared 04a5ee958b docs: record session
R-CMD-check / R CMD check (push) Successful in 5m11s
Journal entry for the roborev triage session and a regenerated status board.
Adds AGENTS.md and .roborev.toml to the packaging workstream so root-level
config commits attribute to a stream instead of landing unreferenced.
2026-08-24 00:15:09 -04:00
jared cd1af67a3a fix(packaging): exclude scoped chore and docs commits from roborev
The exclusion patterns are substring matches, so `chore:` never matched
`chore(packaging):`. Because the cadence rule in AGENTS.md mandates the
`type(ws):` scope, every chore and docs commit was being reviewed -- job 15
spent a thorough review on a config-only commit. Adds the `chore(` and `docs(`
forms.

Also names pm/compass.toml as authoritative for the review rules, so the
prose copy in AGENTS.md cannot silently drift from the generated
.roborev.toml. Raised by roborev job 15.
2026-08-24 00:13:23 -04:00
jared 496b9ed9ac docs: record session (#21, #22)
Journal entry for the compass init session and the first generated status
board, published as pinned issue #23.
2026-08-23 23:39:35 -04:00
jared 7cad98b434 chore(packaging): initialise compass project tracking
Adds pm/compass.toml with five workstreams (theme, logo, quarto, helpers,
packaging), the journal and decision-record scaffold, and a composed
.roborev.toml carrying nine project-specific review rules derived from the
package's actual conventions.

Also adds AGENTS.md as the cross-tool conventions file, wires project memory
into .agent/memory/ so it travels between machines, and untracks
tests/testthat/Rplots.pdf, which churned on every test run.
2026-08-23 23:33:48 -04:00
9 changed files with 386 additions and 4 deletions
View File
+11 -4
View File
@@ -1,4 +1,11 @@
.Rproj.user
.Rhistory
.RData
.Ruserdata
.Rproj.user
.Rhistory
.RData
.Ruserdata
.compass-cache/
# roborev snapshots
/.roborev/
# Test run debris
tests/testthat/Rplots.pdf
tests/testthat/_problems/
+64
View File
@@ -0,0 +1,64 @@
# roborev configuration, initialised by compass.
# Reviews are queued to a background daemon -- they never block a commit.
post_commit_review = 'commit'
# Scoped conventional commits (`chore(packaging):`) do not contain `chore:`,
# and this repo's cadence rule mandates the scope -- so both forms are listed.
excluded_commit_patterns = ['WIP', 'chore:', 'chore(', 'docs:', 'docs(', 'Merge ']
review_guidelines = '''
# --- compass:begin (generated -- edit the sources, not this) ---
- Prefer returning new values to mutating arguments in place. A function that edits
its caller's object is a bug waiting for a second caller.
- Validate at system boundaries -- user input, API responses, file contents, config.
Fail fast with a message naming the field and the file.
- Never swallow an error. Handle it or let it propagate; a bare catch that continues
is worse than a crash.
- No hardcoded secrets, tokens, or credentials, and no secrets in log output or error
messages.
- Parameterise every query. String-built SQL is a defect even when the input looks safe.
- Keep functions under roughly 50 lines and files under roughly 400. Flag nesting
deeper than four levels.
- No magic numbers or hardcoded paths -- name them as constants or read them from config.
- New behaviour needs a test. A bug fix needs a test that fails without the fix.
- Prose a person reads -- an issue title or body, a journal entry, a decision record,
the narrative on the status board -- names the action or the thing, not the shape of
the machinery. Flag "gate", "seam", "surface area", "load-bearing", "first-class",
"primitive", "blast radius". A project's own defined vocabulary is not the target.
- Use the native pipe `|>`, not magrittr `%>%`.
- snake_case for objects and functions; UPPER_SNAKE for constants. Never use `.` as a
word separator in a function name -- it collides with S3 dispatch.
- Validate arguments at the top of exported functions with `stopifnot()` or an explicit
check, and say which argument was wrong.
- Never `setDT()`, `set()`, or otherwise modify by reference a data.table the caller
still owns. `as.data.table()` copies; use it.
- Prefer `vapply()` to `sapply()` -- `sapply()` silently returns a list when the type
varies, which turns a type error into a downstream mystery.
- Use `seq_len(n)` / `seq_along(x)`, never `1:n`, which iterates backwards when n is 0.
- Compare strings with `==` only after checking for NA; use `identical()` for scalars
where NA would be wrong.
- Do not call `library()` inside package or module files; attach packages in scripts and
test helpers only.
- Namespace-qualify calls into other packages (`stats::sd`) in code that is sourced.
- Every exported function needs roxygen with `@param` for each argument (type, meaning,
and why the default is what it is) and `@return`. Add `@examples` for exported API.
- Declare dependencies in DESCRIPTION. Prefer base R or an existing dependency over
adding a new one; a package with zero hard deps is worth keeping that way.
- Signal errors with `stop()` carrying a condition class, so callers can catch the kind
rather than matching on message text.
- Keep internals internal. Export only what a user needs; an accidentally exported
helper becomes an API you have to keep.
- Tests use testthat edition 3. Each test is self-sufficient -- no reliance on state
left by an earlier test or on a fixture built elsewhere in the file.
- Prefer duplication in tests over a helper that hides what is being asserted.
- Brand colors come from `civilytics_colors`; never write a hex literal in theme, scale, logo, or table code. `R/flextable.R` still carries off-brand Bootstrap defaults (#2c3e50, #f0f0eb, #888888, #cccccc, #555555) -- do not add more.
- No tidyverse dependency. Imports is base R plus ggplot2, grid/gridExtra, png/jpeg, stringr, stringdist, showtext/sysfonts, jsonlite. Reject dplyr, purrr, magrittr, tibble, and data.table; use base R idioms.
- NAMESPACE is roxygen-generated. Declare imports with `@importFrom` in the roxygen block and re-run roxygen; never hand-edit NAMESPACE.
- Themes default to a transparent background (`paper_bg = FALSE`) so plots composite onto any Quarto, Reveal, or Typst background. Making an opaque background the default is a regression, not a preference.
- Theme functions thread `ink`/`paper`/`accent` into ggplot2 4.0's base-theme parameters rather than setting element colors ad hoc. ggplot2 >= 4.0 is a hard dependency, so use S7 `@` property access on plot and theme objects, not `$`.
- User-facing progress goes through `message()` so callers can suppress it. No `cat()` or `print()` in package code.
- Nothing personal or client-identifying is vendored into `inst/` -- brand assets only. Version 0.3.2 removed headshots for exactly this reason.
- The camelCase exports (`countCleanr`, `dbSafeNames`, `simpleCap`, `waldInterval`, `countDots`, `countNA`, `findDots`, `nvals`) are frozen public API; do not rename them. New functions are snake_case.
- `R/theme.R` and `R/logo.R` are long by design -- one file per surface, with dense roxygen. File length is not a finding in this package; flag a single function over roughly 80 lines instead.
# --- compass:end ---
'''
+93
View File
@@ -0,0 +1,93 @@
# AGENTS.md
`civilytics` is the house R package for Civilytics Consulting: a ggplot2 brand theme
system, curated palettes, logo composition, Quarto/Typst/Reveal templates, and
data-wrangling helpers for public-sector analysis. It is a library other projects
depend on, so a breaking change here breaks reports that are already published.
## Project tracking
This repo is managed with compass. `pm/compass.toml`
defines the workstreams; `pm/JOURNAL.md` records sessions; `pm/decisions/` holds
numbered, immutable decision records. Ask compass where things stand rather than
reading the config back.
Workstreams (the `ws/` label on every issue):
| `ws/` | covers |
|---|---|
| `theme` | `R/theme.R`, `R/colors.R`, `R/fonts.R` — themes, palettes, font loading |
| `logo` | `R/logo.R`, `R/flextable.R`, `inst/img/` — logo and branded output |
| `quarto` | `R/quarto.R`, `inst/quarto/` — HTML, PDF, Typst, Reveal templates |
| `helpers` | `R/utils.R`, `R/prop_conf.R`, `R/join_utilities.R`, `R/db.R`, `R/notifications.R` |
| `packaging` | `DESCRIPTION`, `NAMESPACE`, `Makefile`, `Dockerfile`, `.gitea/workflows/` |
## Commit cadence
One coherent unit per commit, subject line:
```
type(ws): subject (#N)
```
`type` is one of `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `perf`, `ci`.
`ws` is a workstream id from the table above. `#N` is the issue, when there is one.
## Conventions
`pm/compass.toml` is authoritative for these rules. Its `[roborev]
project_guidelines` are composed into `.roborev.toml`, so change them there and
re-run compass rather than editing `.roborev.toml` by hand. The prose below is the
same rules stated for a human reader; if the two ever disagree, `pm/compass.toml`
wins.
**Dependencies.** Base R plus ggplot2 (>= 4.0), grid/gridExtra, png/jpeg, stringr,
stringdist, showtext/sysfonts, jsonlite. No tidyverse: no dplyr, purrr, magrittr,
tibble, or data.table. Prefer an existing dependency or base R over adding one —
this package is installed into other people's environments.
**Brand colors** live in `civilytics_colors` and nowhere else. Never write a hex
literal in theme, scale, logo, or table code. (`R/flextable.R` still carries
off-brand Bootstrap defaults; that is known debt, not a pattern to copy.)
**Themes** default to a transparent background (`paper_bg = FALSE`) so plots
composite onto any Quarto, Reveal, or Typst background. Thread `ink`, `paper`, and
`accent` into ggplot2 4.0's base-theme parameters rather than setting element colors
one at a time. ggplot2 >= 4.0 is a hard dependency, so use S7 `@` property access on
plot and theme objects, not `$`.
**Naming.** New functions are snake_case. The camelCase exports (`countCleanr`,
`dbSafeNames`, `simpleCap`, `waldInterval`, `countDots`, `countNA`, `findDots`,
`nvals`) are frozen public API — do not rename them.
**Documentation.** Roxygen generates both `man/` and `NAMESPACE`. Declare imports
with `@importFrom` in the roxygen block; never hand-edit `NAMESPACE`. Every exported
function needs `@param` for each argument and `@return`; exported API needs
`@examples`. Re-run roxygen in the same commit as the code change — compass files
documentation debt when code moves and its `man/` pages do not.
**Output.** User-facing progress goes through `message()` so callers can suppress it.
No `cat()` or `print()` in package code.
**Assets.** Nothing personal or client-identifying is vendored into `inst/` — brand
assets only. Version 0.3.2 removed headshots for exactly this reason.
## Testing
testthat edition 3, one file per source file (`tests/testthat/test_theme.R` etc.).
Tests are self-sufficient: no reliance on state left by an earlier test. New
behaviour needs a test; a bug fix needs a test that fails without the fix.
```zsh
make check # R CMD check --no-manual against a built tarball
Rscript -e 'devtools::test()'
```
CI runs `R CMD check` on `rocker/r-ver:4.6` via `.gitea/workflows/check.yaml` for
every push and PR to `master`.
## Release
Bump `Version` in `DESCRIPTION` and add a `NEWS.md` entry under **New features**,
**Bug fixes**, or **Internal**. `NEWS.md` is the changelog; compass tracks the work
that led to it, not the release itself.
+34
View File
@@ -0,0 +1,34 @@
# Project journal
Append-only, newest first. **Entries are never edited** — the value of this file is
that it records what was believed at the time, including the parts that turned out
wrong. Where things stand *today* is in `STATUS.md`, which is generated.
Four lines per entry. The analysis belongs in the issue or the decision record; this
file carries the reasoning and the pointers.
- **Why** — the driver. The one line git cannot reconstruct later.
- **Obligates** — issues this change created elsewhere. Numbers, not prose.
- **Refs** — commits, issues, decision records.
---
## 2026-08-24 · packaging · roborev exclusion patterns fixed; init review triaged
**Why:** roborev reviewed the init commit because `excluded_commit_patterns` are
substring matches and `chore:` never matches the `chore(packaging):` form the
cadence rule mandates — the commit convention was silently defeating its own
review filter. Two low findings came back; one was half-right, one rested on a
generator it could not see.
**Obligates:** —
**Refs:** cd1af67, roborev job 15
## 2026-08-23 · packaging · compass initialised, five workstreams defined
**Why:** the package had no tracking at all — one open issue, a NEWS changelog, and
no record of why anything was decided. Workstream boundaries follow how the code
actually goes stale, not the directory listing: colors stay with theme because the
scales are consumed by the themes, and packaging is its own stream so CI and
dependency drift file against something.
**Obligates:** #21, #22
**Refs:** 7cad98b, #20
+65
View File
@@ -0,0 +1,65 @@
# Project status
> Between the compass markers is generated. Edit the sources, not this.
<!-- compass:begin -->
<!-- compass:board -->
## Where this stands
`civilytics` is the house R package behind every Civilytics chart, table, and report —
ggplot2 themes, brand palettes, logo composition, and Quarto templates, plus the
data-wrangling helpers the analysis work leans on. It is at version 0.3.1 and already
depended on by live projects, so changes here surface in reports that are published.
Project tracking went in on 23 August. Five workstreams divide the package by how it
actually goes stale rather than by directory: themes and palettes, logo and branded
output, Quarto templates, analysis helpers, and packaging. Documentation debt is caught
automatically — if code moves and its roxygen pages do not, an issue gets filed.
Automated review is now working correctly. The first review ran on a config-only commit,
which turned out to be the useful part: the exclusion patterns match plain text, so
`chore:` never matched the `chore(packaging):` form this project's own commit convention
requires. Both forms are now listed, and `pm/compass.toml` is named as the one place
review rules are edited.
Three things are open, none blocked. A logo defect (#20) drops patchwork panels and clips
captions, which is the one that bites users. The flextable styling helper (#21) ships
Bootstrap's palette as its default instead of the brand's, so tables built with the
defaults are quietly off-brand. The third (#22) is forge housekeeping: the `kodor/*`
labels are defined twice, at repo and org scope, which makes filtering by name unreliable.
The next substantive work is #20.
## Ready to work on next
- **#20** civilytics_logo(): drops patchwork panels, clips captions, and silently rescales fonts · `ws/logo` — nothing is blocking it; something is currently wrong
- **#21** style_flextable_civilytics() defaults to Bootstrap colors, not Civilytics brand · `ws/logo` — nothing is blocking it; owed work from an earlier change
- **#22** kodor/* labels are defined at both repo and org scope · `ws/packaging` — nothing is blocking it
## Workstreams
| Stream | Commits since | Open | Debt | Owes docs |
|---|---|---|---|---|
| Themes, palettes, and fonts | 0 | 0 | 0 | no |
| Logo and branded output composition | 0 | 2 | 1 | no |
| Quarto themes and publishing templates | 0 | 0 | 0 | no |
| Analysis and workflow helpers | 0 | 0 | 0 | no |
| Package infrastructure and release | 1 | 1 | 0 | **yes** |
## CI
![R-CMD-check](https://gitea.civilytics.org/Civilytics/civilyticsR/actions/workflows/check.yaml/badge.svg?branch=master)
<details>
<summary>Dependency graph and detail</summary>
_Nothing blocks anything else, so there is no graph to draw._
- Marker: `496b9ed9` (2026-08-23)
- Commits since: 1
- Open issues: 3
</details>
<!-- compass:end -->
+107
View File
@@ -0,0 +1,107 @@
[project]
name = "civilytics"
forge = "Civilytics/civilyticsR"
[[workstream]]
id = "theme"
title = "Themes, palettes, and fonts"
paths = [
"R/theme.R",
"R/colors.R",
"R/fonts.R",
"tests/testthat/test_theme.R",
]
docs = [
"man/theme_civilytics*.Rd",
"man/scale_color_civilytics.Rd",
"man/scale_fill_civilytics.Rd",
"man/civilytics_pal*.Rd",
"man/civilytics_colors.Rd",
"man/civilytics_load_fonts.Rd",
"README.Rmd",
]
[[workstream]]
id = "logo"
title = "Logo and branded output composition"
paths = [
"R/logo.R",
"R/flextable.R",
"inst/img/**",
"tests/testthat/test_logo.R",
"tests/testthat/test_flextable.R",
]
docs = [
"man/*logo*.Rd",
"man/*flextable*.Rd",
"man/plot_jpeg.Rd",
"man/get_png.Rd",
"man/has_caption.Rd",
"man/measure_caption.Rd",
"README.Rmd",
]
[[workstream]]
id = "quarto"
title = "Quarto themes and publishing templates"
paths = [
"R/quarto.R",
"inst/quarto/**",
]
docs = [
"man/use_civilytics_*.Rd",
"man/quarto-helpers.Rd",
"inst/quarto/examples/*.qmd",
]
[[workstream]]
id = "helpers"
title = "Analysis and workflow helpers"
paths = [
"R/utils.R",
"R/prop_conf.R",
"R/join_utilities.R",
"R/db.R",
"R/notifications.R",
"tests/testthat/test_utils.R",
"tests/testthat/test_propint.R",
"tests/testthat/test_joins.R",
"tests/testthat/test_db.R",
"tests/testthat/test_notifications.R",
]
docs = [
"README.Rmd",
"NEWS.md",
]
[[workstream]]
id = "packaging"
title = "Package infrastructure and release"
paths = [
"DESCRIPTION",
"NAMESPACE",
"Makefile",
"Dockerfile",
".Rbuildignore",
".gitea/workflows/**",
"tests/testthat.R",
"AGENTS.md",
".roborev.toml",
]
docs = [
"NEWS.md",
"README.Rmd",
]
[roborev]
project_guidelines = [
"Brand colors come from `civilytics_colors`; never write a hex literal in theme, scale, logo, or table code. `R/flextable.R` still carries off-brand Bootstrap defaults (#2c3e50, #f0f0eb, #888888, #cccccc, #555555) -- do not add more.",
"No tidyverse dependency. Imports is base R plus ggplot2, grid/gridExtra, png/jpeg, stringr, stringdist, showtext/sysfonts, jsonlite. Reject dplyr, purrr, magrittr, tibble, and data.table; use base R idioms.",
"NAMESPACE is roxygen-generated. Declare imports with `@importFrom` in the roxygen block and re-run roxygen; never hand-edit NAMESPACE.",
"Themes default to a transparent background (`paper_bg = FALSE`) so plots composite onto any Quarto, Reveal, or Typst background. Making an opaque background the default is a regression, not a preference.",
"Theme functions thread `ink`/`paper`/`accent` into ggplot2 4.0's base-theme parameters rather than setting element colors ad hoc. ggplot2 >= 4.0 is a hard dependency, so use S7 `@` property access on plot and theme objects, not `$`.",
"User-facing progress goes through `message()` so callers can suppress it. No `cat()` or `print()` in package code.",
"Nothing personal or client-identifying is vendored into `inst/` -- brand assets only. Version 0.3.2 removed headshots for exactly this reason.",
"The camelCase exports (`countCleanr`, `dbSafeNames`, `simpleCap`, `waldInterval`, `countDots`, `countNA`, `findDots`, `nvals`) are frozen public API; do not rename them. New functions are snake_case.",
"`R/theme.R` and `R/logo.R` are long by design -- one file per surface, with dense roxygen. File length is not a finding in this package; flag a single function over roughly 80 lines instead.",
]
+12
View File
@@ -0,0 +1,12 @@
# Decisions
One file per decision, numbered and immutable. A decision that changes is superseded
by a new record, never edited in place — the old reasoning is the point.
The table below is **generated** by `compass:decide`. Do not hand-edit it.
<!-- compass:begin decisions -->
| # | Date | Decision | Status |
|---|---|---|---|
| — | — | *No decisions recorded yet.* | — |
<!-- compass:end decisions -->
Binary file not shown.