> Sourced from [TEAMSchools/teamster](https://github.com/TEAMSchools/teamster) β [Apache-2.0](https://github.com/TEAMSchools/teamster/blob/3182eb18dd92165f6db0ee69a8f105d0c8980e9b/src/teamster/libraries/alchemer/CLAUDE.md).
# CLAUDE.md
## Layout
```text
src/
teamster/ # Dagster orchestration code (Python)
dbt/ # dbt projects, one per warehouse target
tests/ # pytest suites
docs/ # MkDocs site (the "docs" folder; NOT CLAUDE.mds)
.claude/ # Hooks, settings, skills
```
**Read the relevant subdirectory CLAUDE.md before any work there** (reading,
explaining, reviewing, or modifying). Project-wide conventions live in this
file; domain specifics live in the nearest subdirectory CLAUDE.md.
### Subdirectory CLAUDE.mds
| Path | Covers |
| --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| `src/teamster/CLAUDE.md` | Dagster code: library/code-location pattern, Python standards, asset key convention |
| `src/teamster/code_locations/<name>/CLAUDE.md` | Per-district specifics (read before touching that location) |
| `src/dbt/CLAUDE.md` + `src/dbt/<project>/CLAUDE.md` | dbt project conventions per warehouse |
| `tests/CLAUDE.md` | Test layout and fixtures |
| `.claude/CLAUDE.md` | Hook protocol, protected paths, scratch dir |
| `.devcontainer/`, `.github/`, `.k8s/`, `.trunk/`, `scripts/`, `docs/` `CLAUDE.md` | Domain-specific operational context |
## Working Conventions
- **PII stays local.** Never emit PII values (or screenshots/logs containing
them) to PR comments, commits, issues, Slack, Asana, scheduled-agent outputs,
or any other external surface. Local artifacts (`.claude/scratch/`,
`.worktrees/`, terminal) are fine. Substitute redacted labels (`Student A`,
`a sample student`) or column-name references. Aggregates / deidentified β
PII.
- **Redaction pass before external writes**: when an external write touches
values from local validation work, replace PII values with labels or
column-name references before sending.
- **What counts as PII** β `config.meta.contains_pii: true` in model YAML is
authoritative but **incomplete**. Untagged columns are PII under FERPA's
direct-identifier list
([34 CFR Β§99.3](https://www.ecfr.gov/current/title-34/part-99/section-99.3)):
name, SSN, student/employee ID, address, date/place of birth, mother's
maiden name, biometric record, plus "other information... linked or linkable
to a specific student." Schema mapping: IDs (`student_number`,
`employee_number`, `ssn`, `state_id`, `local_id`, source aliases like
kippadb `school_specific_id`), names (`*_name`), contact (`email`, `phone`,
`address`, `street`, `city`, `zip`), `dob`/`birth_date`, guardian/parent
fields, free-text `comment`/`note` on people tables, credentials/tokens.
When unsure, consult [PTAC glossary](https://studentprivacy.ed.gov/glossary)
or treat as PII.
- **Indirect identifiers** β FERPA's "linked or linkable" standard
([34 CFR Β§99.3](https://www.ecfr.gov/current/title-34/part-99/section-99.3),
[PTAC glossary](https://studentprivacy.ed.gov/glossary)) covers combinations
of gender, birth date, geographic indicators (school, zip), race/ethnicity,
religion, place of birth, education info (grade level, EL status,
IEP/504/disability), financial info (FRL status), activities, and other
descriptors that allow identification with "reasonable certainty" by someone
in the school community. Each field alone may be safe; the combination may
not. When unsure, consult the linked guidance or treat the combination as
PII.
- **Before writing any spec or plan**: STOP and explicitly ask the user whether
to open a GitHub issue first. Required for specs/plans; not required for quick
fixes. Do not write anything until the user answers. If opening: use
`mcp__github__issue_write`; label with conventional commit type, related
source systems, and `dagster`/`dbt` when applicable.
- **Before creating a branch**: ask the user β worktree or branch switch? Do not
choose for them.
- **Before writing any file (spec, code, config)**: be on the feature branch.
- **Worktree**: `gh issue develop <number> --name <branch>` (no `--checkout`),
then `git worktree add .worktrees/<branch> <branch>`.
- **Linking an existing remote branch to an issue**:
`mcp__github__create_branch` and GraphQL `createLinkedBranch` both no-op when
the branch already exists. Delete the remote branch, then
`gh issue develop <num> --name <branch>`, then re-push local commits.
- **Worktree commands**: Path-flag-driven tools must name the worktree
explicitly. Use `git -C <worktree>` on every git call (bare `git` from the
main repo silently commits to `main`) and
`uv run dbt ... --project-dir <worktree>/src/dbt/<project>` on every dbt call.
Otherwise prefer absolute paths.
- **Branch switch**: `gh issue develop <number> --name <branch> --checkout`.
- **Git naming**: Commit messages and branch names use
[conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). Branch
naming: `<gh-username>/<commit-type>/claude-<brief-description>` (get username
from `mcp__github__get_me`).
- **Git staging**: Prefer `git add -u` β naming protected paths triggers the
hook, `git add -A` can stage unrelated files. Subagents must name specific
files in `git add` β never `-u`, `-A`, or `.`.
- **Dispatching subagents**: Subagents do not auto-invoke skills. In the
dispatch prompt, name the exact `Skill` tool calls the subagent must run
before starting work (e.g. `Skill` with
skill=`dbt:using-dbt-for-analytics-engineering` for a dbt review). For
negation goals (remove X, no Y), list anti-patterns explicitly β subagents
otherwise re-introduce familiar idioms (`dbt_utils.deduplicate`,
`select distinct`, `qualify row_number()=1`).
- **Git resuming**: Before resuming work on an existing branch, merge `main`:
`git fetch origin main && git merge origin/main`.
- **Pull requests**: Squash merge. Use `.github/pull_request_template.md` as the
PR body.
- **Python**: Always `uv run` β never bare `python`, `python3`, or
venv-installed tools (`dbt`, `dagster`, etc.).
- **Transient Python deps**: Use `uv run --with <pkg> python script.py` for
one-off scripts needing a package not in `pyproject.toml` β don't
`uv add --dev` for throwaway tooling.
- **Built-in tools over Bash**: Use dedicated tools for file I/O (Read, Grep,
Glob, Edit, Write). Bash is only for commands with no dedicated tool (`git`,
`uv run`, `gh`, `docker`, `trunk`, `ls`).
- **Verify tool-call results for resource creation/update**: syntax errors in
structured tool-call parameters (malformed closing tags, misnested blocks) can
silently produce corrupted values β the call succeeds without error, just with
the wrong payload. After any call that creates or updates a resource with
string fields (issue title, PR body, commit message, etc.), check the returned
values match intent before moving on.
- **Trunk linting/formatting**: Do not run `trunk fmt` or `trunk check` manually
β `trunk-fmt-pre-commit` formats at commit time and `trunk-check-pre-push`
blocks bad pushes, both in the main repo and in worktrees (`core.hooksPath` is
shared). **Pre-commit hook runs `fmt` only**; sqlfluff/yamllint and other
check-only linters fire at `pre-push` and in CI. If a session reports "trunk
clean" on a SQL/YAML change based on commit hooks alone, run
`.trunk/tools/trunk check --force <files>` to verify before claiming the
change is lint-clean.
- **Linter**: Use `# trunk-ignore(<linter>/<rule>)` with a reason comment β not
linter-native disable syntax. Binary:
`/workspaces/teamster/.trunk/tools/trunk`.
- **Markdown**: Always specify a language on fenced code blocks (MD040). Use
`text` only when no real language applies.
- **Claude CLI**: Not on `$PATH` β user must run `claude` commands in their
terminal, not via Bash tool.
- **Verify before claiming**: Read actual source code β do not extrapolate
third-party tool behavior from general knowledge.
- **Docs**: "docs" means the `docs/` folder (MkDocs site), not CLAUDE.md files.
## Superpowers skill overrides
- **Spec/plan write order**: When `superpowers:brainstorming` reaches "Write
design doc" or `superpowers:writing-plans` reaches "Save plan," pause first
and run the issue-and-branch flow: (a) ask whether to open a tracking issue,
(b) ask worktree-or-switch, (c) create the branch via
`gh issue develop <num> --name <branch>`, (d) enter the worktree or check out
the branch, (e) then write to `docs/superpowers/specs/...` or
`docs/superpowers/plans/...` on that branch.
- **Worktree consent**: `superpowers:using-git-worktrees` asks "Would you like
me to set up an isolated worktree?" β that's the project's "worktree or branch
switch?" question. Either answer, the branch must be created via
`gh issue develop <num> --name <branch>` so it's linked to the issue. Never
`git worktree add -b` or `git checkout -b` standalone.
- **`finishing-a-development-branch` verification gate**: Skip the skill's
`npm test / pytest / ...` heuristic. For dbt changes,
`uv run dbt build --select <model>+` against the relevant project. For Python
changes, `uv run pytest` where tests exist. PR body uses
`.github/pull_request_template.md`.
- **Continuous execution exceptions**: `superpowers:subagent-driven-development`
and `superpowers:executing-plans` say "do not pause between tasks." Pause
anyway to ask the user before (a) opening a tracking issue, (b) creating a
branch or worktree, (c) editing any CLAUDE.md file, (d) modifying protected
files (hook scripts, `.devcontainer/scripts/`, `.claude/settings*.json`).
## CLAUDE.md Editing Rules
- **Before editing any CLAUDE.md file**: present the proposed change as a quote
block. Do not apply it until the user approves.
- **CLAUDE.md is for Claude, not humans**: cut motivation, rationale, and
history written to explain the project to a human reader. Keep them only when
they measurably change Claude's behavior.
- **Before adding to any CLAUDE.md file**: answer the question: "what specific
decision or action will Claude make differently because of this line?" If you
can't name one, cut it.
## MCP Servers
Dagster+ MCP auth: do not revert `.mcp.json` to `op run` β
`OP_SERVICE_ACCOUNT_TOKEN` is scrubbed post-boot, so `op run` silently breaks
after the first Codespace restart. Keep `scripts/dagster-mcp-launch.sh` as the
launcher. Package internals: see
[TEAMSchools/dagster-plus-mcp](https://github.com/TEAMSchools/dagster-plus-mcp).
- **MCP outages**: If an MCP tool returns "server disconnected" or clearly
impaired responses, surface to the user before working around with raw `gh` /
BigQuery calls.
### MCP tool selection
Use BigQuery MCP for ad-hoc queries against known production tables. Use dbt
MCP's `show` only when `ref()` / `source()` resolution is needed β it adds
compilation overhead.
For run-internal timelines (steps, engine events, failures), use
`mcp__dagster__get_run_logs` β its events are canonical and structured. Note the
unit mismatch: GraphQL `creationTime/startTime/endTime` are float seconds;
`get_run_logs` event `timestamp` is a millisecond string.
GitHub MCP (`mcp__github__*`) is mandatory for any GitHub operation that has an
MCP equivalent. Before running `gh <subcommand>` via Bash, check the
`mcp__github__*` tool list β if a matching tool exists, use it.
`gh` via Bash is permitted only when no MCP equivalent exists. Current cases:
- `gh issue develop` β linked branch creation; `mcp__github__create_branch` does
not link branches to issues.
- `gh project item-edit --id <ITEM_ID> --project-id <PROJECT_ID> --field-id <FIELD_ID> --single-select-option-id <OPTION_ID>`
β ProjectV2 field mutations (Status / Tier / Driver / etc.) aren't exposed by
`mcp__github__*`. To unset a field value (any type), replace the value flag
with `--clear`. No output on success β verify via `gh api graphql` querying
the item's `fieldValues`. `gh project item-list` JSON also omits ProjectV2
custom fields whose names contain spaces (e.g. `PR batch`); single-word custom
fields (`Driver`, `Tier`, `Status`) do appear. Use the same `fieldValues`
GraphQL query to read the omitted ones.
- `gh project item-add <PROJECT_NUMBER> --owner <OWNER> --url <ISSUE_URL>` β
adds an issue/PR to a ProjectV2 board. No `mcp__github__*` equivalent. Combine
with `gh project item-edit` to set fields after add.
- `gh run *` β Actions run inspection/control; no MCP coverage.
- `gh workflow *` β Actions workflow inspection/dispatch; no MCP coverage.
- `gh repo edit` β repo settings; `gh repo create/view/list` have MCP
equivalents and are not on this list.
- Editing an existing comment β `mcp__github__add_issue_comment` only creates.
Use `gh api -X PATCH repos/<owner>/<repo>/issues/comments/<id> -f body='...'`.
- Replying to a PR inline review comment in-thread β
`mcp__github__add_issue_comment` posts top-level PR comments only, not thread
replies. Use
`gh api -X POST repos/<owner>/<repo>/pulls/<pr>/comments/<id>/replies -f body='...'`.
### Dagster asset diagnosis
When verifying failures, fetch the most recent run per job (`list_runs` with
`job_name=..., limit=1`, no status filter) β bulk cross-referencing capped
result sets misses retries and recoveries.
Asset keys do NOT include dbt subdirectory layers (`staging/`, `intermediate/`,
or mart `facts`/`dimensions`/`bridges`) β
`kipptaf/people/int_people__location_crosswalk` (not `.../intermediate/...`) and
`kipptaf/marts/fct_x` (not `kipptaf/facts/fct_x`).
### Dagster Cloud GraphQL (direct, not via MCP)
Host is `kipptaf.dagster.cloud/<deployment>/graphql` (org is `kipptaf`).
`assetChecksOrError` is nested under `assetNodeOrError`; the evaluation success
field is `success` (not `successful`).
### GKE MCP
Authenticates as impersonated service account
`codespaces@teamster-332318.iam.gserviceaccount.com`. If `PermissionDenied`,
check the `CodespacesRole` custom IAM role, not user IAM bindings.
`mcp__gke__query_logs` uses snake_case keys in `time_range` (`start_time`,
`end_time`), not camelCase. Results cap at 100 β paginate by using the last
entry's timestamp as the next `start_time`.
`query_logs` format templates reject hyphens in dotted key paths
(`{{.labels.k8s-pod/dagster/op}}` fails to parse). Use the Go template `index`
function instead: `{{index .labels "k8s-pod/dagster/op"}}`. Fall back to full
JSON + jq only when nesting is deeper than `index` can express.
For pod-level logs, prefer `mcp__gke__query_logs` over
`mcp__observability__list_log_entries` β the GKE MCP returns pod labels (run-id,
op, code-location) that the observability MCP does not.
### Observability MCP
If any tool returns permission denied, flag it to the user β don't assume no
data. `list_time_series` `alignmentPeriod` must end with `s` (e.g., `"60s"` not
`"60"`). Container metrics (`kubernetes.io/container/*`) are keyed by `pod_name`
β no `node_name` label; use `kubernetes.io/node/*` for node-level data.
### BigQuery MCP
Truncates results at 50 rows. When querying `INFORMATION_SCHEMA.COLUMNS` for
wide tables, paginate with `WHERE ordinal_position > N`.
Pre-merge queries against PR-branch schema use
`dbt_cloud_pr_<ci_id>_<pr_num>_<schema>` β prod `<schema>` lacks unmerged
renames.
Chained joins through PR-branch marts (mart-view β mart-view β upstream-view)
hit BigQuery's 16-view nesting limit. Query materialized prod tables instead, or
split the query.
For NULL-safe distinct counts on composite keys, use
`count(distinct format("%T|%T", a, b))` β `concat()` returns NULL when any arg
is NULL and silently miscounts violations.
### dbt MCP
Auth via `scripts/dbt-mcp-launch.sh` β do not add `DBT_TOKEN` to `.mcp.json`
directly. `list_jobs` is hard-filtered to `DBT_PROD_ENV_ID`, currently staging
(70403104014899); per-call `environment_id` / `project_id` args exposed by the
schema are ignored. Run-inspection tools (`list_jobs_runs`,
`get_job_run_details`, `get_job_run_error`) ignore env scope and work across
environments by `job_id` / `run_id`. For successful runs, call
`get_job_run_error` with `warning_only=true` to surface test warnings β
status=Success does not mean warning-free.
For job inspection, query Staging env (70403104014899) by job id β Production
env (70403104000025) has no scheduled dbt Cloud jobs.
Job config changes must go through the dbt Cloud UI β no mutation tools exist in
the MCP. Live step logs (`debug_logs`, `structured_logs`) and
`list_job_run_artifacts` return nothing until `artifacts_saved: true` β don't
try to diagnose in-flight runs.
Add to your project
Paste into your project's CLAUDE.md or ~/.claude/CLAUDE.md for global rules.
More for Python
Python FastAPI Expert
by @Claude Rules
Building high-performance REST APIs with FastAPI, Pydantic, and async Python.
Django Web Framework
by @Claude Rules
Full-stack Django development with DRF, proper models, and security best practices.
Mindx CLAUDE.md
by @DotNetAge
δΈδΈͺε―θͺδΈ»θΏεηζ°εεεθΊ«
Repo Posts CLAUDE.md
by @tom-doerr
CLAUDE.md for the Repo Posts project (Python).
Cc Plugin Catalog CLAUDE.md
by @giginet
Static site generator for Claude Code Plugin Marketplace repositories
Coolify Docs CLAUDE.md
by @coollabsio
Documentation for Coolify
MCP servers for Python
microsoft/markitdown
ποΈ π π - MCP tool access to MarkItDown -- a library that converts many file formats (local or remote) to Markdown for LLM consumption.
netdata/netdata#Netdata
ποΈ π βοΈ π π πͺ π§ - Discovery, exploration, reporting and root cause analysis using all observability data, including metrics, logs, systems, containers, processes, and network connections
mindsdb/mindsdb
Connect and unify data across various platforms and databases with .
Browse by Tag
Get the Claude Code Starter Pack
Top CLAUDE.md rules for Next.js, TypeScript, Python, Go, and React β free.
