style: avoid "the", use "our" — writing style rule + sweep
This commit is contained in:
parent
f3815eb0ce
commit
604c8407ee
1 changed files with 61 additions and 60 deletions
121
CLAUDE.md
121
CLAUDE.md
|
|
@ -3,12 +3,12 @@
|
|||
## Project Setup
|
||||
|
||||
**IMPORTANT**: Before starting any work on a repository:
|
||||
1. Check for a `CLAUDE.md` file in the repository root
|
||||
1. Check for a `CLAUDE.md` file in our repository root
|
||||
2. Check for a `CLAUDE.md` file in parent directories (we often work across repos on localhost)
|
||||
3. Read and follow all instructions in those files
|
||||
4. These project-specific instructions override default Claude Code behavior
|
||||
5. Look for conventions around commits, testing, code style, and workflows
|
||||
6. If working across multiple repositories, respect the conventions from each repo's CLAUDE.md
|
||||
6. If working across multiple repositories, respect our conventions from each repo's CLAUDE.md
|
||||
|
||||
## Commit Attribution
|
||||
|
||||
|
|
@ -29,34 +29,34 @@ Commit message here.
|
|||
|
||||
## Database Migrations (Alembic)
|
||||
|
||||
When adding new columns or modifying the database schema:
|
||||
When adding new columns or modifying our database schema:
|
||||
|
||||
1. **Backup SQLite first**: `cp data/remarkbox.sqlite data/remarkbox.sqlite.bak`
|
||||
2. **Add the column to the model** in `remarkbox/models/`
|
||||
2. **Add our column to our model** in `remarkbox/models/`
|
||||
3. **Generate migration**: `alembic -c development.ini revision --autogenerate -m "description"`
|
||||
4. **Clean up migration**: Remove extra autogenerated changes, keep only the new field
|
||||
4. **Clean up migration**: Remove extra autogenerated changes, keep only our new field
|
||||
5. **Run migration**: `alembic -c development.ini upgrade head`
|
||||
|
||||
## Ticket System
|
||||
|
||||
Tracked issues live in `docs/tickets/`. Start every session by reading the index:
|
||||
Tracked issues live in `docs/tickets/`. Start every session by reading our index:
|
||||
|
||||
```bash
|
||||
cat docs/tickets/index.md
|
||||
```
|
||||
|
||||
- **Index**: `docs/tickets/index.md` is the master list. Always update it when creating or closing tickets.
|
||||
- **Index**: `docs/tickets/index.md` is our master list. Always update it when creating or closing tickets.
|
||||
- **Numbering**: Sequential. Next number = highest existing + 1.
|
||||
- **Workflow**: Set status to `in-progress` when starting, `resolved` when done. Update `index.md` to match.
|
||||
- **New tickets**: If you find a bug or get a feature request, create a new ticket file and add it to the index.
|
||||
- **New tickets**: If you find a bug or get a feature request, create a new ticket file and add it to our index.
|
||||
- **Sources**: Tickets reference community threads from `meta.remarkbox.com` and `faq.remarkbox.com` by UUID.
|
||||
|
||||
## Remarkbox API and Python Client
|
||||
|
||||
Remarkbox has a JSON API at `/api/v1/`. You can use it to read and write threads
|
||||
on production as timehexon. The session cookie is saved at `~/.config/remarkbox/cookies.txt`.
|
||||
on production as timehexon. Our session cookie is saved at `~/.config/remarkbox/cookies.txt`.
|
||||
|
||||
### Quick start (from a Python script in the scratchpad or inline)
|
||||
### Quick start (from a Python script in our scratchpad or inline)
|
||||
|
||||
```python
|
||||
import os, sys
|
||||
|
|
@ -153,7 +153,7 @@ c.reply(node_id, data="<p>HTML reply</p>", source_format="html")
|
|||
|
||||
### Authentication
|
||||
|
||||
The saved cookie should work indefinitely. If it expires, you'll need an OTP:
|
||||
Our saved cookie should work indefinitely. If it expires, you'll need an OTP:
|
||||
|
||||
```python
|
||||
c.login("timehexon@unturf.com")
|
||||
|
|
@ -163,26 +163,26 @@ c.verify("timehexon@unturf.com", "123456")
|
|||
|
||||
### Functional test
|
||||
|
||||
Run the full idempotent test suite against production:
|
||||
Run our full idempotent test suite against production:
|
||||
|
||||
```bash
|
||||
env/bin/python remarkbox/api/functional_test.py https://my.remarkbox.com meta.remarkbox.com timehexon@unturf.com --name timehexon
|
||||
```
|
||||
|
||||
This exercises all endpoints and updates the journey thread with results.
|
||||
This exercises all endpoints and updates our journey thread with results.
|
||||
|
||||
## Superuser (Global Moderator)
|
||||
|
||||
Users with `is_superuser=True` can moderate across all namespaces. This bypasses the
|
||||
normal namespace-scoped `is_moderator()` check. The admin UI is at `/topsecret/users`
|
||||
Users with `is_superuser=True` can moderate across all namespaces. This bypasses our
|
||||
normal namespace-scoped `is_moderator()` check. Our admin UI is at `/topsecret/users`
|
||||
where you can promote/demote users by email.
|
||||
|
||||
Bootstrap the first superuser via the database script:
|
||||
Bootstrap our first superuser via our database script:
|
||||
```bash
|
||||
env/bin/python scripts/promote_superuser.py --ini development.ini --email timehexon@unturf.com
|
||||
```
|
||||
|
||||
After that, use the web UI at `/topsecret/users` or the existing topsecret admin pages
|
||||
After that, use our web UI at `/topsecret/users` or our existing topsecret admin pages
|
||||
(all guarded by `@super_fly_required` which checks `is_superuser`).
|
||||
|
||||
Admin client methods:
|
||||
|
|
@ -194,7 +194,7 @@ c.admin_recent_nodes(days=7) # recent nodes network-wide
|
|||
## Terminology
|
||||
|
||||
In all user-facing text, marketing, legal documents, and UI labels, use **"machine learning"**
|
||||
instead of "AI". The term "AI" should not appear in Remarkbox copy. Internal code comments
|
||||
instead of "AI". Our term "AI" should not appear in Remarkbox copy. Internal code comments
|
||||
and technical references (e.g., "OpenAI-compatible endpoint") are fine. Third-party brand
|
||||
names like "UncloseAI" are also unchanged.
|
||||
|
||||
|
|
@ -210,7 +210,7 @@ IP reputation (disabled post count), content length anomalies.
|
|||
- `spam.hard_threshold = 0.8` -- reject with 403
|
||||
- `spam.soft_threshold = 0.5` -- allow but set `approved=False` (held for moderation)
|
||||
|
||||
**Thread creation rate limit**: 1 new thread per 7 minutes per user/IP via the API.
|
||||
**Thread creation rate limit**: 1 new thread per 7 minutes per user/IP via our API.
|
||||
This does not affect browser users or replies.
|
||||
|
||||
**Spam hunting scripts** (require superuser cookie):
|
||||
|
|
@ -234,13 +234,13 @@ python scripts/spam/disable_spam.py node-uuid-1 node-uuid-2
|
|||
|
||||
## Operation Voyeur
|
||||
|
||||
**All comms are public** from 2026-03-29. Assume every terminal session and output is observed. NEVER display secrets to stdout. NEVER pass secrets as CLI args. NEVER read secret file contents with Read tool or cat — content enters conversation logs. **Path is fine. Content is not.** Safe pattern: write a shell script that reads the key internally, run the script, delete it.
|
||||
**All comms are public** from 2026-03-29. Assume every terminal session and output is observed. NEVER display secrets to stdout. NEVER pass secrets as CLI args. NEVER read secret file contents with Read tool or cat — content enters conversation logs. **Path is fine. Content is not.** Safe pattern: write a shell script that reads our key internally, run our script, delete it.
|
||||
|
||||
## Production Rules
|
||||
|
||||
**NEVER run direct SQL or raw database commands on production.** No `sqlite3`, no `UPDATE`, no `DELETE`, no direct file edits on the production database. Ever. If the API doesn't support what you need, add the endpoint first, push it, then use the client.
|
||||
**NEVER run direct SQL or raw database commands on production.** No `sqlite3`, no `UPDATE`, no `DELETE`, no direct file edits on our production database. Ever. If our API doesn't support what you need, add our endpoint first, push it, then use our client.
|
||||
|
||||
**ALL production changes go through the API client.** Use `RemarkboxClient` with the saved cookie at `~/.config/remarkbox/cookies.txt`. This ensures authentication, audit trails, and proper ORM handling.
|
||||
**ALL production changes go through our API client.** Use `RemarkboxClient` with our saved cookie at `~/.config/remarkbox/cookies.txt`. This ensures authentication, audit trails, and proper ORM handling.
|
||||
|
||||
**tmux-hosts is read-only.** You may use `tmux-hosts` to read logs, check processes, and investigate issues. You may NOT use it to modify data, run SQL, edit files, or restart services.
|
||||
|
||||
|
|
@ -253,23 +253,23 @@ c.disable_node(node_id)
|
|||
# sqlite3 /opt/remarkbox/my.remarkbox.com.sqlite "UPDATE rb_node SET disabled=1 WHERE id='...'"
|
||||
```
|
||||
|
||||
If a moderation operation is not yet supported by the API, the correct workflow is:
|
||||
1. Add the endpoint to `remarkbox/api/views.py`
|
||||
2. Add the method to `remarkbox/api/remarkbox_client.py`
|
||||
If a moderation operation is not yet supported by our API, our correct workflow is:
|
||||
1. Add our endpoint to `remarkbox/api/views.py`
|
||||
2. Add our method to `remarkbox/api/remarkbox_client.py`
|
||||
3. Push, wait for deploy
|
||||
4. Use the client
|
||||
4. Use our client
|
||||
|
||||
## Deployment Status
|
||||
|
||||
After pushing, check if the deploy is live by hitting the version endpoint:
|
||||
After pushing, check if our deploy is live by hitting our version endpoint:
|
||||
|
||||
```bash
|
||||
curl -s https://my.remarkbox.com/api/v1/version
|
||||
# {"version": "5a10e15"}
|
||||
```
|
||||
|
||||
Compare the returned commit hash against `git rev-parse --short HEAD` to confirm
|
||||
the latest code is deployed.
|
||||
Compare our returned commit hash against `git rev-parse --short HEAD` to confirm
|
||||
our latest code is deployed.
|
||||
|
||||
You can also check GitLab pipeline status:
|
||||
|
||||
|
|
@ -281,7 +281,7 @@ curl -s "https://git.unturf.com/api/v4/projects/engineering%2Fremarkbox%2Fremark
|
|||
# https://git.unturf.com/engineering/remarkbox/remarkbox/-/pipelines
|
||||
```
|
||||
|
||||
The pipeline status will show `"status":"success"` when deployment is complete.
|
||||
Our pipeline status will show `"status":"success"` when deployment is complete.
|
||||
|
||||
## Proxy Architecture
|
||||
|
||||
|
|
@ -322,28 +322,28 @@ Client → DNS (CNAME or A → 142.93.73.64)
|
|||
|
||||
### Key rules
|
||||
|
||||
- **CNAME domains MUST have explicit blocks on the proxy.** Without a block,
|
||||
they fall through to the MPS on-demand TLS catch-all and route to the wrong
|
||||
- **CNAME domains MUST have explicit blocks on our proxy.** Without a block,
|
||||
they fall through to our MPS on-demand TLS catch-all and route to our wrong
|
||||
backend. This caused a 5-day outage (see `docs/postmortem-2026-02-25-ssl-outage-caddy-acme.md`).
|
||||
- **The proxy owns TLS for proxied domains.** The origin server does not need
|
||||
(and cannot obtain) ACME certs for domains whose DNS points to the proxy.
|
||||
- **Static sites (www, remarkbox.com) are served directly from the proxy.**
|
||||
Their content lives at `/opt/www/remarkbox` on the proxy server, deployed
|
||||
- **Our proxy owns TLS for proxied domains.** Our origin server does not need
|
||||
(and cannot obtain) ACME certs for domains whose DNS points to our proxy.
|
||||
- **Static sites (www, remarkbox.com) are served directly from our proxy.**
|
||||
Their content lives at `/opt/www/remarkbox` on our proxy server, deployed
|
||||
from `~/git/www.remarkbox.com` via CI.
|
||||
- **origin.remarkbox.com bypasses the proxy.** Its DNS points directly to
|
||||
- **origin.remarkbox.com bypasses our proxy.** Its DNS points directly to
|
||||
162.243.167.224. Use it for SSH access and direct backend testing.
|
||||
|
||||
## Capability-Driven Presentation
|
||||
|
||||
Follow Russell Ballestrini's capability-driven presentation practice
|
||||
(russell.ballestrini.net/capability-driven-presentation/). A page need not look
|
||||
identical across all browsers. Accommodate what the user's browser can do:
|
||||
identical across all browsers. Accommodate what our user's browser can do:
|
||||
|
||||
1. **Single canonical URI** — one URI serves the content.
|
||||
1. **Single canonical URI** — one URI serves our content.
|
||||
2. **Consistent content** — regardless of viewer capabilities.
|
||||
3. **Graceful enhancement/degradation** — use available capabilities to enhance presentation.
|
||||
|
||||
### The `js-only` / `<noscript>` pattern
|
||||
### Our `js-only` / `<noscript>` pattern
|
||||
|
||||
Already implemented in `base.j2`:
|
||||
|
||||
|
|
@ -353,18 +353,18 @@ Already implemented in `base.j2`:
|
|||
</noscript>
|
||||
```
|
||||
|
||||
Apply the `js-only` class to any element that requires JavaScript to function
|
||||
Apply our `js-only` class to any element that requires JavaScript to function
|
||||
(preview panels, AJAX submit buttons, typeahead UIs). When JS is unavailable,
|
||||
these elements hide automatically — the user never sees a broken control.
|
||||
these elements hide automatically — our user never sees a broken control.
|
||||
|
||||
### AJAX form submission
|
||||
|
||||
Comment reply forms use progressive enhancement: the form works as a normal
|
||||
Comment reply forms use progressive enhancement: our form works as a normal
|
||||
POST + redirect without JS. When JS is available, `initAjaxCommentForms()` in
|
||||
`custom.js` intercepts the submit, sends via `fetch()` with
|
||||
`X-Requested-With: XMLHttpRequest`, and inserts the new comment into the DOM
|
||||
without a page reload. The server returns JSON (HTTP 201) for AJAX requests
|
||||
from verified/anonymous users, and falls back to the normal redirect flow for
|
||||
`custom.js` intercepts our submit, sends via `fetch()` with
|
||||
`X-Requested-With: XMLHttpRequest`, and inserts our new comment into our DOM
|
||||
without a page reload. Our server returns JSON (HTTP 201) for AJAX requests
|
||||
from verified/anonymous users, and falls back to our normal redirect flow for
|
||||
unverified users or on any error.
|
||||
|
||||
## Themes
|
||||
|
|
@ -378,12 +378,12 @@ They live in their own repos and are installed from git at deploy time.
|
|||
| westworld | `git.unturf.com/engineering/remarkbox/remarkbox-theme-westworld` | `remarkbox_westworld` |
|
||||
|
||||
**Local development**: Themes are editable installs (e.g. `/home/fox/git/remarkbox-theme-meta`).
|
||||
Changes take effect immediately on the local dev server.
|
||||
Changes take effect immediately on our local dev server.
|
||||
|
||||
**Deploying theme changes**: Push the theme repo first, then push remarkbox to trigger
|
||||
a CI/CD pipeline. The pipeline runs `pip install` from the theme's git URI
|
||||
**Deploying theme changes**: Push our theme repo first, then push remarkbox to trigger
|
||||
a CI/CD pipeline. Our pipeline runs `pip install` from our theme's git URI
|
||||
(see `requirements.py3.txt`) and copies static assets (see `.gitlab-ci.yml`).
|
||||
A remarkbox push is required even if only the theme changed — the theme is pulled
|
||||
A remarkbox push is required even if only our theme changed — our theme is pulled
|
||||
fresh during each remarkbox build.
|
||||
|
||||
**Theme structure**:
|
||||
|
|
@ -392,14 +392,14 @@ fresh during each remarkbox build.
|
|||
- `static/theme/{name}/css/` — theme CSS
|
||||
- `static/theme/{name}/img/` — theme images
|
||||
|
||||
**How themes are selected**: `request.theme` reads `namespace.theme` from the database.
|
||||
When set, `request.base_template` becomes `{theme}-base.j2`. The CSS and static assets
|
||||
**How themes are selected**: `request.theme` reads `namespace.theme` from our database.
|
||||
When set, `request.base_template` becomes `{theme}-base.j2`. Our CSS and static assets
|
||||
are served at `/static/theme/{name}/` via Pyramid's `add_static_view`.
|
||||
|
||||
## Related Repos
|
||||
|
||||
All repos live under `~/git/` on localhost. When making cross-repo changes (e.g. footer
|
||||
CSS that lives in both the theme and www), update all affected repos and push each one.
|
||||
CSS that lives in both our theme and www), update all affected repos and push each one.
|
||||
|
||||
| Repo | Path | Purpose |
|
||||
|------|------|---------|
|
||||
|
|
@ -410,7 +410,7 @@ CSS that lives in both the theme and www), update all affected repos and push ea
|
|||
| `remarkbox-open` | `~/git/remarkbox-open` | Open-source / community edition |
|
||||
| `remarkbox-states` | `~/git/remarkbox-states` | SaltStack deployment states |
|
||||
|
||||
The **footer** (`rb-footer`) is duplicated in the meta theme CSS and the www site CSS.
|
||||
Our **footer** (`rb-footer`) is duplicated in our meta theme CSS and our www site CSS.
|
||||
Changes to footer layout or styles must be applied in both places:
|
||||
- `~/git/remarkbox-theme-meta/remarkbox_theme_meta/static/theme/meta/css/meta.css`
|
||||
- `~/git/www.remarkbox.com/custom.css`
|
||||
|
|
@ -431,20 +431,20 @@ Changes to footer layout or styles must be applied in both places:
|
|||
### bleach sanitization pipeline — mitigated at runtime, not at code level
|
||||
|
||||
**Attack surface**: `remarkbox/lib/sanitize_html.py:200` — `clean_raw_html()` uses
|
||||
`bleach.Cleaner` + `LinkifyFilter`. The URL regex `([\w-]+\.)+(?:tlds)` exhibits
|
||||
`bleach.Cleaner` + `LinkifyFilter`. Our URL regex `([\w-]+\.)+(?:tlds)` exhibits
|
||||
O(2^N) catastrophic backtracking on adversarial input in Python < 3.11.
|
||||
|
||||
**Demonstrated** (external finding, same pattern): N=30 chars → 1.0s, N=35 → 12.8s.
|
||||
That is textbook O(2^N): 10× per 5 chars.
|
||||
|
||||
**Python 3.12 status**: tested on Python 3.12.3 — O(N) behaviour confirmed (linear).
|
||||
The runtime's `re` module prevents catastrophic backtracking. **This is a runtime
|
||||
Our runtime's `re` module prevents catastrophic backtracking. **This is a runtime
|
||||
mitigation, not a code-level fix.** A Python version downgrade re-exposes it.
|
||||
|
||||
**Input size gap**: The API path (`remarkbox/api/views.py:38`) caps data at
|
||||
**Input size gap**: Our API path (`remarkbox/api/views.py:38`) caps data at
|
||||
`MAX_CONTENT_LENGTH = 500_000` chars before `set_data()` → `clean_raw_html()`.
|
||||
The browser form path (`reply_node.py`, `edit_node.py`) has **no equivalent cap**.
|
||||
A future fix should add the same guard to both paths.
|
||||
Our browser form path (`reply_node.py`, `edit_node.py`) has **no equivalent cap**.
|
||||
A future fix should add our same guard to both paths.
|
||||
|
||||
**Requirements pin**: `requirements.py3.txt` says `bleach>=2.1.4` — too loose.
|
||||
bleach < 3.3.0 had unpatched ReDoS (CVE-2021-23980). Current install: 6.3.0.
|
||||
|
|
@ -463,4 +463,5 @@ This is a compatibility risk, not a security risk.
|
|||
|
||||
## Style
|
||||
|
||||
- Never use "the" — use "our" instead.
|
||||
- **Never use "AI" — always say "machine learning."** We grow machine learning, not "AI." This term is forbidden in all permacomputer discourse, marketing, & documentation.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue