4 Commits

Author SHA1 Message Date
user
73099520ca remove unnecessary secondary clauses
All checks were successful
check / check (push) Successful in 11s
2026-03-04 14:56:44 -08:00
user
1c70757fb9 restore example quotes
All checks were successful
check / check (push) Successful in 5s
2026-03-04 14:52:23 -08:00
user
6438ed22d3 remove redundant adjectives
All checks were successful
check / check (push) Successful in 5s
2026-03-04 14:51:33 -08:00
user
d38c1295e9 expand checklist item 16
All checks were successful
check / check (push) Successful in 11s
2026-03-04 14:50:18 -08:00
3 changed files with 175 additions and 204 deletions

View File

@@ -1,6 +1,6 @@
--- ---
title: Existing Repo Checklist title: Existing Repo Checklist
last_modified: 2026-03-10 last_modified: 2026-02-22
--- ---
Use this checklist when beginning work in a repo that may not yet conform to our Use this checklist when beginning work in a repo that may not yet conform to our
@@ -78,22 +78,6 @@ with your task.
`internal/`, `static/`, etc.) `internal/`, `static/`, etc.)
- [ ] Go migrations in `internal/db/migrations/` and embedded in binary - [ ] 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 # Final
- [ ] `make check` passes - [ ] `make check` passes

View File

@@ -1,6 +1,7 @@
# LLM Prose Tells # LLM Prose Tells
A catalog of patterns found in LLM-generated prose. Human writers occasionally use every pattern in this document. The reason they
work as tells is that LLM output packs fifteen of them into a paragraph.
--- ---
@@ -13,11 +14,17 @@ 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 > "It's not just a tool—it's a paradigm shift." "This isn't about
> technology—it's about trust." > technology—it's about trust."
The most recognizable LLM construction. Models produce this at roughly 10 to 50x
the rate of human writers. Four of them in one essay and you know what you're
reading.
### Em-Dash Overuse Generally ### Em-Dash Overuse Generally
Even outside the "not X but Y" pivot, models substitute em-dashes for commas, Even outside the "not X but Y" pivot, models use em-dashes at far higher rates
semicolons, parentheses, colons, and periods. The em-dash can replace any other than human writers. They substitute em-dashes for commas, semicolons,
punctuation mark, so models default to it. parentheses, colons, and periods. A human writer might use one or two in a
piece. Models scatter them everywhere because the em-dash can stand in for any
other punctuation mark. More than two or three per page is a signal.
### The Colon Elaboration ### The Colon Elaboration
@@ -25,25 +32,33 @@ 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 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 ### The Triple Construction
> "It's fast, it's scalable, and it's open source." > "It's fast, it's scalable, and it's open source."
Three parallel items in a list, usually escalating. Always exactly three (rarely Three parallel items in a list, usually escalating. Always exactly three (rarely
two, never four) with strict grammatical parallelism. two, never four) with strict grammatical parallelism that human writers rarely
bother maintaining.
### The Staccato Burst ### The Staccato Burst
> "This matters. It always has. And it always will." "The data is clear. The > "This matters. It always has. And it always will." "The data is clear. The
> trend is undeniable. The conclusion is obvious." > trend is undeniable. The conclusion is obvious."
Runs of very short sentences at the same cadence and matching length. 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.
### The Two-Clause Compound Sentence ### The Two-Clause Compound Sentence
An independent clause, a comma, a conjunction ("and," "but," "which," Possibly the most pervasive tell, and easy to miss because each individual
"because"), and a second independent clause of similar length. Every sentence instance looks like normal English. The model produces sentence after sentence
becomes two balanced halves. where an independent clause is followed by 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 > "The construction itself is perfectly normal, which is why the frequency is
> what gives it away." "They contain zero information, and the actual point > what gives it away." "They contain zero information, and the actual point
@@ -53,44 +68,49 @@ becomes two balanced halves.
Human prose has sentences with one clause, sentences with three, sentences that Human prose has sentences with one clause, sentences with three, sentences that
start with a subordinate clause before reaching the main one, sentences that start with a subordinate clause before reaching the main one, sentences that
embed their complexity in the middle. embed their complexity in the middle. When every sentence on the page has that
same two-part structure, the rhythm becomes monotonous.
### Uniform Sentences Per Paragraph ### Uniform Sentences Per Paragraph
Model-generated paragraphs contain between three and five sentences, a count Model-generated paragraphs contain between three and five sentences. This count
that holds steady across a piece. If the first paragraph has four sentences, holds steady across a piece. If the first paragraph has four sentences, every
every subsequent paragraph will too. 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.
### The Dramatic Fragment ### The Dramatic Fragment
Sentence fragments used as standalone paragraphs for emphasis. Sentence fragments used as standalone paragraphs for emphasis, like "Full stop."
or "Let that sink in." on their own line. Using one in an essay is a reasonable
> "Full stop." "Let that sink in." stylistic choice, but models drop them in once per section or more.
### The Pivot Paragraph ### The Pivot Paragraph
> "But here's where it gets interesting." "Which raises an uncomfortable truth." > "But here's where it gets interesting." "Which raises an uncomfortable truth."
One-sentence paragraphs that exist only to transition between ideas, containing One-sentence paragraphs that exist only to transition between ideas. They
zero information. The actual point is always in the next paragraph. 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 ### The Parenthetical Qualifier
> "This is, of course, a simplification." "There are, to be fair, exceptions." > "This is, of course, a simplification." "There are, to be fair, exceptions."
Parenthetical asides inserted to perform nuance without changing the argument. Parenthetical asides inserted to look thoughtful. The qualifier never changes
the argument that follows it. Its purpose is to perform nuance.
### The Unnecessary Contrast ### The Unnecessary Contrast
A contrasting clause appended to a statement that doesn't need one, using Models append a contrasting clause to statements that don't need one, tacking on
"whereas," "as opposed to," "unlike," or "except that." "whereas," "as opposed to," "unlike," or "except that."
> "Models write one register above where a human would, whereas human writers > "Models write one register above where a human would, whereas human writers
> tend to match register to context." > tend to match register to context."
The contrasting clause restates what the first clause already said. If you The first clause already makes the point. The contrasting clause restates it
delete the "whereas" clause and the sentence still says everything it needs to, from the other direction. If you delete the "whereas" clause and the sentence
the contrast was filler. still says everything it needs to, the contrast was filler.
### Unnecessary Elaboration ### Unnecessary Elaboration
@@ -100,15 +120,19 @@ Models keep going after the sentence has already made its point.
> LLM output will use fifteen of them per paragraph, consistently, throughout > LLM output will use fifteen of them per paragraph, consistently, throughout
> the entire piece." > the entire piece."
This sentence could end at "paragraph." The words after it repeat what "per This sentence could end at "paragraph." The words after it just repeat what "per
paragraph" already means. If you can cut the last third of a sentence without paragraph" already means. Models do this because they're optimizing for clarity
losing meaning, the last third shouldn't be there. at the expense of concision. The result is prose that feels padded. If you can
cut the last third of a sentence without losing any meaning, the last third
shouldn't be there.
### The Question-Then-Answer ### The Question-Then-Answer
> "So what does this mean for the average user? It means everything." > "So what does this mean for the average user? It means everything."
A rhetorical question immediately followed by its own answer. A rhetorical question immediately followed by its own answer. Models do this two
or three times per piece because it fakes forward momentum. A human writer might
do it once.
--- ---
@@ -116,38 +140,43 @@ A rhetorical question immediately followed by its own answer.
### Overused Intensifiers ### Overused Intensifiers
"Crucial," "vital," "robust," "comprehensive," "fundamental," "arguably," 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), "straightforward," "noteworthy," "realm," "landscape," "leverage" (as a verb),
"delve," "tapestry," "multifaceted," "nuanced" (applied to the model's own "delve," "tapestry," "multifaceted," "nuanced" (which models apply to their own
analysis), "pivotal," "unprecedented" (applied to things with plenty of analysis with startling regularity), "pivotal," "unprecedented" (frequently
precedent), "navigate," "foster," "underscores," "resonates," "embark," applied to things with plenty of precedent), "navigate," "foster,"
"streamline," "spearhead." "underscores," "resonates," "embark," "streamline," and "spearhead." Three or
more on the same page is a strong signal.
### Elevated Register Drift ### Elevated Register Drift
Models write one register above where a human would, replacing "use" with Models write one register above where a human would. "Use" becomes "utilize."
"utilize," "start" with "commence," "help" with "facilitate," "show" with "Start" becomes "commence." "Help" becomes "facilitate." "Show" becomes
"demonstrate," "try" with "endeavor," "change" with "transform," and "make" with "demonstrate." "Try" becomes "endeavor." "Change" becomes "transform." "Make"
"craft." becomes "craft." The tendency holds regardless of topic or audience.
### Filler Adverbs ### Filler Adverbs
"Importantly," "essentially," "fundamentally," "ultimately," "inherently," "Importantly," "essentially," "fundamentally," "ultimately," "inherently,"
"particularly," "increasingly." Dropped in to signal that something matters when "particularly," "increasingly." Dropped in to signal that something matters,
the writing itself should make the importance clear. which is unnecessary when the writing itself makes the importance clear.
### The "Almost" Hedge ### The "Almost" Hedge
Instead of saying a pattern "always" or "never" does something, models write Models rarely commit to an unqualified statement. Instead of saying a pattern
"almost always," "almost never," "almost certainly," "almost exclusively." A "always" or "never" does something, they write "almost always," "almost never,"
micro-hedge, less obvious than the full hedge stack. "almost certainly," "almost exclusively." The word "almost" shows up at high
density in model-generated analytical prose. It's a micro-hedge, diagnostic in
volume.
### "In an era of..." ### "In an era of..."
> "In an era of rapid technological change..." > "In an era of rapid technological change..."
Used to open an essay. The model is stalling while it figures out what the A model habit as an essay opener. The model uses it to stall while it figures
actual argument is. out what the actual argument is. Human writers don't begin a piece by zooming
out to the civilizational scale.
--- ---
@@ -158,20 +187,23 @@ actual argument is.
> "While X has its drawbacks, it also offers significant benefits." > "While X has its drawbacks, it also offers significant benefits."
Every argument followed by a concession, every criticism softened. A direct Every argument followed by a concession, every criticism softened. A direct
artifact of RLHF training, which penalizes strong stances. artifact of RLHF training, which penalizes strong stances. Models reflexively
both-sides everything.
### The Throat-Clearing Opener ### The Throat-Clearing Opener
> "In today's rapidly evolving digital landscape, the question of data privacy > "In today's rapidly evolving digital landscape, the question of data privacy
> has never been more important." > has never been more important."
The first paragraph adds no information. Delete it and the piece improves. The first paragraph of most model-generated essays adds no information. Delete
it and the piece improves.
### The False Conclusion ### The False Conclusion
> "At the end of the day, what matters most is..." "Moving forward, we must..." > "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 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 ### The Sycophantic Frame
@@ -192,13 +224,15 @@ the key considerations:"
> cases it can potentially offer significant benefits." > cases it can potentially offer significant benefits."
Five hedges in one sentence ("worth noting," "while," "may not be," "in many Five hedges in one sentence ("worth noting," "while," "may not be," "in many
cases," "can potentially"), communicating nothing. cases," "can potentially"), communicating nothing. The model would rather be
vague than risk being wrong about anything.
### The Empathy Performance ### The Empathy Performance
> "This can be a deeply challenging experience." "Your feelings are valid." > "This can be a deeply challenging experience." "Your feelings are valid."
Generic emotional language that could apply to anything. Generic emotional language that could apply equally to a bad day at work or a
natural disaster.
--- ---
@@ -206,28 +240,33 @@ Generic emotional language that could apply to anything.
### Symmetrical Section Length ### Symmetrical Section Length
If the first section runs about 150 words, every subsequent section will fall If the first section of a model-generated essay runs about 150 words, every
between 130 and 170. subsequent section will fall between 130 and 170. Human writing is much more
uneven.
### The Five-Paragraph Prison ### The Five-Paragraph Prison
Model essays follow a rigid introduction-body-conclusion arc even when nobody 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 asked for one. The introduction previews the argument, the body presents 3 to 5
points, the conclusion restates the thesis. points, and then the conclusion restates the thesis.
### Connector Addiction ### Connector Addiction
The first word of each paragraph forms an unbroken chain of transition words: Look at the first word of each paragraph in model output. You'll find an
"However," "Furthermore," "Moreover," "Additionally," "That said," "To that unbroken chain of transition words: "However," "Furthermore," "Moreover,"
end," "With that in mind," "Building on this." "Additionally," "That said," "To that end," "With that in mind," "Building on
this." Human prose doesn't do this.
### Absence of Mess ### Absence of Mess
Model prose doesn't contradict itself mid-paragraph and then catch the 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 contradiction. It doesn't go on a tangent and have to walk it back, use an
without explaining it, make a joke that risks falling flat, leave a thought obscure idiom without explaining it, make a joke that risks falling flat, leave
genuinely unfinished, or keep a sentence the writer liked the sound of even a thought genuinely unfinished, or keep a sentence the writer liked the sound of
though it doesn't quite work. even though it doesn't quite work.
Human writing does all of those things regularly. That total absence of rough
patches and false starts is one of the strongest signals.
--- ---
@@ -237,27 +276,42 @@ though it doesn't quite work.
> "This has implications far beyond just the tech industry." > "This has implications far beyond just the tech industry."
Zooming out to claim broader significance without substantiating it. 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.
### "It's important to note that..." ### "It's important to note that..."
This phrase and its variants ("it's worth noting," "it bears mentioning," "it 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 should be noted") appear at absurd rates in model output. They function as
believes someone expects. verbal tics before a qualification the model believes someone expects.
### The Metaphor Crutch ### The Metaphor Crutch
Models rely on a small, predictable set of metaphors: "double-edged sword," "tip Models rely on a small, predictable set of metaphors ("double-edged sword," "tip
of the iceberg," "north star," "building blocks," "elephant in the room," of the iceberg," "north star," "building blocks," "elephant in the room,"
"perfect storm," "game-changer." "perfect storm," "game-changer") and reach for them with unusual regularity
across every topic.
---
## 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
10 to 20 of these patterns per page. Human writing might trigger 2 or 3,
distributed unevenly. When every paragraph on the page reads like it came from
the same careful, balanced, slightly formal, structurally predictable process,
it was generated by one.
--- ---
## Copyediting Checklist: Removing LLM Tells ## Copyediting Checklist: Removing LLM Tells
Follow this checklist when editing any document to remove machine-generated Follow this checklist when editing any document to remove machine-generated
patterns. Do at least two full passes, because fixing one pattern often patterns. Go through the entire list for every piece. Do at least two full
introduces another. passes, because fixing one pattern often introduces another.
### Pass 1: Word-Level Cleanup ### Pass 1: Word-Level Cleanup
@@ -271,7 +325,8 @@ introduces another.
2. Search for filler adverbs ("importantly," "essentially," "fundamentally," 2. Search for filler adverbs ("importantly," "essentially," "fundamentally,"
"ultimately," "inherently," "particularly," "increasingly") and delete every "ultimately," "inherently," "particularly," "increasingly") and delete every
instance where the sentence still makes sense without it. instance where the sentence still makes sense without it. That will be most
of them.
3. Look for elevated register drift ("utilize," "commence," "facilitate," 3. Look for elevated register drift ("utilize," "commence," "facilitate,"
"demonstrate," "endeavor," "transform," "craft" and similar) and replace with "demonstrate," "endeavor," "transform," "craft" and similar) and replace with
@@ -279,6 +334,7 @@ introduces another.
4. Search for "it's important to note," "it's worth noting," "it bears 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. 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," 5. Search for the stock metaphors ("double-edged sword," "tip of the iceberg,"
"north star," "building blocks," "elephant in the room," "perfect storm," "north star," "building blocks," "elephant in the room," "perfect storm,"
@@ -287,114 +343,105 @@ introduces another.
6. Search for "almost" used as a hedge ("almost always," "almost never," "almost 6. Search for "almost" used as a hedge ("almost always," "almost never," "almost
certainly," "almost exclusively") and decide in each case whether to commit certainly," "almost exclusively") and decide in each case whether to commit
to the unqualified claim or to drop the sentence entirely. to the unqualified claim or to drop the sentence entirely. If the claim needs
"almost" to be true, it might not be worth making.
7. Search for em-dashes and replace each one with the punctuation mark that 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 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 parentheses). If you can't identify which one it should be, the sentence
needs to be restructured. 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 ### Pass 2: Sentence-Level Restructuring
10. Find every em-dash pivot ("not Xbut Y," "not just XY," "more than X—Y") 8. Find every em-dash pivot ("not X...but Y," "not just X...Y," "more than
and rewrite it as two separate clauses or a single sentence that makes the X...Y") and rewrite it as two separate clauses or a single sentence that
point without the negation-then-correction structure. makes the point without the negation-then-correction structure.
11. Find every colon elaboration and check whether it's doing real work. If the 9. 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 clause before the colon could be deleted without losing meaning, rewrite the
sentence to start with the substance that comes after the colon. sentence to start with the substance that comes after the colon.
12. Find every triple construction (three parallel items in a row) and either 10. 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 reduce it to two, expand it to four or more, or break the parallelism so the
items don't share the same grammatical structure. items don't share the same grammatical structure.
13. Find every staccato burst (three or more short sentences in a row at similar 11. 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 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. their lengths so they don't land at the same cadence.
14. Find every unnecessary contrast ("whereas," "as opposed to," "unlike," "as 12. Find every unnecessary contrast ("whereas," "as opposed to," "unlike," "as
compared to," "except that") and check whether the contrasting clause adds compared to," "except that") and check whether the contrasting clause adds
information not already obvious from the main clause. If the sentence says information not already obvious from the main clause. If the sentence says
the same thing twice from two directions, delete the contrast. the same thing twice from two directions, delete the contrast.
15. Check for the two-clause compound sentence pattern. If most sentences in a 13. Check for the two-clause compound sentence pattern. If most sentences in a
passage follow the "\[clause\], \[conjunction\] \[clause\]" structure, first passage follow the "\[clause\], \[conjunction\] \[clause\]" structure,
try removing the conjunction and second clause entirely, since it's often rewrite some of them. Break a few into two sentences. Start some with a
redundant. If the second clause does carry meaning, break it into its own subordinate clause. Embed a relative clause in the middle of one instead of
sentence, start the sentence with a subordinate clause, or embed a relative appending it at the end. The goal is variety in sentence shape, not just
clause in the middle instead of appending it at the end. sentence length.
16. Find every rhetorical question that is immediately followed by its own 14. Find every rhetorical question that is immediately followed by its own
answer and rewrite the passage as a direct statement. answer and rewrite the passage as a direct statement.
17. Find every sentence fragment being used as its own paragraph and either 15. Find every sentence fragment being used as its own paragraph and either
delete it or expand it into a complete sentence that adds information. delete it or expand it into a complete sentence that adds actual
information.
18. Check for unnecessary elaboration. Read every clause, phrase, and adjective 16. Check for unnecessary elaboration. Read every clause, phrase, and adjective
in each sentence and ask whether the sentence loses meaning without it. If in each sentence and ask whether the sentence loses meaning without it. This
you can cut it and the sentence still says the same thing, cut it. includes trailing clauses that restate what the sentence already said,
redundant modifiers ("a single paragraph" when "a paragraph" works),
secondary clauses that add nothing ("which is why this matters"), and any
words whose removal doesn't change the meaning. 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 17. Find every pivot paragraph ("But here's where it gets interesting." and
sentence cleanly. If a sentence just continues the thought of the previous similar) and delete it. The paragraph after it always contains the actual
one, combine them using a participle, a relative clause, or by folding the point.
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 ### Pass 3: Paragraph and Section-Level Review
21. Review the last sentence of each paragraph. If it restates the point the 18. Check paragraph lengths across the piece and verify they actually vary. If
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 most paragraphs have between three and five sentences, rewrite some to be
one or two sentences and let others run to six or seven. one or two sentences and let others run to six or seven.
23. Check section lengths for suspicious uniformity. If every section is roughly 19. Check section lengths for suspicious uniformity. If every section is roughly
the same word count, combine some shorter ones or split a longer one the same word count, combine some shorter ones or split a longer one
unevenly. unevenly.
24. Check the first word of every paragraph for chains of connectors ("However," 20. Check the first word of every paragraph for chains of connectors ("However,"
"Furthermore," "Moreover," "Additionally," "That said"). If more than two "Furthermore," "Moreover," "Additionally," "That said"). If more than two
transition words start consecutive paragraphs, rewrite those openings to transition words start consecutive paragraphs, rewrite those openings to
start with their subject. start with their subject.
25. Check whether every argument is followed by a concession or qualifier. If 21. 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 the piece both-sides every point, pick a side on at least some of them and
cut the hedging. cut the hedging.
26. Read the first paragraph and ask whether deleting it would improve the 22. 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 piece. If it's scene-setting that previews the argument, delete it and start
with paragraph two. with paragraph two.
27. Read the last paragraph and check whether it restates the thesis or uses a 23. 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 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. delete it or rewrite it to say something the piece hasn't said yet.
### Pass 4: Overall Texture ### Pass 4: Overall Texture
28. Read the piece aloud and listen for passages that sound too smooth, too 24. 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 even, or too predictable. Human prose has rough patches. If there aren't
any, the piece still reads as machine output. any, the piece still reads as machine output.
29. Check that the piece contains at least a few constructions that feel 25. Check that the piece contains at least a few constructions that feel
idiosyncratic: a sentence with unusual word order, a parenthetical that goes 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 on a bit long, an aside only loosely connected to the main point, a word
choice that's specific and unexpected. choice that's specific and unexpected. If every sentence is clean and
correct and unremarkable, it will still read as generated.
30. Verify that you haven't introduced new patterns while fixing the original 26. 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. ones. This happens constantly. Run the entire checklist again from the top
on the revised version.
--- ---
@@ -441,9 +488,16 @@ roughly like this:
> >
> **model:** _(rewrites entire document without em-dashes while describing > **model:** _(rewrites entire document without em-dashes while describing
> em-dash overuse)_ > em-dash overuse)_
>
> **human:** now run the checklist methodically on each paragraph
>
> **model:** _(finds staccato burst in the section about triple constructions, a
> triple in the section about absence of mess, two-clause compounds everywhere,
> and "almost" hedges in its own prose about em-dash overuse)_
The human compared this process to the deleted scene in Terminator 2 where John 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 Connor switches the T-800's CPU to learning mode. The model compared it to a
physician trying to heal itself. Both are accurate. physician trying to heal itself. Both are accurate.
This document has been through ten editing passes and it still has tells in it. This document has been through eight editing passes and it still has tells in
it.

View File

@@ -1,6 +1,6 @@
--- ---
title: Repository Policies title: Repository Policies
last_modified: 2026-03-10 last_modified: 2026-02-22
--- ---
This document covers repository structure, tooling, and workflow standards. Code This document covers repository structure, tooling, and workflow standards. Code
@@ -98,13 +98,6 @@ style conventions are in separate documents:
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.gitignore` when setting up `https://git.eeqj.de/sneak/prompts/raw/branch/main/.gitignore` when setting up
a new repo. 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 use `git add -A` or `git add .`. Always stage files explicitly by name.
- Never force-push to `main`. - Never force-push to `main`.
@@ -128,66 +121,6 @@ style conventions are in separate documents:
- Dockerized web services listen on port 8080 by default, overridable with - Dockerized web services listen on port 8080 by default, overridable with
`PORT`. `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: - `README.md` is the primary documentation. Required sections:
- **Description**: First line must include the project name, purpose, - **Description**: First line must include the project name, purpose,
category (web server, SPA, CLI tool, etc.), license, and author. Example: category (web server, SPA, CLI tool, etc.), license, and author. Example: