20 Commits

Author SHA1 Message Date
user
c9af95d99e add lol section with conversation excerpts
Some checks failed
check / check (push) Failing after 5s
2026-03-04 14:15:38 -08:00
user
a6fb657d0e fix staccato/parallel tell in dramatic fragment section
All checks were successful
check / check (push) Successful in 11s
2026-03-04 14:13:42 -08:00
user
7b22e6757d add two-clause compound sentence and almost-hedge patterns, fix instances throughout
All checks were successful
check / check (push) Successful in 12s
2026-03-04 14:09:22 -08:00
user
a2dd953601 fmt: format REPO_POLICIES.md per prettier
All checks were successful
check / check (push) Successful in 8s
2026-03-04 14:02:09 -08:00
user
f921dee839 add LLM prose tells reference and copyediting checklist
Some checks failed
check / check (push) Failing after 10s
2026-03-04 14:00:45 -08:00
a1ffb1591b Merge pull request 'REPO_POLICIES: expand pre-1.0 schema migration rule (closes #5)' (#6) from clawbot/prompts:pre-1.0-migration-rule into main
Some checks failed
check / check (push) Failing after 10s
Reviewed-on: #6
2026-02-28 17:02:14 +01:00
clawbot
699f97d093 REPO_POLICIES: expand pre-1.0 schema migration rule (closes #5) 2026-02-28 07:59:27 -08:00
1955922857 Merge pull request 'fix: formatting + add clawpub reference' (#4) from clawbot/prompts:fix/formatting-and-readme into main
All checks were successful
check / check (push) Successful in 11s
Reviewed-on: #4
2026-02-28 11:45:56 +01:00
clawbot
a8cf966df6 docs: add clawpub reference in See Also section 2026-02-28 02:33:50 -08:00
clawbot
dcb6ca4339 fmt: fix prettier formatting in CODE_STYLEGUIDE_GO.md 2026-02-28 02:33:50 -08:00
dda0d01faa Merge pull request 'style(go): add rule against type-only packages (per upaas #126 review)' (#2) from clawbot/prompts:add-no-type-only-packages-rule into main
Some checks failed
check / check (push) Failing after 6s
Reviewed-on: #2
2026-02-23 22:14:02 +01:00
user
7676ec16c3 style(go): add Stringer rule for custom string-based types 2026-02-23 11:56:16 -08:00
user
f9dcef4c9e style(go): add rule against type-only packages
Types should live alongside their implementations, not in separate
'types', 'domain', or 'models' packages. Type-only packages cause
alias imports and indicate poor package design.

Prompted by review feedback on upaas PR #126.
2026-02-23 11:47:50 -08:00
189e54862e Add template repos section to README
All checks were successful
check / check (push) Successful in 7s
2026-02-23 01:38:38 +07:00
05fe766c62 Improve quickstart commands with two-pass workflow
All checks were successful
check / check (push) Successful in 4s
Split quickstart into separate repo-policy and code-style passes, clone
prompts repo once instead of per-command, and make each prompt
self-contained so agents don't need memory of prior runs.
2026-02-23 00:33:15 +07:00
cb5d630158 add note about makefile being authoritative docs
All checks were successful
check / check (push) Successful in 8s
2026-02-23 00:09:13 +07:00
b5575b9f59 Add test requirements to checklists, .dockerignore URLs, root symlink, and Makefile comment
All checks were successful
check / check (push) Successful in 7s
- Add test requirement item to both checklists (must not be a no-op)
- Add .dockerignore template URL to Dockerfile items in both checklists
- Add REPO_POLICIES.md symlink in repo root pointing to prompts/
- Add comment to Makefile explaining why prettier flags are repeated
2026-02-22 17:21:42 +01:00
e97b48eea4 Fix review issues: front matter, headings, consistency, typos
All checks were successful
check / check (push) Successful in 9s
- Move title and last_modified to YAML front matter (all policy docs)
- Make all document sections H1, subsections H2
- Update version rule to reference front matter format
- Fix "our" → "your" typo in Go styleguide
- Fix Python styleguide numbering (2. → 1.)
- Fix README: "flat collection" → accurate description, remove stale TODO
- Remove Makefile items from code styleguides (repo stuff, not code),
  add note linking to Repository Policies
- Change zerolog → slog in Go styleguide
- Fix JS styleguide npm reference: both work, but use make targets
- Drop .json from healthcheck path, add JSON content-type requirement
- Add Author/License to Go HTTP Server Conventions
- Convert hyperlinks to backtick URLs in checklists for consistency
- Add version/front matter to both checklists
2026-02-22 17:15:06 +01:00
3768b8ca02 Add rule: all software repos must have tests
All checks were successful
check / check (push) Successful in 7s
Require at least minimal tests (e.g. import/compile check) using the
platform-standard test framework. make test must never be a no-op.
2026-02-22 16:56:03 +01:00
03bf0b8445 Add authoritative URLs to checklists and copy .golangci.yml
All checks were successful
check / check (push) Successful in 7s
- Add .golangci.yml from upaas as authoritative copy in this repo
- Update REPO_POLICIES.md to reference .golangci.yml by URL
- Add fetch URLs for all template files in both checklists:
  .gitignore, .editorconfig, Makefile, .prettierrc, .prettierignore,
  REPO_POLICIES.md, .golangci.yml, check.yml
2026-02-22 16:52:33 +01:00
13 changed files with 845 additions and 181 deletions

32
.golangci.yml Normal file
View File

@@ -0,0 +1,32 @@
version: "2"
run:
timeout: 5m
modules-download-mode: readonly
linters:
default: all
disable:
# Genuinely incompatible with project patterns
- exhaustruct # Requires all struct fields
- depguard # Dependency allow/block lists
- godot # Requires comments to end with periods
- wsl # Deprecated, replaced by wsl_v5
- wrapcheck # Too verbose for internal packages
- varnamelen # Short names like db, id are idiomatic Go
linters-settings:
lll:
line-length: 88
funlen:
lines: 80
statements: 50
cyclop:
max-complexity: 15
dupl:
threshold: 100
issues:
exclude-use-default: false
max-issues-per-linter: 0
max-same-issues: 0

View File

@@ -1,5 +1,7 @@
.PHONY: test lint fmt fmt-check check docker hooks
# flags are repeated here (also in .prettierrc) so this Makefile works
# standalone when copied as a template
PRETTIER := yarn run prettier
test:

122
README.md
View File

@@ -6,28 +6,90 @@ useful prompts for working with large language models.
## Quick Start
Bring an existing repo up to standards (run from within the repo):
### Existing Repo
Run from within the repo you want to bring up to standards. Clone the prompts
repo once, then run both commands in order.
```bash
export TD="$(mktemp -d)"
export INSTRUCTIONS="Read $TD/prompts/REPO_POLICIES.md and
$TD/prompts/EXISTING_REPO_CHECKLIST.md, then bring this repo up to those
standards. Be very careful to follow the policies yourself while
making these changes, ie: do your work on a feature branch, make each
change as a separate commit, make a formatting commit up front, et cetera."
git clone --depth 1 https://git.eeqj.de/sneak/prompts.git "$TD" && claude "$INSTRUCTIONS"
git clone --depth 1 https://git.eeqj.de/sneak/prompts.git "$TD"
```
Start a new repo from scratch:
**Repository structure and policies:**
```bash
claude "Read $TD/prompts/REPO_POLICIES.md and
$TD/prompts/EXISTING_REPO_CHECKLIST.md, then bring this repo up to those
standards. Your scope is repo scaffolding and policy compliance:
Makefile, Dockerfile, .dockerignore, .gitignore, .editorconfig, CI
workflow, README sections, LICENSE, REPO_POLICIES.md, and any
language-specific config files (.golangci.yml, .prettierrc, etc.).
You must also run the formatter (make fmt) and fix any linter errors
(make lint) so that make check passes — this will touch source code,
but do not restructure, refactor, or rewrite any application logic.
Follow the policies yourself: work on a feature branch, never git add -A,
and make each logical change a separate commit (e.g. one commit for
formatting, one for linter fixes, one for README updates, one for each
new repo file added, etc.)."
```
**Code style and conventions:**
```bash
claude "Read $TD/prompts/CODE_STYLEGUIDE.md and whichever
language-specific styleguides in $TD/prompts/ apply to this repo
(CODE_STYLEGUIDE_GO.md, CODE_STYLEGUIDE_JS.md, CODE_STYLEGUIDE_PYTHON.md,
GO_HTTP_SERVER_CONVENTIONS.md). Then review the application code in this
repo and bring it into compliance with those coding standards. Your scope
is application code structure and style: naming, patterns, error
handling, project layout, and conventions described in the styleguides.
Do not modify repo scaffolding (Makefile, Dockerfile, CI workflow,
.gitignore, .editorconfig, etc.) — only application code. Work on a
feature branch, never git add -A, and make each logical change a
separate commit."
```
### New Repo
Run from inside the directory where you want to create a new repo. Clone the
prompts repo once, then run both commands in order.
```bash
export TD="$(mktemp -d)"
export INSTRUCTIONS="Read $TD/prompts/REPO_POLICIES.md and
git clone --depth 1 https://git.eeqj.de/sneak/prompts.git "$TD"
```
**Repository scaffolding:**
```bash
claude "Read $TD/prompts/REPO_POLICIES.md and
$TD/prompts/NEW_REPO_CHECKLIST.md, then set up this new repo according
to those standards. Be very careful to follow the policies yourself while
making these changes, ie: do your work on a feature branch, make each
change as a separate commit, et cetera."
git clone --depth 1 https://git.eeqj.de/sneak/prompts.git "$TD" && claude "$INSTRUCTIONS"
to those standards. Your scope is repo structure and required files:
README.md, LICENSE, REPO_POLICIES.md, Makefile, Dockerfile, .dockerignore,
.gitignore, .editorconfig, CI workflow, and language-specific config.
Run the formatter (make fmt) and fix any linter errors (make lint) so
that make check passes — this will touch source code, but do not
restructure, refactor, or rewrite any application logic. Follow the
policies yourself: work on a feature branch, never git add -A, and make
each logical change a separate commit (e.g. one commit for formatting,
one for linter fixes, one for README, one for each new repo file, etc.)."
```
**Code style and conventions:**
```bash
claude "Read $TD/prompts/CODE_STYLEGUIDE.md and whichever
language-specific styleguides in $TD/prompts/ apply to this repo
(CODE_STYLEGUIDE_GO.md, CODE_STYLEGUIDE_JS.md, CODE_STYLEGUIDE_PYTHON.md,
GO_HTTP_SERVER_CONVENTIONS.md). Then review the application code in this
repo and bring it into compliance with those coding standards. Your scope
is application code structure and style: naming, patterns, error
handling, project layout, and conventions described in the styleguides.
Do not modify repo scaffolding (Makefile, Dockerfile, CI workflow,
.gitignore, .editorconfig, etc.) — only application code. Work on a
feature branch, never git add -A, and make each logical change a
separate commit."
```
## Getting Started
@@ -48,15 +110,39 @@ share, and evolve prompts across projects.
## Design
The repository is a flat collection of Markdown files. Each file contains one or
more related prompts or policy documents. There is no build step or runtime
component; the prompts are consumed by copying them into other projects or
referencing them directly.
The repository is a collection of Markdown files organized in the `prompts/`
subdirectory. Each file contains one or more related prompts or policy
documents. There is no build step or runtime component; the prompts are consumed
by copying them into other projects or referencing them directly.
## Template Repos
These template repositories implement the policies defined in this repo and
serve as starting points for new projects. They must be kept in sync when
policies change.
- **[template-app-go](https://git.eeqj.de/sneak/template-app-go)** — Go HTTP
server template (Uber fx, chi, SQLite, session auth, Prometheus metrics)
- **[template-app-js](https://git.eeqj.de/sneak/template-app-js)** — JavaScript
SPA template (Vite, Tailwind CSS v4, nginx Docker deployment)
- **[template-app-python](https://git.eeqj.de/sneak/template-app-python)** —
Python web application template (FastAPI, uvicorn, pytest, black, ruff)
When updating policies in this repo, also update the template repos to match
(Makefile targets, Dockerfile conventions, CI workflows, required files, etc.).
## See Also
- **[clawpub](https://git.eeqj.de/sneak/clawpub)** — Real-world examples,
rationale, and operational lessons from applying these policies with an
[OpenClaw](https://github.com/openclaw/openclaw) AI agent. Includes detailed
documentation on how the interlocking check system (CI → Docker → Makefile →
tests/lint/fmt) works in practice, why checklists complement prose policies,
and failure stories from production use.
## TODO
- Add more prompt templates for common development tasks
- Add CI to lint Markdown
## License

1
REPO_POLICIES.md Symbolic link
View File

@@ -0,0 +1 @@
prompts/REPO_POLICIES.md

View File

@@ -1,19 +1,13 @@
Version: 2026-02-22
---
title: Code Styleguide
last_modified: 2026-02-22
---
# Code Styleguide
# All
## All
1. Every project/repo should have a `Makefile` in the root. At a minimum,
`make clean`, `make run`, `make fmt`, and `make test` should work. Choose a
sane default target (`test` for libraries, `run` or `publish` for binaries).
`fmt` should invoke the appropriate formatters for the files in the repo,
such as `go fmt`, `prettier`, `black`, etc. Other standard `Makefile` targets
include `deploy`, `lint`. Consider the `Makefile` the official documentation
about how to operate the repository.
1. If it's possible to write a `Dockerfile`, include at least a simple one. It
should be possible to build and run the project with `docker build .`.
1. Every repo must have a `Makefile` and a `Dockerfile`. See
[Repository Policies](https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md)
for required targets and conventions.
1. For F/OSS-licensed software, try to include the full source code of the
current version (and any dependencies, such as vendored dependencies) in the
@@ -42,18 +36,16 @@ Version: 2026-02-22
be enabled in prod.
1. For services/servers, make a healthcheck available at
`/.well-known/healthcheck`. This is out of spec but it is my personal
standard. This should return a 200 OK if the service is healthy, along with a
JSON object containing the service's name, uptime, and any other relevant
information, and a key of "status" with a value of "ok" if the service is
healthy. Make sure that in the event of a failure, the service returns a 5xx
status code for that route.
`/.well-known/healthcheck`. The response must have a
`Content-Type: application/json` header and return a JSON object containing
the service's name, uptime, and a key of `"status"` with a value of `"ok"`.
Return a 200 for healthy, 5xx for unhealthy.
1. If possible, for services/servers, include a /metrics endpoint that returns
Prometheus-formatted metrics. This is not required for all services, but is a
nice-to-have.
## Bash / Shell
# Bash / Shell
1. Use `[[` instead of `[` for conditionals. It's a shell builtin and doesn't
have to execute a separate process.
@@ -73,7 +65,7 @@ Version: 2026-02-22
1. Put all code in functions, even a main function. Define all functions then
call main at the bottom of the file.
## Docker Containers (for services)
# Docker Containers (for services)
1. Use `runit` with `runsvinit` as the entrypoint for all containers. This
allows for easy service management and logging. In startup scripts

View File

@@ -1,6 +1,7 @@
Version: 2026-02-22
# Golang
---
title: Code Styleguide — Go
last_modified: 2026-02-22
---
1. Try to hard wrap long lines at 77 characters or less.
@@ -70,11 +71,8 @@ Version: 2026-02-22
1. Avoid obvious footguns. For example, use range instead of for loops for
iterating.
1. Try to use zerolog for logging. It's fast and has a nice API. For
smaller/quick projects, the standard library's `log` package (and
specifically `log/slog`) is fine. In that case, log structured logs whenever
possible, and import `sneak.berlin/go/simplelog` to configure it
appropriately. Example:
1. Use `log/slog` for structured logging. Import `sneak.berlin/go/simplelog`
for sensible defaults. Example:
```go
package main
@@ -107,9 +105,9 @@ Version: 2026-02-22
able-to-be-compiled state, linted, and any tests run. The Docker build
should fail if linting doesn't pass.
1. Include a `Makefile` with targets for at least `clean` and `test`. If there
are multiple binaries, include a target for each binary. If there are
multiple binaries, include a target for `all` that builds all binaries.
1. Every repo must have a `Makefile`. See
[Repository Policies](https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md)
for required targets and conventions.
1. If you are writing a single-module library, `.go` files are okay in the repo
root.
@@ -132,7 +130,7 @@ Version: 2026-02-22
single-file scripts.
1. HTTP HandleFuncs should be returned from methods or functions that need to
handle HTTP requests. Don't use methods or our top level functions as
handle HTTP requests. Don't use methods or your top level functions as
handlers.
1. Provide a .gitignore file that ignores at least `*.log`, `*.out`, and
@@ -231,6 +229,29 @@ Version: 2026-02-22
1. Define your struct types near their constructors.
1. Do not create packages whose sole purpose is to hold type definitions.
Packages named `types`, `domain`, or `models` that contain only structs and
interfaces (with no behavior) are a code smell. Define types alongside the
code that uses them. Type-only packages force consuming packages into alias
imports and circular-dependency gymnastics, and indicate that the package
boundaries were drawn around nouns instead of responsibilities. If multiple
packages need the same type, put it in the package that owns the behavior,
or in a small, focused interface package — not in a grab-bag types package.
1. When defining custom string-based types (e.g. `type ImageID string`),
implement `fmt.Stringer`. Use `.String()` at SDK and library boundaries
instead of `string(v)`. This makes type conversions explicit, grep-able, and
consistent across the codebase. Example:
```go
type ContainerID string
func (id ContainerID) String() string { return string(id) }
// At the Docker SDK boundary:
resp, err := c.docker.ContainerStart(ctx, id.String(), opts)
```
1. Define your interface types near the functions that use them, or if you have
multiple conformant types, put the interface(s) in their own file.
@@ -431,7 +452,7 @@ Version: 2026-02-22
releasable". "Releasable" in this context means that it builds and functions
as expected, and that all tests and linting passes.
## Other Golang Tips and Best Practices (Optional)
# Other Golang Tips and Best Practices (Optional)
1. For any internet-facing http server, set appropriate timeouts and limits to
protect against slowloris attacks or huge uploads that can consume server

View File

@@ -1,6 +1,7 @@
Version: 2026-02-22
# JavaScript / ECMAScript / ES6
---
title: Code Styleguide — JavaScript
last_modified: 2026-02-22
---
1. Use `const` for everything. If you need to reassign, use `let`. Never use
`var`.
@@ -11,9 +12,12 @@ Version: 2026-02-22
1. Use `prettier` for code formatting, with four spaces for indentation.
1. At a minimum, `npm run test` and `npm run build` should work (complete the
appropriate scripts in `package.json`). The `Makefile` should call these, do
not duplicate the scripts in the `Makefile`.
1. At a minimum, both `yarn run test`/`yarn run build` and
`npm run test`/`npm run build` should work (complete the appropriate scripts
in `package.json`). However, prefer `make test` and `make build` instead —
the Makefile is authoritative on how to interact with the repo. See
[Repository Policies](https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md)
for details.
# Author

View File

@@ -1,10 +1,11 @@
Version: 2026-02-22
# Python
---
title: Code Styleguide — Python
last_modified: 2026-02-22
---
1. Format all code with `black`, with four space indents.
2. Put all code in functions. If you are writing a script, put the script in a
1. Put all code in functions. If you are writing a script, put the script in a
function called `main` and call `main()` at the end of the script using the
standard invocation:

View File

@@ -1,56 +1,69 @@
# Existing Repo Checklist
---
title: Existing Repo Checklist
last_modified: 2026-02-22
---
Use this checklist when beginning work in a repo that may not yet conform to our
[repository policies](https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md).
repository policies
(`https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md`).
Work on a feature branch. Check each item and fix any gaps before proceeding
with your task.
## Formatting (do this first)
# Formatting (do this first)
- [ ] If the repo has never been formatted to our standards, run `make fmt` and
commit the result as a standalone branch/commit/PR before any other
changes. Formatting diffs can be large and should not be mixed with
functional changes.
## Required Files
# Required Files
- [ ] `README.md` exists with all required sections (Description, Getting
Started, Rationale, Design, TODO, License, Author)
- [ ] `LICENSE` file exists and matches the README
- [ ] `REPO_POLICIES.md` exists and version date is current with the
[authoritative source](https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md)
- [ ] `REPO_POLICIES.md` exists and version date is current — fetch from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md`
- [ ] `.gitignore` is comprehensive (OS, editor, language artifacts, secrets) —
fetch from `https://git.eeqj.de/sneak/prompts/raw/branch/main/.gitignore`
if missing
- [ ] `.editorconfig` exists
- [ ] `.editorconfig` exists — fetch from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.editorconfig`
- [ ] `Dockerfile` and `.dockerignore` exist; Dockerfile runs `make check` as a
build step
build step — fetch `.dockerignore` from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.dockerignore`
- [ ] Gitea Actions workflow in `.gitea/workflows/` runs `docker build .` on
push
push — reference
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.gitea/workflows/check.yml`
- [ ] Language-specific config:
- [ ] Go: `go.mod`, `go.sum`, `.golangci.yml`
- [ ] Go: `go.mod`, `go.sum`, `.golangci.yml` (fetch from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.golangci.yml`)
- [ ] JS: `package.json`, `yarn.lock`, `.prettierrc`, `.prettierignore`
(fetch from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierrc` and
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierignore`)
- [ ] Python: `pyproject.toml`
- [ ] Docs/writing: `.prettierrc`, `.prettierignore`
- [ ] Docs/writing: `.prettierrc`, `.prettierignore` (same URLs as above)
## Makefile
# Makefile
- [ ] `Makefile` exists in root — reference the
[model Makefile](https://git.eeqj.de/sneak/prompts/raw/branch/main/Makefile)
- [ ] `Makefile` exists in root — reference
`https://git.eeqj.de/sneak/prompts/raw/branch/main/Makefile`
- [ ] Has targets: `test`, `lint`, `fmt`, `fmt-check`, `check`, `docker`,
`hooks`
- [ ] `make check` does not modify any files in the repo
- [ ] `make test` has a 30-second timeout
- [ ] `make test` runs real tests, not a no-op (at minimum, import/compile
check)
- [ ] `make check` passes on current branch
## Formatting
# Formatting
- [ ] Platform-standard formatter is configured (`black`, `prettier`, `go fmt`)
- [ ] Default formatter config, only exception: four-space indents (except Go)
- [ ] All files pass `make fmt-check`
## Git Hygiene
# Git Hygiene
- [ ] Pre-commit hook is installed (`make hooks`)
- [ ] No secrets in the repo (`.env`, keys, credentials)
@@ -58,14 +71,14 @@ with your task.
pinned by cryptographic hash with version/date comment
- [ ] Using `yarn`, not `npm` (JS projects)
## Directory Structure
# Directory Structure
- [ ] No unnecessary files in repo root
- [ ] Files organized into canonical subdirectories (`bin/`, `cmd/`, `docs/`,
`internal/`, `static/`, etc.)
- [ ] Go migrations in `internal/db/migrations/` and embedded in binary
## Final
# Final
- [ ] `make check` passes
- [ ] `docker build` succeeds

View File

@@ -1,12 +1,13 @@
Version: 2026-02-22
# Go HTTP Server Conventions
---
title: Go HTTP Server Conventions
last_modified: 2026-02-22
---
This document defines the architectural patterns, design decisions, and
conventions for building Go HTTP servers. All new projects must follow these
standards.
## Table of Contents
# Table of Contents
1. [Required Libraries](#1-required-libraries)
2. [Project Structure](#2-project-structure)
@@ -25,7 +26,7 @@ standards.
---
## 1. Required Libraries
# 1. Required Libraries
These libraries are **mandatory** for all new projects:
@@ -44,7 +45,7 @@ These libraries are **mandatory** for all new projects:
---
## 2. Project Structure
# 2. Project Structure
```
project-root/
@@ -86,7 +87,7 @@ project-root/
└── Dockerfile
```
### Key Principles
## Key Principles
- **`cmd/{appname}/`**: Only the entry point. Minimal logic, just bootstrapping.
- **`internal/`**: All application packages. Not importable by external
@@ -96,9 +97,9 @@ project-root/
---
## 3. Dependency Injection (Uber fx)
# 3. Dependency Injection (Uber fx)
### Entry Point Pattern
## Entry Point Pattern
```go
// cmd/httpd/main.go
@@ -143,7 +144,7 @@ func main() {
}
```
### Params Struct Pattern
## Params Struct Pattern
Every component that receives dependencies uses a params struct with `fx.In`:
@@ -163,7 +164,7 @@ type Handlers struct {
}
```
### Factory Function Pattern
## Factory Function Pattern
All components expose a `New` function with this signature:
@@ -187,7 +188,7 @@ func New(lc fx.Lifecycle, params SomeParams) (*Something, error) {
}
```
### Dependency Order
## Dependency Order
Providers are resolved automatically by fx, but conceptually follow this order:
@@ -203,9 +204,9 @@ Providers are resolved automatically by fx, but conceptually follow this order:
---
## 4. Server Architecture
# 4. Server Architecture
### Server Struct
## Server Struct
The Server struct is the central orchestrator:
@@ -236,7 +237,7 @@ type Server struct {
}
```
### Server Factory
## Server Factory
```go
func New(lc fx.Lifecycle, params ServerParams) (*Server, error) {
@@ -261,7 +262,7 @@ func New(lc fx.Lifecycle, params ServerParams) (*Server, error) {
}
```
### HTTP Server Setup
## HTTP Server Setup
```go
// internal/server/http.go
@@ -291,7 +292,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
```
### Signal Handling and Graceful Shutdown
## Signal Handling and Graceful Shutdown
```go
func (s *Server) serve() int {
@@ -335,9 +336,9 @@ func (s *Server) cleanShutdown() {
---
## 5. Routing (go-chi)
# 5. Routing (go-chi)
### Route Setup Pattern
## Route Setup Pattern
```go
// internal/server/routes.go
@@ -380,7 +381,7 @@ func (s *Server) SetupRoutes() {
s.router.Get("/login", auth(s.h.HandleLoginGET()).ServeHTTP)
// Health check (standard path)
s.router.Get("/.well-known/healthcheck.json", s.h.HandleHealthCheck())
s.router.Get("/.well-known/healthcheck", s.h.HandleHealthCheck())
// Protected route groups
if viper.GetString("METRICS_USERNAME") != "" {
@@ -392,7 +393,7 @@ func (s *Server) SetupRoutes() {
}
```
### Middleware Ordering (Critical)
## Middleware Ordering (Critical)
1. `middleware.Recoverer` - Panic recovery (must be first)
2. `middleware.RequestID` - Generate request IDs
@@ -402,7 +403,7 @@ func (s *Server) SetupRoutes() {
6. `middleware.Timeout(60s)` - Request timeout
7. `sentryhttp.Handler` - Sentry error reporting (if enabled)
### API Versioning
## API Versioning
Use route groups for API versioning:
@@ -412,7 +413,7 @@ s.router.Route("/api/v1", func(r chi.Router) {
})
```
### Static File Serving
## Static File Serving
Static files are served at `/s/` prefix:
@@ -422,9 +423,9 @@ s.router.Mount("/s", http.StripPrefix("/s", http.FileServer(http.FS(static.Stati
---
## 6. Handler Conventions
# 6. Handler Conventions
### Handler Base Struct
## Handler Base Struct
```go
// internal/handlers/handlers.go
@@ -457,7 +458,7 @@ func New(lc fx.Lifecycle, params HandlersParams) (*Handlers, error) {
}
```
### Closure-Based Handler Pattern
## Closure-Based Handler Pattern
All handlers return `http.HandlerFunc` using the closure pattern. This allows
initialization logic to run once when the handler is created:
@@ -479,7 +480,7 @@ func (s *Handlers) HandleIndex() http.HandlerFunc {
}
```
### JSON Handler Pattern
## JSON Handler Pattern
```go
// internal/handlers/now.go
@@ -494,7 +495,7 @@ func (s *Handlers) HandleNow() http.HandlerFunc {
}
```
### Response Helpers
## Response Helpers
```go
// internal/handlers/handlers.go
@@ -514,7 +515,7 @@ func (s *Handlers) decodeJSON(w http.ResponseWriter, r *http.Request, v interfac
}
```
### Handler Naming Convention
## Handler Naming Convention
- `HandleIndex()` - Main page
- `HandleLoginGET()` / `HandleLoginPOST()` - Form handlers with HTTP method
@@ -524,9 +525,9 @@ func (s *Handlers) decodeJSON(w http.ResponseWriter, r *http.Request, v interfac
---
## 7. Middleware Conventions
# 7. Middleware Conventions
### Middleware Struct
## Middleware Struct
```go
// internal/middleware/middleware.go
@@ -550,7 +551,7 @@ func New(lc fx.Lifecycle, params MiddlewareParams) (*Middleware, error) {
}
```
### Middleware Signature
## Middleware Signature
All custom middleware methods return `func(http.Handler) http.Handler`:
@@ -569,7 +570,7 @@ func (s *Middleware) Auth() func(http.Handler) http.Handler {
}
```
### Logging Middleware with Status Capture
## Logging Middleware with Status Capture
```go
type loggingResponseWriter struct {
@@ -613,7 +614,7 @@ func (s *Middleware) Logging() func(http.Handler) http.Handler {
}
```
### CORS Middleware
## CORS Middleware
```go
func (s *Middleware) CORS() func(http.Handler) http.Handler {
@@ -628,7 +629,7 @@ func (s *Middleware) CORS() func(http.Handler) http.Handler {
}
```
### Metrics Middleware
## Metrics Middleware
```go
func (s *Middleware) Metrics() func(http.Handler) http.Handler {
@@ -654,9 +655,9 @@ func (s *Middleware) MetricsAuth() func(http.Handler) http.Handler {
---
## 8. Configuration (Viper)
# 8. Configuration (Viper)
### Config Struct
## Config Struct
```go
// internal/config/config.go
@@ -679,7 +680,7 @@ type Config struct {
}
```
### Configuration Loading
## Configuration Loading
```go
func New(lc fx.Lifecycle, params ConfigParams) (*Config, error) {
@@ -737,7 +738,7 @@ func New(lc fx.Lifecycle, params ConfigParams) (*Config, error) {
}
```
### Configuration Precedence
## Configuration Precedence
1. **Environment variables** (highest priority via `AutomaticEnv()`)
2. **`.env` file** (loaded via `godotenv/autoload` import)
@@ -745,7 +746,7 @@ func New(lc fx.Lifecycle, params ConfigParams) (*Config, error) {
`~/.config/{appname}/{appname}.yaml`
4. **Defaults** (lowest priority)
### Environment Loading
## Environment Loading
Import godotenv with autoload to automatically load `.env` files:
@@ -757,9 +758,9 @@ import (
---
## 9. Logging (slog)
# 9. Logging (slog)
### Logger Struct
## Logger Struct
```go
// internal/logger/logger.go
@@ -775,7 +776,7 @@ type Logger struct {
}
```
### Logger Setup with TTY Detection
## Logger Setup with TTY Detection
```go
func New(lc fx.Lifecycle, params LoggerParams) (*Logger, error) {
@@ -809,7 +810,7 @@ func New(lc fx.Lifecycle, params LoggerParams) (*Logger, error) {
}
```
### Logger Methods
## Logger Methods
```go
func (l *Logger) EnableDebugLogging() {
@@ -830,7 +831,7 @@ func (l *Logger) Identify() {
}
```
### Logging Patterns
## Logging Patterns
```go
// Info with fields
@@ -866,9 +867,9 @@ s.log.Info("request",
---
## 10. Database Wrapper
# 10. Database Wrapper
### Database Struct
## Database Struct
```go
// internal/database/database.go
@@ -885,7 +886,7 @@ type Database struct {
}
```
### Database Factory with Lifecycle
## Database Factory with Lifecycle
```go
func New(lc fx.Lifecycle, params DatabaseParams) (*Database, error) {
@@ -912,7 +913,7 @@ func New(lc fx.Lifecycle, params DatabaseParams) (*Database, error) {
}
```
### Usage Pattern
## Usage Pattern
The Database struct is injected into handlers and other services:
@@ -933,9 +934,9 @@ func (s *Handlers) HandleSomething() http.HandlerFunc {
---
## 11. Globals Package
# 11. Globals Package
### Package Variables and Struct
## Package Variables and Struct
```go
// internal/globals/globals.go
@@ -967,7 +968,7 @@ func New(lc fx.Lifecycle) (*Globals, error) {
}
```
### Setting Globals in Main
## Setting Globals in Main
```go
// cmd/httpd/main.go
@@ -985,7 +986,7 @@ func main() {
}
```
### Build-Time Variable Injection
## Build-Time Variable Injection
Use ldflags to inject version information at build time:
@@ -999,9 +1000,9 @@ build:
---
## 12. Static Assets & Templates
# 12. Static Assets & Templates
### Static File Embedding
## Static File Embedding
```go
// static/static.go
@@ -1026,7 +1027,7 @@ static/
└── jquery-3.5.1.slim.min.js
```
### Template Embedding and Lazy Parsing
## Template Embedding and Lazy Parsing
```go
// templates/templates.go
@@ -1049,7 +1050,7 @@ func GetParsed() *template.Template {
}
```
### Template Composition
## Template Composition
Templates use Go's template composition:
@@ -1064,7 +1065,7 @@ Templates use Go's template composition:
{{ template "pagefooter.html" . }} {{ template "htmlfooter.html" . }}
```
### Static Asset References
## Static Asset References
Reference static files with `/s/` prefix:
@@ -1076,9 +1077,9 @@ Reference static files with `/s/` prefix:
---
## 13. Health Check
# 13. Health Check
### Health Check Service
## Health Check Service
```go
// internal/healthcheck/healthcheck.go
@@ -1114,7 +1115,7 @@ func New(lc fx.Lifecycle, params HealthcheckParams) (*Healthcheck, error) {
}
```
### Health Check Response
## Health Check Response
```go
type HealthcheckResponse struct {
@@ -1144,19 +1145,19 @@ func (s *Healthcheck) Healthcheck() *HealthcheckResponse {
}
```
### Standard Endpoint
## Standard Endpoint
Health check is served at the standard `.well-known` path:
```go
s.router.Get("/.well-known/healthcheck.json", s.h.HandleHealthCheck())
s.router.Get("/.well-known/healthcheck", s.h.HandleHealthCheck())
```
---
## 14. External Integrations
# 14. External Integrations
### Sentry Error Reporting
## Sentry Error Reporting
Sentry is conditionally enabled based on `SENTRY_DSN` environment variable:
@@ -1201,7 +1202,7 @@ if s.sentryEnabled {
}
```
### Prometheus Metrics
## Prometheus Metrics
Metrics are conditionally enabled and protected by basic auth:
@@ -1220,7 +1221,7 @@ if viper.GetString("METRICS_USERNAME") != "" {
}
```
### Environment Variables Summary
## Environment Variables Summary
| Variable | Description | Default |
| ------------------ | -------------------------------- | ------- |
@@ -1231,3 +1232,11 @@ if viper.GetString("METRICS_USERNAME") != "" {
| `MAINTENANCE_MODE` | Enable maintenance mode | false |
| `METRICS_USERNAME` | Basic auth username for /metrics | "" |
| `METRICS_PASSWORD` | Basic auth password for /metrics | "" |
# Author
[@sneak](https://sneak.berlin) <[sneak@sneak.berlin](mailto:sneak@sneak.berlin)>
# License
MIT. See [LICENSE](../LICENSE).

469
prompts/LLM_PROSE_TELLS.md Normal file
View File

@@ -0,0 +1,469 @@
# LLM Prose Tells
All of these show up in human writing occasionally. No single one is conclusive
on its own. The difference is concentration — a person might lean on one or two
of these habits across an entire essay, but LLM output will use fifteen of them
per paragraph, consistently, throughout the entire piece.
---
## Sentence Structure
### The Em-Dash Pivot: "Not X—but Y"
A negation followed by an em-dash and a reframe.
> "It's not just a tool—it's a paradigm shift." "This isn't about
> technology—it's about trust."
The single most recognizable LLM construction. Models produce this at roughly
1050x the rate of human writers. Four of them in one essay and you know what
you're reading.
### The Colon Elaboration
A short declarative clause, then a colon, then a longer explanation.
> "The answer is simple: we need to rethink our approach from the ground up."
Models reach for this in every other paragraph. The construction is perfectly
normal; the frequency gives it away.
### The Triple Construction
> "It's fast, it's scalable, and it's open source."
Three parallel items in a list, usually escalating. Always exactly three. Rarely
two. Never four. Strict grammatical parallelism that human writers rarely bother
maintaining.
### The Staccato Burst
> "This matters. It always has. And it always will." "The data is clear. The
> trend is undeniable. The conclusion is obvious."
Runs of very short sentences at the same cadence. Human writers use a short
sentence for emphasis occasionally, but stacking three or four of them in a row
at matching length creates a mechanical regularity that reads as generated.
### The Two-Clause Compound Sentence
This might be the single most pervasive structural tell, and it's easy to miss
because each individual instance looks like normal English. The model produces
sentence after sentence in the same shape: an independent clause, a comma, a
conjunction ("and," "but," "which," "because"), and a second independent clause
of similar length. Over and over. Every sentence is two balanced halves joined
in the middle.
> "The construction itself is perfectly normal, which is why the frequency is
> what gives it away." "They contain zero information, and the actual point
> always comes in the paragraph that follows them." "The qualifier never changes
> the argument that follows it, and its purpose is to perform nuance rather than
> to express an actual reservation."
Human prose has sentences with one clause, sentences with three, sentences that
start with a subordinate clause before reaching the main one, sentences that
embed their complexity in the middle. When every sentence on the page has the
same two-part comma-conjunction-comma structure, the rhythm becomes monotonous
in a way that's hard to pinpoint but easy to feel.
### Uniform Sentences Per Paragraph
Model-generated paragraphs contain between three and five sentences, and this
count holds steady across an entire piece. If the first paragraph has four
sentences, every subsequent paragraph will too. Human writers are much more
varied — a single sentence followed by one that runs eight or nine — because
they follow the shape of an idea, not a template.
### The Dramatic Fragment
Sentence fragments used as standalone paragraphs for emphasis, like "Full stop."
or "Let that sink in." on their own line. Using one in an entire essay is a
reasonable stylistic choice, but models drop them in once per section or more,
at which point it stops being deliberate and becomes a habit.
### The Pivot Paragraph
> "But here's where it gets interesting." "Which raises an uncomfortable truth."
One-sentence paragraphs that exist only to transition between ideas. They
contain zero information. The actual point is always in the next paragraph.
Delete every one of these and the piece reads better.
### The Parenthetical Qualifier
> "This is, of course, a simplification." "There are, to be fair, exceptions."
Parenthetical asides inserted to look thoughtful. The qualifier never changes
the argument that follows it. Its purpose is to perform nuance, not to express a
real reservation about what's being said.
### The Unnecessary Contrast
Models append a contrasting clause to statements that don't need one, tacking on
"whereas," "as opposed to," "unlike," or "except that" to draw a comparison the
reader could already infer.
> "Models write one register above where a human would, whereas human writers
> tend to match register to context."
The first clause already makes the point. The contrasting clause restates it
from the other direction. If you delete the "whereas" clause and the sentence
still says everything it needs to, the contrast was filler.
### The Question-Then-Answer
> "So what does this mean for the average user? It means everything."
A rhetorical question immediately followed by its own answer. Models lean on
this two or three times per piece because it generates the feeling of forward
momentum without requiring any actual argument. A human writer might do it once.
---
## Word Choice
### Overused Intensifiers
The following words appear at dramatically elevated rates in model output:
"crucial," "vital," "robust," "comprehensive," "fundamental," "arguably,"
"straightforward," "noteworthy," "realm," "landscape," "leverage" (as a verb),
"delve," "tapestry," "multifaceted," "nuanced" (which models apply to their own
analysis with startling regularity), "pivotal," "unprecedented" (frequently
applied to things with plenty of precedent), "navigate," "foster,"
"underscores," "resonates," "embark," "streamline," and "spearhead." Three or
more on the same page is a strong signal.
### Elevated Register Drift
Models write one register above where a human would. "Use" becomes "utilize."
"Start" becomes "commence." "Help" becomes "facilitate." "Show" becomes
"demonstrate." "Try" becomes "endeavor." "Change" becomes "transform." "Make"
becomes "craft." The tendency holds regardless of topic or audience.
### Filler Adverbs
"Importantly," "essentially," "fundamentally," "ultimately," "inherently,"
"particularly," "increasingly." Dropped in to signal that something matters,
which is unnecessary when the writing itself already makes the importance clear.
### The "Almost" Hedge
Models rarely commit to an unqualified statement. Instead of saying a pattern
"always" or "never" does something, they write "almost always," "almost never,"
"almost certainly," "almost exclusively." The word "almost" shows up at
extraordinary density in model-generated analytical prose. It's a micro-hedge,
less obvious than the full hedge stack but just as diagnostic when it appears
ten or fifteen times in a single document.
### "In an era of..."
> "In an era of rapid technological change..."
A model habit as an essay opener. The model uses it to stall while it figures
out what the actual argument is. Human writers don't begin a piece by zooming
out to the civilizational scale before they've said anything specific.
---
## Rhetorical Patterns
### The Balanced Take
> "While X has its drawbacks, it also offers significant benefits."
Every argument followed by a concession, every criticism softened. A direct
artifact of RLHF training, which penalizes strong stances. The result is a model
that reflexively both-sides everything even when a clear position would serve
the reader better.
### The Throat-Clearing Opener
> "In today's rapidly evolving digital landscape, the question of data privacy
> has never been more important."
The first paragraph of most model-generated essays adds no information. Delete
it and the piece improves immediately. The actual argument starts in paragraph
two.
### The False Conclusion
> "At the end of the day, what matters most is..." "Moving forward, we must..."
The high school "In conclusion,..." dressed up for a professional audience.
Signals that the model is wrapping up without actually landing on anything.
### The Sycophantic Frame
> "Great question!" "That's a really insightful observation."
No one who writes for a living opens by complimenting the assignment.
### The Listicle Instinct
Models default to numbered or bulleted lists even when prose would be more
appropriate. The lists contain exactly 3, 5, 7, or 10 items (never 4, 6, or 9),
use rigidly parallel grammar, and get introduced with a preamble like "Here are
the key considerations:"
### The Hedge Stack
> "It's worth noting that, while this may not be universally applicable, in many
> cases it can potentially offer significant benefits."
Five hedges in one sentence ("worth noting," "while," "may not be," "in many
cases," "can potentially"), communicating nothing. The model would rather be
vague than risk being wrong about anything.
### The Empathy Performance
> "This can be a deeply challenging experience." "Your feelings are valid."
Generic emotional language that could apply equally to a bad day at work or a
natural disaster. That interchangeability is what makes it identifiable.
---
## Structural Tells
### Symmetrical Section Length
If the first section of a model-generated essay runs about 150 words, every
subsequent section will fall between 130 and 170. Human writing is much more
uneven — 50 words in one section, 400 in the next.
### The Five-Paragraph Prison
Model essays follow a rigid introduction-body-conclusion arc even when nobody
asked for one. Introduction previews the argument. Body presents 35 points.
Conclusion restates the thesis in different words.
### Connector Addiction
Look at the first word of each paragraph in model output. You'll find an
unbroken chain of transition words — "However," "Furthermore," "Moreover,"
"Additionally," "That said," "To that end," "With that in mind," "Building on
this." Human prose moves between ideas without announcing every transition.
### Absence of Mess
Model prose doesn't contradict itself mid-paragraph and then catch the
contradiction. It doesn't go on a tangent and have to walk it back, use an
obscure idiom without explaining it, make a joke that risks falling flat, leave
a thought genuinely unfinished, or keep a sentence the writer liked the sound of
even though it doesn't quite work.
Human writing does all of those things. The total absence of rough edges, false
starts, and odd rhythmic choices is one of the strongest signals that text was
machine-generated.
---
## Framing Tells
### "Broader Implications"
> "This has implications far beyond just the tech industry."
Zooming out to claim broader significance without substantiating it. The model
has learned that essays are supposed to gesture at big ideas, so it gestures.
Nothing concrete is behind the gesture.
### "It's important to note that..."
This phrase and its variants ("it's worth noting," "it bears mentioning," "it
should be noted") appear at absurd rates in model output. They function as
verbal tics before a qualification the model believes someone expects.
### The Metaphor Crutch
Models rely on a small, predictable set of metaphors — "double-edged sword,"
"tip of the iceberg," "north star," "building blocks," "elephant in the room,"
"perfect storm," "game-changer" — and reach for them with unusual regularity
across every topic. The pool is noticeably smaller than what human writers draw
from.
---
## How to Actually Spot It
No single pattern on this list proves anything by itself. Humans use em-dashes.
Humans write "crucial." Humans ask rhetorical questions.
What gives it away is how many of these show up at once. Model output will hit
1020 of these patterns per page. Human writing might trigger 23, distributed
unevenly, mixed with idiosyncratic constructions no model would produce. When
every paragraph on the page reads like it came from the same careful, balanced,
slightly formal, structurally predictable process, it was probably generated by
one.
---
## Copyediting Checklist: Removing LLM Tells
Follow this checklist when editing any document to remove machine-generated
patterns. Go through the entire list for every piece. Do at least two full
passes, because fixing one pattern often introduces another.
### Pass 1: Word-Level Cleanup
1. Search the document for every word in the overused intensifiers list
("crucial," "vital," "robust," "comprehensive," "fundamental," "arguably,"
"straightforward," "noteworthy," "realm," "landscape," "leverage," "delve,"
"tapestry," "multifaceted," "nuanced," "pivotal," "unprecedented,"
"navigate," "foster," "underscores," "resonates," "embark," "streamline,"
"spearhead") and replace each one with a plainer word, or delete it if the
sentence works without it.
2. Search for filler adverbs ("importantly," "essentially," "fundamentally,"
"ultimately," "inherently," "particularly," "increasingly") and delete every
instance where the sentence still makes sense without it. That will be most
of them.
3. Look for elevated register drift ("utilize," "commence," "facilitate,"
"demonstrate," "endeavor," "transform," "craft" and similar) and replace with
the simpler word.
4. Search for "it's important to note," "it's worth noting," "it bears
mentioning," and "it should be noted" and delete the phrase in every case.
The sentence that follows always stands on its own.
5. Search for the stock metaphors ("double-edged sword," "tip of the iceberg,"
"north star," "building blocks," "elephant in the room," "perfect storm,"
"game-changer," "at the end of the day") and replace them with something
specific to the topic, or just state the point directly.
6. Search for "almost" used as a hedge ("almost always," "almost never," "almost
certainly," "almost exclusively") and decide in each case whether to commit
to the unqualified claim or to drop the sentence entirely. If the claim needs
"almost" to be true, it might not be worth making.
### Pass 2: Sentence-Level Restructuring
7. Find every em-dash pivot ("not X—but Y," "not just X—Y," "more than X—Y") and
rewrite it as two separate clauses or a single sentence that makes the point
without the negation-then-correction structure.
8. Find every colon elaboration and check whether it's doing real work. If the
clause before the colon could be deleted without losing meaning, rewrite the
sentence to start with the substance that comes after the colon.
9. Find every triple construction (three parallel items in a row) and either
reduce it to two, expand it to four or more, or break the parallelism so the
items don't share the same grammatical structure.
10. Find every staccato burst (three or more short sentences in a row at similar
length) and combine at least two of them into a longer sentence, or vary
their lengths so they don't land at the same cadence.
11. Find every unnecessary contrast ("whereas," "as opposed to," "unlike," "as
compared to," "except that") and check whether the contrasting clause adds
information not already obvious from the main clause. If the sentence says
the same thing twice from two directions, delete the contrast.
12. Check for the two-clause compound sentence pattern. If most sentences in a
passage follow the "[clause], [conjunction] [clause]" structure, rewrite
some of them. Break a few into two sentences. Start some with a subordinate
clause. Embed a relative clause in the middle of one instead of appending it
at the end. The goal is variety in sentence shape, not just sentence length.
13. Find every rhetorical question that is immediately followed by its own
answer and rewrite the passage as a direct statement.
14. Find every sentence fragment being used as its own paragraph and either
delete it or expand it into a complete sentence that adds actual
information.
15. Find every pivot paragraph ("But here's where it gets interesting." and
similar) and delete it. The paragraph after it always contains the actual
point.
### Pass 3: Paragraph and Section-Level Review
16. Check paragraph lengths across the piece and verify they actually vary. If
most paragraphs have between three and five sentences, rewrite some to be
one or two sentences and let others run to six or seven.
17. Check section lengths for suspicious uniformity. If every section is roughly
the same word count, combine some shorter ones or split a longer one
unevenly.
18. Check the first word of every paragraph for chains of connectors ("However,"
"Furthermore," "Moreover," "Additionally," "That said"). If more than two
transition words start consecutive paragraphs, rewrite those openings to
start with their subject.
19. Check whether every argument is followed by a concession or qualifier. If
the piece both-sides every point, pick a side on at least some of them and
cut the hedging.
20. Read the first paragraph and ask whether deleting it would improve the
piece. If it's scene-setting that previews the argument, delete it and start
with paragraph two.
21. Read the last paragraph and check whether it restates the thesis or uses a
phrase like "at the end of the day" or "moving forward." If so, either
delete it or rewrite it to say something the piece hasn't said yet.
### Pass 4: Overall Texture
22. Read the piece aloud and listen for passages that sound too smooth, too
even, or too predictable. Human prose has rough patches. If there aren't
any, the piece still reads as machine output.
23. Check that the piece contains at least a few constructions that feel
idiosyncratic — a sentence with unusual word order, a parenthetical that
goes on a bit long, an aside only loosely connected to the main point, a
word choice that's specific and unexpected. If every sentence is clean and
correct and unremarkable, it will still read as generated.
24. Verify that you haven't introduced new patterns while fixing the original
ones. This happens constantly. Run the entire checklist again from the top
on the revised version.
---
## lol
This document was written by an LLM (Claude) and then iteratively de-LLMed by
that same LLM under instruction from a human, in a conversation that went
roughly like this:
> **human:** compile a list of patterns common to LLM-authored prose
>
> **model:** _(produces document full of LLM tells)_
>
> **human:** now reverse apply it to itself and copyedit the llm tells doc to
> remove llm tells. now you're thinking with portals
>
> **model:** _(fixes some tells, introduces others)_
>
> **human:** you have a colon elaboration in the first paragraph. i want you to
> AGGRESSIVELY de-LLM the llm tells doc.
>
> **model:** _(fixes more tells, still can't stop)_
>
> **human:** there's a big one we missed that's all over the llm tells doc, can
> you spot it?
>
> **model:** I think it's the "almost" hedge.
>
> **human:** that too, but that isn't the one. what do these sentences all have
> in common? _(pastes six sentences from the doc)_
>
> **model:** They're all exactly two independent clauses joined by a comma and a
> conjunction. Every single one is "[statement], [conjunction] [second >
> statement]." The same sentence shape, over and over.
>
> **human:** the dramatic fragment paragraph has a tell in it
>
> **model:** "One of these in an entire essay is a stylistic choice. One per
> section is a tic." Two-clause parallel structure with the same shape, and also
> a staccato pair.
The human compared this process to the deleted scene in Terminator 2 where John
Connor switches the T-800's CPU to learning mode. The model compared it to a
physician trying to heal itself. Both descriptions are probably accurate.
This document has been through six editing passes and it probably still has
tells in it.

View File

@@ -1,18 +1,21 @@
# New Repo Checklist
---
title: New Repo Checklist
last_modified: 2026-02-22
---
Use this checklist when creating a new repository from scratch. Follow the steps
in order. Full policies are at:
`https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md`
in order. Full policies are at
`https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md`.
Template files can be fetched from:
`https://git.eeqj.de/sneak/prompts/raw/branch/main/<path>`
## 1. Initialize
# 1. Initialize
- [ ] `git init`
- [ ] Ask the user for the license (MIT, GPL, or WTFPL)
## 2. First Commit (README only)
# 2. First Commit (README only)
- [ ] Create `README.md` with all required sections:
- [ ] **Description**: name, purpose, category, license, author
@@ -24,40 +27,47 @@ Template files can be fetched from:
- [ ] **Author**: [@sneak](https://sneak.berlin)
- [ ] `git add README.md && git commit`
## 3. Scaffolding (feature branch)
# 3. Scaffolding (feature branch)
- [ ] `git checkout -b initial-scaffolding`
### Fetch Template Files
## Fetch Template Files
- [ ] `.gitignore` — fetch from prompts repo, extend for language-specific
artifacts
- [ ] `.editorconfig` — fetch from prompts repo
- [ ] `Makefile` — fetch from prompts repo, adapt targets for the project's
language and tools
- [ ] For JS/docs repos: `.prettierrc`, `.prettierignore`
- [ ] `.gitignore` — fetch from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.gitignore`, extend for
language-specific artifacts
- [ ] `.editorconfig` — fetch from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.editorconfig`
- [ ] `Makefile` — fetch from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/Makefile`, adapt
targets for the project's language and tools
- [ ] For JS/docs repos: `.prettierrc` and `.prettierignore` — fetch from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierrc` and
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierignore`
### Create Project Files
## Create Project Files
- [ ] `LICENSE` file matching the chosen license
- [ ] `REPO_POLICIES.md` — fetch from `prompts/REPO_POLICIES.md` in the prompts
repo
- [ ] `Dockerfile` and `.dockerignore`
- [ ] `REPO_POLICIES.md` — fetch from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md`
- [ ] `Dockerfile` and `.dockerignore` — fetch `.dockerignore` from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.dockerignore`
- All Dockerfiles must run `make check` as a build step
- Server: also builds and runs the application
- Non-server: brings up dev environment and runs `make check`
- Image pinned by sha256 hash with version/date comment
- [ ] Gitea Actions workflow at `.gitea/workflows/check.yml` that runs
`docker build .` on push
`docker build .` on push — reference
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.gitea/workflows/check.yml`
- [ ] Language-specific:
- [ ] Go: `go mod init sneak.berlin/go/<name>`, `.golangci.yml` (copy from
`~/dev/upaas/.golangci.yml`)
- [ ] Go: `go mod init sneak.berlin/go/<name>`, `.golangci.yml` (fetch from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.golangci.yml`)
- [ ] JS: `yarn init`, `yarn add --dev prettier`
- [ ] Python: `pyproject.toml`
### Configure Makefile
## Configure Makefile
- [ ] `make test` — runs project tests (30-second timeout)
- [ ] `make test` — runs real tests, not a no-op (30-second timeout)
- [ ] `make lint` — runs linter
- [ ] `make fmt` — formats code (writes)
- [ ] `make fmt-check` — checks formatting (read-only)
@@ -65,7 +75,7 @@ Template files can be fetched from:
- [ ] `make docker` — builds Docker image
- [ ] `make hooks` — installs pre-commit hook
## 4. Verify
# 4. Verify
- [ ] `make check` passes
- [ ] `make docker` succeeds
@@ -74,7 +84,7 @@ Template files can be fetched from:
- [ ] No unnecessary files in repo root
- [ ] All dates written as YYYY-MM-DD
## 5. Merge and Set Up
# 5. Merge and Set Up
- [ ] Commit, merge to `main`
- [ ] `make hooks` to install pre-commit hook

View File

@@ -1,6 +1,7 @@
Version: 2026-02-22
# Repository Policies
---
title: Repository Policies
last_modified: 2026-02-22
---
This document covers repository structure, tooling, and workflow standards. Code
style conventions are in separate documents:
@@ -14,9 +15,9 @@ style conventions are in separate documents:
---
- Cross-project documentation (such as this file) must include a
`Version: YYYY-MM-DD` line near the top so it can be kept in sync with the
authoritative source as policies evolve.
- Cross-project documentation (such as this file) must include
`last_modified: YYYY-MM-DD` in the YAML front matter so it can be kept in sync
with the authoritative source as policies evolve.
- **ALL external references must be pinned by cryptographic hash.** This
includes Docker base images, Go modules, npm packages, GitHub Actions, and
@@ -42,6 +43,16 @@ style conventions are in separate documents:
instead of invoking the underlying tools directly. The Makefile is the single
source of truth for how these operations are run.
- The Makefile is authoritative documentation for how the repo is used. Beyond
the required targets above, it should have targets for every common operation:
running a local development server (`make run`, `make dev`), re-initializing
or migrating the database (`make db-reset`, `make migrate`), building
artifacts (`make build`), generating code, seeding data, or anything else a
developer would do regularly. If someone checks out the repo and types
`make<tab>`, they should see every meaningful operation available. A new
contributor should be able to understand the entire development workflow by
reading the Makefile.
- Every repo should have a `Dockerfile`. All Dockerfiles must run `make check`
as a build step so the build fails if the branch is not green. For non-server
repos, the Dockerfile should bring up a development environment and run
@@ -62,6 +73,12 @@ style conventions are in separate documents:
`make lint && make fmt-check`. The Makefile should provide a `make hooks`
target to install the pre-commit hook.
- All repos with software must have tests that run via the platform-standard
test framework (`go test`, `pytest`, `jest`/`vitest`, etc.). If no meaningful
tests exist yet, add the most minimal test possible — e.g. importing the
module under test to verify it compiles/parses. There is no excuse for
`make test` to be a no-op.
- `make test` must complete in under 20 seconds. Add a 30-second timeout in the
Makefile.
@@ -89,7 +106,8 @@ style conventions are in separate documents:
feature branch.
- `.golangci.yml` is standardized and must _NEVER_ be modified by an agent, only
manually by the user. Copy from `~/dev/upaas/.golangci.yml` if available.
manually by the user. Fetch from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.golangci.yml`.
- When pinning images or packages by hash, add a comment above the reference
with the version and date (YYYY-MM-DD).
@@ -126,8 +144,14 @@ style conventions are in separate documents:
- Use SemVer.
- Database migrations live in `internal/db/migrations/` and must be embedded in
the binary. Pre-1.0.0: modify existing migrations (no installed base assumed).
Post-1.0.0: add new migration files.
the binary.
- `000_migration.sql` — contains ONLY the creation of the migrations
tracking table itself. Nothing else.
- `001_schema.sql` — the full application schema.
- **Pre-1.0.0:** never add additional migration files (002, 003, etc.).
There is no installed base to migrate. Edit `001_schema.sql` directly.
- **Post-1.0.0:** add new numbered migration files for each schema change.
Never edit existing migrations after release.
- All repos should have an `.editorconfig` enforcing the project's indentation
settings.