Compare commits
34 Commits
improve-qu
...
update-doc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ec7ecfca8 | ||
| 41005ecbe5 | |||
| eb6b11ee23 | |||
| ee4f9039f2 | |||
|
|
18173fabc6 | ||
| 68a00dc545 | |||
|
|
533e77ad34 | ||
| 492fb85500 | |||
|
|
5c02cf8bde | ||
| 3ce000178f | |||
|
|
771551baed | ||
|
|
720d6ee57c | ||
|
|
5e15d77d8e | ||
|
|
2f4f5c9cab | ||
| 7eae7dcc6c | |||
|
|
6401aa482f | ||
|
|
e45ffacd80 | ||
|
|
c8ad5762ab | ||
| e0e607713e | |||
|
|
3fcc1750ff | ||
|
|
45b379011d | ||
| 58d564b641 | |||
| a1052b758f | |||
|
|
a2dd953601 | ||
|
|
f921dee839 | ||
| a1ffb1591b | |||
|
|
699f97d093 | ||
| 1955922857 | |||
|
|
a8cf966df6 | ||
|
|
dcb6ca4339 | ||
| dda0d01faa | |||
|
|
7676ec16c3 | ||
|
|
f9dcef4c9e | ||
| 189e54862e |
25
README.md
25
README.md
@@ -115,6 +115,31 @@ 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
|
||||
|
||||
@@ -229,6 +229,29 @@ last_modified: 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.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Existing Repo Checklist
|
||||
last_modified: 2026-02-22
|
||||
last_modified: 2026-03-10
|
||||
---
|
||||
|
||||
Use this checklist when beginning work in a repo that may not yet conform to our
|
||||
@@ -78,6 +78,22 @@ with your task.
|
||||
`internal/`, `static/`, etc.)
|
||||
- [ ] Go migrations in `internal/db/migrations/` and embedded in binary
|
||||
|
||||
# HTTP Service Hardening (if targeting 1.0 and the repo is an HTTP/web service)
|
||||
|
||||
- [ ] Security headers set on all responses (HSTS, CSP, X-Frame-Options,
|
||||
X-Content-Type-Options, Referrer-Policy, Permissions-Policy)
|
||||
- [ ] Request body size limits enforced on all endpoints
|
||||
- [ ] Read/write/idle timeouts configured on the HTTP server (slowloris defense)
|
||||
- [ ] Per-handler execution time limits in place
|
||||
- [ ] Password-based auth endpoints are rate-limited
|
||||
- [ ] CSRF tokens on all state-mutating HTML forms
|
||||
- [ ] Passwords hashed with bcrypt, scrypt, or argon2
|
||||
- [ ] Session cookies use HttpOnly, Secure, and SameSite attributes
|
||||
- [ ] True client IP correctly detected behind reverse proxy (trusted proxy
|
||||
allowlist configured)
|
||||
- [ ] CORS restricted to explicit origin allowlist for authenticated endpoints
|
||||
- [ ] Error responses do not leak stack traces, SQL queries, or internal paths
|
||||
|
||||
# Final
|
||||
|
||||
- [ ] `make check` passes
|
||||
|
||||
449
prompts/LLM_PROSE_TELLS.md
Normal file
449
prompts/LLM_PROSE_TELLS.md
Normal file
@@ -0,0 +1,449 @@
|
||||
# LLM Prose Tells
|
||||
|
||||
A catalog of patterns found in LLM-generated prose.
|
||||
|
||||
---
|
||||
|
||||
## 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."
|
||||
|
||||
### Em-Dash Overuse Generally
|
||||
|
||||
Even outside the "not X but Y" pivot, models substitute em-dashes for commas,
|
||||
semicolons, parentheses, colons, and periods. The em-dash can replace any other
|
||||
punctuation mark, so models default to it.
|
||||
|
||||
### 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."
|
||||
|
||||
### 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) with strict grammatical parallelism.
|
||||
|
||||
### 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 and matching length.
|
||||
|
||||
### The Two-Clause Compound Sentence
|
||||
|
||||
An independent clause, a comma, a conjunction ("and," "but," "which,"
|
||||
"because"), and a second independent clause of similar length. Every sentence
|
||||
becomes two balanced halves.
|
||||
|
||||
> "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.
|
||||
|
||||
### Uniform Sentences Per Paragraph
|
||||
|
||||
Model-generated paragraphs contain between three and five sentences, a count
|
||||
that holds steady across a piece. If the first paragraph has four sentences,
|
||||
every subsequent paragraph will too.
|
||||
|
||||
### The Dramatic Fragment
|
||||
|
||||
Sentence fragments used as standalone paragraphs for emphasis.
|
||||
|
||||
> "Full stop." "Let that sink in."
|
||||
|
||||
### 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, containing
|
||||
zero information. The actual point is always in the next paragraph.
|
||||
|
||||
### The Parenthetical Qualifier
|
||||
|
||||
> "This is, of course, a simplification." "There are, to be fair, exceptions."
|
||||
|
||||
Parenthetical asides inserted to perform nuance without changing the argument.
|
||||
|
||||
### The Unnecessary Contrast
|
||||
|
||||
A contrasting clause appended to a statement that doesn't need one, using
|
||||
"whereas," "as opposed to," "unlike," or "except that."
|
||||
|
||||
> "Models write one register above where a human would, whereas human writers
|
||||
> tend to match register to context."
|
||||
|
||||
The contrasting clause restates what the first clause already said. If you
|
||||
delete the "whereas" clause and the sentence still says everything it needs to,
|
||||
the contrast was filler.
|
||||
|
||||
### Unnecessary Elaboration
|
||||
|
||||
Models keep going after the sentence has already made its point.
|
||||
|
||||
> "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."
|
||||
|
||||
This sentence could end at "paragraph." The words after it repeat what "per
|
||||
paragraph" already means. If you can cut the last third of a sentence without
|
||||
losing meaning, the last third shouldn't be there.
|
||||
|
||||
### 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.
|
||||
|
||||
---
|
||||
|
||||
## Word Choice
|
||||
|
||||
### Overused Intensifiers
|
||||
|
||||
"Crucial," "vital," "robust," "comprehensive," "fundamental," "arguably,"
|
||||
"straightforward," "noteworthy," "realm," "landscape," "leverage" (as a verb),
|
||||
"delve," "tapestry," "multifaceted," "nuanced" (applied to the model's own
|
||||
analysis), "pivotal," "unprecedented" (applied to things with plenty of
|
||||
precedent), "navigate," "foster," "underscores," "resonates," "embark,"
|
||||
"streamline," "spearhead."
|
||||
|
||||
### Elevated Register Drift
|
||||
|
||||
Models write one register above where a human would, replacing "use" with
|
||||
"utilize," "start" with "commence," "help" with "facilitate," "show" with
|
||||
"demonstrate," "try" with "endeavor," "change" with "transform," and "make" with
|
||||
"craft."
|
||||
|
||||
### Filler Adverbs
|
||||
|
||||
"Importantly," "essentially," "fundamentally," "ultimately," "inherently,"
|
||||
"particularly," "increasingly." Dropped in to signal that something matters when
|
||||
the writing itself should make the importance clear.
|
||||
|
||||
### The "Almost" Hedge
|
||||
|
||||
Instead of saying a pattern "always" or "never" does something, models write
|
||||
"almost always," "almost never," "almost certainly," "almost exclusively." A
|
||||
micro-hedge, less obvious than the full hedge stack.
|
||||
|
||||
### "In an era of..."
|
||||
|
||||
> "In an era of rapid technological change..."
|
||||
|
||||
Used to open an essay. The model is stalling while it figures out what the
|
||||
actual argument is.
|
||||
|
||||
---
|
||||
|
||||
## 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 Throat-Clearing Opener
|
||||
|
||||
> "In today's rapidly evolving digital landscape, the question of data privacy
|
||||
> has never been more important."
|
||||
|
||||
The first paragraph adds no information. Delete it and the piece improves.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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 Empathy Performance
|
||||
|
||||
> "This can be a deeply challenging experience." "Your feelings are valid."
|
||||
|
||||
Generic emotional language that could apply to anything.
|
||||
|
||||
---
|
||||
|
||||
## Structural Tells
|
||||
|
||||
### Symmetrical Section Length
|
||||
|
||||
If the first section runs about 150 words, every subsequent section will fall
|
||||
between 130 and 170.
|
||||
|
||||
### The Five-Paragraph Prison
|
||||
|
||||
Model essays follow a rigid introduction-body-conclusion arc even when nobody
|
||||
asked for one. The introduction previews the argument, the body presents 3 to 5
|
||||
points, the conclusion restates the thesis.
|
||||
|
||||
### Connector Addiction
|
||||
|
||||
The first word of each paragraph forms an unbroken chain of transition words:
|
||||
"However," "Furthermore," "Moreover," "Additionally," "That said," "To that
|
||||
end," "With that in mind," "Building on this."
|
||||
|
||||
### Absence of Mess
|
||||
|
||||
Model prose doesn't contradict itself mid-paragraph and then catch the
|
||||
contradiction, 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.
|
||||
|
||||
---
|
||||
|
||||
## Framing Tells
|
||||
|
||||
### "Broader Implications"
|
||||
|
||||
> "This has implications far beyond just the tech industry."
|
||||
|
||||
Zooming out to claim broader significance without substantiating it.
|
||||
|
||||
### "It's important to note that..."
|
||||
|
||||
This phrase and its variants ("it's worth noting," "it bears mentioning," "it
|
||||
should be noted") 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."
|
||||
|
||||
---
|
||||
|
||||
## Copyediting Checklist: Removing LLM Tells
|
||||
|
||||
Follow this checklist when editing any document to remove machine-generated
|
||||
patterns. 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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
7. Search for em-dashes and replace each one with the punctuation mark that
|
||||
would normally be used in that position (comma, semicolon, colon, period, or
|
||||
parentheses). If you can't identify which one it should be, the sentence
|
||||
needs to be restructured.
|
||||
|
||||
8. Remove redundant adjectives. For each adjective, ask whether the sentence
|
||||
changes meaning without it. "A single paragraph" means the same as "a
|
||||
paragraph." "An entire essay" means the same as "an essay." If the adjective
|
||||
doesn't change the meaning, cut it.
|
||||
|
||||
9. Remove unnecessary trailing clauses. Read the end of each sentence and ask
|
||||
whether the last clause restates what the sentence already said. If so, end
|
||||
the sentence earlier.
|
||||
|
||||
### Pass 2: Sentence-Level Restructuring
|
||||
|
||||
10. 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.
|
||||
|
||||
11. 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.
|
||||
|
||||
12. 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.
|
||||
|
||||
13. 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.
|
||||
|
||||
14. 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.
|
||||
|
||||
15. Check for the two-clause compound sentence pattern. If most sentences in a
|
||||
passage follow the "\[clause\], \[conjunction\] \[clause\]" structure, first
|
||||
try removing the conjunction and second clause entirely, since it's often
|
||||
redundant. If the second clause does carry meaning, break it into its own
|
||||
sentence, start the sentence with a subordinate clause, or embed a relative
|
||||
clause in the middle instead of appending it at the end.
|
||||
|
||||
16. Find every rhetorical question that is immediately followed by its own
|
||||
answer and rewrite the passage as a direct statement.
|
||||
|
||||
17. Find every sentence fragment being used as its own paragraph and either
|
||||
delete it or expand it into a complete sentence that adds information.
|
||||
|
||||
18. Check for unnecessary elaboration. Read every clause, phrase, and adjective
|
||||
in each sentence and ask whether the sentence loses meaning without it. If
|
||||
you can cut it and the sentence still says the same thing, cut it.
|
||||
|
||||
19. Check each pair of adjacent sentences to see if they can be merged into one
|
||||
sentence cleanly. If a sentence just continues the thought of the previous
|
||||
one, combine them using a participle, a relative clause, or by folding the
|
||||
second into the first. Don't merge if the result would create a two-clause
|
||||
compound.
|
||||
|
||||
20. Find every pivot paragraph ("But here's where it gets interesting." and
|
||||
similar) and delete it.
|
||||
|
||||
### Pass 3: Paragraph and Section-Level Review
|
||||
|
||||
21. Review the last sentence of each paragraph. If it restates the point the
|
||||
paragraph already made, delete it.
|
||||
|
||||
22. 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.
|
||||
|
||||
23. 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.
|
||||
|
||||
24. 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.
|
||||
|
||||
25. 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.
|
||||
|
||||
26. 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.
|
||||
|
||||
27. 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
|
||||
|
||||
28. 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.
|
||||
|
||||
29. 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.
|
||||
|
||||
30. Verify that you haven't introduced new patterns while fixing the original
|
||||
ones. 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.
|
||||
>
|
||||
> **human:** add em dashes generally as a tell, and remove them all from the
|
||||
> tells doc
|
||||
>
|
||||
> **model:** _(rewrites entire document without em-dashes while describing
|
||||
> em-dash overuse)_
|
||||
|
||||
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 are accurate.
|
||||
|
||||
This document has been through ten editing passes and it still has tells in it.
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Repository Policies
|
||||
last_modified: 2026-02-22
|
||||
last_modified: 2026-03-12
|
||||
---
|
||||
|
||||
This document covers repository structure, tooling, and workflow standards. Code
|
||||
@@ -59,6 +59,73 @@ style conventions are in separate documents:
|
||||
`make check`. For server repos, `make check` should run as an early build
|
||||
stage before the final image is assembled.
|
||||
|
||||
- **Dockerfiles must use a separate lint stage for fail-fast feedback.** Go
|
||||
repos use a multistage build where linting runs in an independent stage based
|
||||
on the `golangci/golangci-lint` image (pinned by hash). This stage runs
|
||||
`make fmt-check` and `make lint` before the full build begins. The build stage
|
||||
then declares an explicit dependency on the lint stage via
|
||||
`COPY --from=lint /src/go.sum /dev/null`, which forces BuildKit to complete
|
||||
linting before proceeding to compilation and tests. This ensures lint failures
|
||||
surface in seconds rather than minutes, without blocking on dependency
|
||||
download or compilation in the build stage.
|
||||
|
||||
The standard pattern for a Go repo Dockerfile is:
|
||||
|
||||
```dockerfile
|
||||
# Lint stage — fast feedback on formatting and lint issues
|
||||
# golangci/golangci-lint:v2.x.x, YYYY-MM-DD
|
||||
FROM golangci/golangci-lint@sha256:... AS lint
|
||||
WORKDIR /src
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
RUN make fmt-check
|
||||
RUN make lint
|
||||
|
||||
# Build stage
|
||||
# golang:1.x-alpine, YYYY-MM-DD
|
||||
FROM golang@sha256:... AS builder
|
||||
WORKDIR /src
|
||||
|
||||
# Force BuildKit to run the lint stage before proceeding
|
||||
COPY --from=lint /src/go.sum /dev/null
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
RUN make test
|
||||
|
||||
ARG VERSION=dev
|
||||
RUN CGO_ENABLED=0 go build -trimpath \
|
||||
-ldflags="-s -w -X main.Version=${VERSION}" \
|
||||
-o /app ./cmd/app/
|
||||
|
||||
# Runtime stage
|
||||
FROM alpine@sha256:...
|
||||
COPY --from=builder /app /usr/local/bin/app
|
||||
ENTRYPOINT ["app"]
|
||||
```
|
||||
|
||||
Key points:
|
||||
- The lint stage uses the `golangci/golangci-lint` image directly (it
|
||||
includes both Go and the linter), so there is no need to install the
|
||||
linter separately.
|
||||
- `COPY --from=lint /src/go.sum /dev/null` is a no-op file copy that creates
|
||||
a stage dependency. BuildKit runs stages in parallel by default; without
|
||||
this line, the build stage would not wait for lint to finish and a lint
|
||||
failure might not fail the overall build.
|
||||
- If the project uses `//go:embed` directives that reference build artifacts
|
||||
(e.g. a web frontend compiled in a separate stage), the lint stage must
|
||||
create placeholder files so the embed directives resolve. Example:
|
||||
`RUN mkdir -p web/dist && touch web/dist/index.html web/dist/style.css`.
|
||||
The lint stage should not depend on the actual build output — it exists to
|
||||
fail fast.
|
||||
- If the project requires CGO or system libraries for linting (e.g.
|
||||
`vips-dev`), install them in the lint stage with `apk add`.
|
||||
- The build stage runs `make test` after compilation setup. Tests run in the
|
||||
build stage, not the lint stage, because they may require compiled
|
||||
artifacts or heavier dependencies.
|
||||
|
||||
- Every repo should have a Gitea Actions workflow (`.gitea/workflows/`) that
|
||||
runs `docker build .` on push. Since the Dockerfile already runs `make check`,
|
||||
a successful build implies all checks pass.
|
||||
@@ -98,6 +165,13 @@ style conventions are in separate documents:
|
||||
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.gitignore` when setting up
|
||||
a new repo.
|
||||
|
||||
- **No build artifacts in version control.** Code-derived data (compiled
|
||||
bundles, minified output, generated assets) must never be committed to the
|
||||
repository if it can be avoided. The build process (e.g. Dockerfile, Makefile)
|
||||
should generate these at build time. Notable exception: Go protobuf generated
|
||||
files (`.pb.go`) ARE committed because repos need to work with `go get`, which
|
||||
downloads code but does not execute code generation.
|
||||
|
||||
- Never use `git add -A` or `git add .`. Always stage files explicitly by name.
|
||||
|
||||
- Never force-push to `main`.
|
||||
@@ -121,6 +195,66 @@ style conventions are in separate documents:
|
||||
- Dockerized web services listen on port 8080 by default, overridable with
|
||||
`PORT`.
|
||||
|
||||
- **HTTP/web services must be hardened for production internet exposure before
|
||||
tagging 1.0.** This means full compliance with security best practices
|
||||
including, without limitation, all of the following:
|
||||
- **Security headers** on every response:
|
||||
- `Strict-Transport-Security` (HSTS) with `max-age` of at least one year
|
||||
and `includeSubDomains`.
|
||||
- `Content-Security-Policy` (CSP) with a restrictive default policy
|
||||
(`default-src 'self'` as a baseline, tightened per-resource as
|
||||
needed). Never use `unsafe-inline` or `unsafe-eval` unless
|
||||
unavoidable, and document the reason.
|
||||
- `X-Frame-Options: DENY` (or `SAMEORIGIN` if framing is required).
|
||||
Prefer the `frame-ancestors` CSP directive as the primary control.
|
||||
- `X-Content-Type-Options: nosniff`.
|
||||
- `Referrer-Policy: strict-origin-when-cross-origin` (or stricter).
|
||||
- `Permissions-Policy` restricting access to browser features the
|
||||
application does not use (camera, microphone, geolocation, etc.).
|
||||
- **Request and response limits:**
|
||||
- Maximum request body size enforced on all endpoints (e.g. Go
|
||||
`http.MaxBytesReader`). Choose a sane default per-route; never accept
|
||||
unbounded input.
|
||||
- Maximum response body size where applicable (e.g. paginated APIs).
|
||||
- `ReadTimeout` and `ReadHeaderTimeout` on the `http.Server` to defend
|
||||
against slowloris attacks.
|
||||
- `WriteTimeout` on the `http.Server`.
|
||||
- `IdleTimeout` on the `http.Server`.
|
||||
- Per-handler execution time limits via `context.WithTimeout` or
|
||||
chi/stdlib `middleware.Timeout`.
|
||||
- **Authentication and session security:**
|
||||
- Rate limiting on password-based authentication endpoints. API keys are
|
||||
high-entropy and not susceptible to brute force, so they are exempt.
|
||||
- CSRF tokens on all state-mutating HTML forms. API endpoints
|
||||
authenticated via `Authorization` header (Bearer token, API key) are
|
||||
exempt because the browser does not attach these automatically.
|
||||
- Passwords stored using bcrypt, scrypt, or argon2 — never plain-text,
|
||||
MD5, or SHA.
|
||||
- Session cookies set with `HttpOnly`, `Secure`, and `SameSite=Lax` (or
|
||||
`Strict`) attributes.
|
||||
- **Reverse proxy awareness:**
|
||||
- True client IP detection when behind a reverse proxy
|
||||
(`X-Forwarded-For`, `X-Real-IP`). The application must accept
|
||||
forwarded headers only from a configured set of trusted proxy
|
||||
addresses — never trust `X-Forwarded-For` unconditionally.
|
||||
- **CORS:**
|
||||
- Authenticated endpoints must restrict `Access-Control-Allow-Origin` to
|
||||
an explicit allowlist of known origins. Wildcard (`*`) is acceptable
|
||||
only for public, unauthenticated read-only APIs.
|
||||
- **Error handling:**
|
||||
- Internal errors must never leak stack traces, SQL queries, file paths,
|
||||
or other implementation details to the client. Return generic error
|
||||
messages in production; detailed errors only when `DEBUG` is enabled.
|
||||
- **TLS:**
|
||||
- Services never terminate TLS directly. They are always deployed behind
|
||||
a TLS-terminating reverse proxy. The service itself listens on plain
|
||||
HTTP. However, HSTS headers and `Secure` cookie flags must still be
|
||||
set by the application so that the browser enforces HTTPS end-to-end.
|
||||
|
||||
This list is non-exhaustive. Apply defense-in-depth: if a standard security
|
||||
hardening measure exists for HTTP services and is not listed here, it is
|
||||
still expected. When in doubt, harden.
|
||||
|
||||
- `README.md` is the primary documentation. Required sections:
|
||||
- **Description**: First line must include the project name, purpose,
|
||||
category (web server, SPA, CLI tool, etc.), license, and author. Example:
|
||||
@@ -144,8 +278,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.
|
||||
|
||||
Reference in New Issue
Block a user