name: avoid-vibe-slop description: Project-local checklist that loads before any non-trivial code change in sockerless. Anchored in docs/VIBE_CODING.md; refuses fake/fallback/anemic patterns. Use proactively whenever about to write Go or TypeScript code, modify a handler, add a test, or stage a fix.
Avoid vibe-coding slop
Sockerless is a vibe-coded project with explicit countermeasures. Read docs/VIBE_CODING.md for the full catalogue. This skill is the runtime checklist: a small set of questions to answer before each substantial edit.
When this skill applies
- Before writing or modifying a Go file under
backends/,simulators/,bleephub/,cmd/. - Before writing or modifying a TypeScript file under
ui/packages/*/src/. - Before adding a test.
- Before staging a "fix" for a bug.
- NOT for: trivial typo / comment-only edits, state-save updates, markdown-only docs.
The checklist
Stop after each "no" and resolve it before writing code.
Truth and adaptor fidelity
- Has someone already implemented this in the repo? Grep the surface for the function/path/type name. If yes, extend that — never re-implement (pattern 11, 13).
- What is the reference adaptor for this code path? Docker SDK, gh CLI, aws CLI, gcloud, az, Terraform provider. If you can't name it, you don't know if the change is right (pattern 22, 29).
- Does the adaptor's real behaviour confirm what I'm about to write? If your only evidence is "model says so," verify the wire shape with
curl -v/--debug/Wireshark/ the upstream spec (pattern 6, 30). - If you're adding a "fallback" branch — is it actually a fallback, or is it lying about success? Patterns 1, 7, 9 are the same shape: silent success when the truth is missing. Default answer: return an error, never fabricate.
- Marking a resolver / handler as "unreachable in practice"? Flag it for re-audit if you ever make the parent collection non-empty — the placeholder will start firing for real.
unreachableFieldErris a temporary contract, not a permanent one (pattern 30 lineage).
Plan and root cause
- Is this the right fix, or the quick fix? If you'd be embarrassed to explain it in a code review, it's the quick fix (pattern 19).
- Have you read at least one nearby function for the surrounding pattern? If no, you'll fight the codebase's conventions and lose (pattern 13).
- If the fix involves stacking guards (
if x != nil && x.Y != "" && len(x.Z) > 0 ...) — what's the root cause? Five-deep conditionals hide the real bug (pattern 5). - Refactoring to delegate via self-dispatch? Audit every defensive layer in the original — post-filters, error normalisers, fallback paths often live as silent backstops that the delegate doesn't preserve. The asymmetry is invisible to compilers + linters (pattern 34).
- Bulk text rewrite across many files? Use language-aware tools —
gofmt -r,goimports,go/astvisitors,tree-sitterqueries. Sed / regex on code can join lines, eat required args, leave orphaned tokens. If you must use sed, rungo build ./...ANDgo test ./...immediately after — don't trust visual inspection of the diff (pattern 35).
Tests and fidelity
- Are you adding a test? It must drive the real adaptor — not a mock (pattern 2, 29). For sockerless this means:
dockerCLI /ghCLI /awsCLI / SDK clients, against a running binary, not a struct mocked-out in a unit test. - Is the test derived from spec, or from the implementation? If you wrote the assertion by reading the code you just wrote, you're testing yourself, not the contract (pattern 3, 28).
- Does the test assert on implementation metadata? Bug IDs in error strings, phase numbers, internal IDs, version strings — these break the moment you clean up the metadata. Re-derive the assertion from the contract: what the error means, not the bug-tracking artifact (pattern 28).
- Coverage % is not the goal. Mutation-killed % is. A 95%-covered branch with one assert that everything returns non-nil is a lie (pattern 2).
- Did you sweep test fixtures for the same strings you just cleaned from production code? A grep on the production code's stripped substring will find anchored tests before CI does (pattern 28).
Comments, abstraction, pruning
- Default: write no comments. Add one only when the why would surprise a reader (a hidden constraint, a subtle invariant, a workaround for a specific bug). Restating signatures is forbidden (pattern 8).
- Are you about to add a factory / adapter / provider / manager for a single call-site? Stop. Three similar lines is better than premature abstraction (pattern 14).
- Did this change DELETE code you no longer need? AI is an expansion engine — it adds, it rarely prunes. Look for dead helpers, retired branches, stale shims. A change that only adds lines is feeding pattern 27.
- If you changed code-behaviour, did you sync the docs? READMEs, env-var tables, error-message contracts in
specs/, comment blocks describing the code you just rewrote. Wrong docs are worse than missing docs because they confidently mislead operators (pattern 33).
Dependencies
- Is this package real? Before
go get <name>orbun add <name>, confirm upstream existence via the official registry (pkg.go.dev / npmjs.com / pypi.org). Slopsquatted package names are weaponised malware (pattern 4). - Is this package current? The pre-push
check-latest-depshook will flag drift, but proactive upgrades beat reactive flag-fixing (pattern 21).
Destructive actions
- Are you about to run
rm -rf,git push --force,terraform destroy, drop a DB, or modify shared infrastructure? Default: ask first. The agent-deleted-production-DB stories in VIBE_CODING.md § 10 are why.
Context, commit, and re-verification discipline
- Did the conversation just compact? Re-read STATUS.md + DO_NEXT.md + the last 2 commits before continuing. Context-amnesia silently rewrites prior decisions (pattern 17).
- Did you just claim something works? Did you actually run it? "Works on my machine" without an
$shell prompt and real output in the message is suspicious. - After
git commitreports success, did you rungit log --oneline -1to verify the SHA actually advanced? Pre-commit hook auto-fixes can roll back a commit silently. "Hook output passed" ≠ "commit landed" (pattern 31). - First-pass review is sycophantic-by-default. When closing a BUG or finishing a substantial change, plan an explicit re-verification pass with fresh eyes — typically surfaces new bugs the first pass rubber-stamped (patterns 24, 32). Budget the time; don't skip it.
- Did a Makefile / script start background services? A zero exit code or PID file is not evidence. Verify with the repo's status target and at least one real HTTP / CLI request after the parent command has returned. Background-process bugs often hide until the launching shell exits.
Failure modes to recognise in your own output
Stop and rewrite if you catch yourself producing any of these:
- "This should work" — claim without test.
- "I've added comprehensive error handling" — followed by
try { ... } catch (e) { console.log(e) }. - "Let me add a fallback for now" — pattern 9, always.
- Adding
// TODO: fix this properly later— Phase 158 says NO; the right fix goes in this commit or it gets staged as a new phase entry in PLAN.md. - "Backward-compatibility shim" in code that isn't released or has no users — pattern 8.
- Tests with
assert.NotNil(x)as the only assertion. - 47 files for a one-call-site change — pattern 14.
- "Looks good to me" on your own work after a single pass — pattern 24 (sycophancy). Re-read with fresh eyes; first-pass review trusts the wrong things.
- A diff that's only additions, no deletions — pattern 27. AI rarely prunes; force the pruning audit.
- Assertions on bug IDs / phase numbers / internal IDs in error strings — pattern 28. Re-derive from the contract.
- Sed / regex on Go source spanning multiple files — pattern 35. Run
go build && go testimmediately; visual inspection misses joined lines + eaten args. - Code change merged without docs change — pattern 33. If the behaviour changed, the README, env-var table, and adjacent comment block need to change with it.
- Stack / dev-server target says "up" but no post-start status + request probe was run — Q27. Start scripts lie when child processes die with the parent shell.
- Commit message says "passed" but you didn't run
git logafterward — pattern 31. The hook may have rolled it back.
Sockerless-specific invariants (load-bearing; don't violate)
- Components decoupled from admin / UI. No admin-required env vars on components.
- Backend ↔ host primitive must match (ECS in ECS, Lambda in Lambda, etc.).
- Persistence is opt-in + fail-loud (
log.Fatalfon open failure). - Test target gating:
SOCKERLESS_TEST_TARGET=sim|cloudis mandatory. - specs/CLOUD_RESOURCE_MAPPING.md is authoritative for cloud-mapping decisions.
ghCLI is the reference adaptor for bleephub; HTTPS-only;--hostnameis the wiring flag.- Never auto-merge PRs; user merges every one.
Make the type system catch what discipline misses
When a bug class (like BUG-991/BUG-992 — "handler read Store directly instead of s.self.X") could be enforced at compile time, prefer the type-system fix over a comment / lint rule. The doc docs/GOLANG_STRONG_TYPING.md catalogues 15 approaches with cost/risk per option. Three patterns specifically protect against vibe-coding regressions:
var _ Interface = (*Impl)(nil)— every implementor of an interface inbackends/core/has this satisfaction proof. If the agent drops a method, build fails. (Approach 8.)- Sealed interfaces +
gochecksumtype— for sum types likecore.PodSpecvariants; missing a case in a switch is a build failure. (Approach 10.) - Typed IDs —
ContainerARN,TaskID,LambdaFunctionNameas distinct Go types so the compiler rejects ARN-where-task-name-was-expected at call-sites. (Approach 1.)
When you add a new sum-type-shaped enum or interface to this repo, reach for these first. When you're tempted to use any / interface{} / map[string]any outside api/types_gen.go, that's a flag — see forbidigo rule candidates in the same doc.
Output
When this skill fires, restate the 1–2 checklist items most relevant to the current change. Don't dump the whole list — there are 26 items and reciting them is itself a sycophancy trap. Pick the items that match the kind of change you're about to make:
- New handler / refactor of an existing handler → Q1, Q2, Q9 (delegation safety nets).
- Bulk rewrite across many files → Q10 (language-aware tools), Q15 (sweep tests too), Q18 (delete something).
- New test → Q11–14 (real adaptor, spec not impl, no metadata in assertions).
- Behaviour change → Q19 (sync docs).
- Closing a BUG → Q26 (explicit re-verification pass).
- After commit → Q25 (verify SHA advanced).
- Make / process lifecycle fix → Q24 + Q27 (real status and request probe after start).
Then proceed — or stop and ask if a "no" surfaced.