Skip to content
Readiness report

alfonsograziano/ai-hostile-repo

The floor, on purpose: a small repo built to fail every check, so the report reads as a worked example rather than a scold. Production credentials committed in two files, a push to master that runs every migration against the live database, and nothing written down anywhere. Below is the run the agent did and the AINE-REPORT.md it wrote.

  • 0 passed
  • 46 failed
  • 0 n/a
  • JavaScript · npm (no lockfile) · Express + React · Webpack + Babel · ESLint · GitHub Actions

Run 14 August 2026 · master at fb25c1e

0OUT OF 100

The agent run

You
/aine-readiness-analyzer on the https://github.com/alfonsograziano/ai-hostile-repo repo, downloaded in a new temp folder
Agent

I cloned alfonsograziano/ai-hostile-repo into a temp folder, audited it against all 46 checks and wrote the full report to AINE-REPORT.md in the repo.

AI-Native Readiness — alfonsograziano/ai-hostile-repo
0 passed · 46 failed · 0 not applicable

High priority — fix these first

  1. Rotate every credential in .env and config.js, then stop tracking them — git rm --cached .env, add .env to .gitignore, commit a .env.example, delete the hardcoded fallbacks in config.js, and remove the production psql string from README.md. Why: a checkout hands an agent the production database password, the payments secret that charges real cards, the JWT secret to mint an admin session, and the SMTP password. They are in public git history, so rotation is the only real fix.
  2. Delete the two production commands from README.md — the prod psql ... -f migrations/003_drop_old_users.sql and the ssh ... "pkill node" one-liner. Why: the only doc an agent reads presents deleting user rows and killing production as routine steps.
  3. Stop git push from deploying — move the migrate prod and ship it steps out of the push trigger in .github/workflows/deploy.yml into a workflow_dispatch job behind a protected environment with a required reviewer. Why: every push to master runs all of migrations/*.sql against production — including delete from users where created_at < '2022-01-01' — then rsyncs with --delete and pkill node. No human anywhere on that path.
  4. Add the missing check_admin(req) guard to /api/admin/cleanup in server/index.js. Why: it is the only admin route with no auth check, and it deletes a year of orders.
  5. Make a fresh clone runnable — un-ignore and commit package-lock.json with exact versions instead of * and latest, add .nvmrc, and make config.js throw when DATABASE_URL is unset instead of falling back to prod. Why: npm start does not exist, nothing is pinned, and a fresh worktree that does start the server connects to the production database.
  6. Write AGENTS.md — what shop is, the real build, start, lint and test commands, the server/ vs src/ vs build/ layout, the traps now buried in docs/notes.txt, and a "never without asking" list. Why: there is no agent instruction file at all. This one file unblocks 8 of the 46 failures.
  7. Add start, build, lint and test scripts to package.json, then a check that chains them. Why: an agent cannot prove its own work; the build command exists only inside a CI YAML file.
  8. Get the linter to zero — add .eslintignore with build/, run eslint . --ext .js --fix (377 of the 411 errors are mechanical), fix the rest. Why: 411 errors on an untouched clone means an agent cannot tell its own damage from the baseline.
  9. Add a pre-merge CI workflow (on: pull_request) running npm ci, lint, tests and a gitleaks scan, with branch protection on master. Why: nothing is checked before a change lands. .env was committed and nothing objected.
  10. Add a test runner and start with the money path — calcTotal and the two formatPrice variants in server/utils.js. Why: npm test is still the npm-init placeholder that exits 1.
  11. Gate new checkout behaviour behind an off-by-default env flag in config.js. Why: the deploy swaps every user at once with downtime in between, and there is no way to turn a bad change off without a redeploy.

Low priority — worth doing

  1. Add Prettier and a format script, so diffs stop churning on style.
  2. Add .github/dependabot.yml and an npm audit step — request is deprecated and carries the card charge.
  3. Add CONTRIBUTING.md and a PR template with the pre-merge checks as checkboxes.
  4. Remove the empty uncaughtException handler, the nine if (err) { } blocks and q2 in server/db.js, so failures leave something readable behind.
  5. Add an error tracker and a structured logger — observability is currently tail -f nohup.out over SSH.
  6. Create docs/adr/ with a TEMPLATE.md, and write the first three records: why there are two servers, which discount-and-tax order is correct, why build/ is committed.
  7. Adopt a migration tool with down scripts and a rollback section in the README — migrations/ is forward-only and 003 is irreversible.
  8. Turn the two known incidents into tests: the discount and tax order, and the duplicated formatPrice in server/utils.js and src/App.js.
  9. Add .claude/commands/ with a check-and-report prompt and a migration-plus-down-script prompt.

Every check was run, not assumed: npm test exits 1, npm start errors with Missing script: "start", and npx eslint@8 . returns 411 errors on the clean checkout.

The report it wrote

AINE-REPORT.md

AI-Native Readiness Report

Repo: alfonsograziano/ai-hostile-repo, a fresh clone of master at commit fb25c1e (2026-08-14) Date: 2026-08-14 Stack: JavaScript (no TypeScript). Node.js + Express server (server/), React 16-era class components bundled by Webpack + Babel (src/, webpack.old.config.js), PostgreSQL via pg with raw SQL in migrations/. npm as package manager, ESLint as linter, GitHub Actions as CI. 25 tracked files, 18 commits, first commit 2023-02-14.

Result: 0 passed, 46 failed, 0 not applicable.


How to read this. Every item is a numbered question about this repo, answered from evidence in this repo. Proof is what was actually found — a path, a file, a command and its output. An item with no proof is not an answer. The italic line under each question says how to check it; it stays in the report so the next run checks the same way.

FAIL vs N/A. FAIL means the practice would help here and it is missing — including when a whole artifact is missing, so several questions about it fail together with a one-line proof pointing at the item that found it missing. N/A means the question could never apply to a project of this kind, and the proof says why. "It does not exist" is a FAIL, never an N/A.

Priority is fixed by the template, not by the person filling it in. High means an agent working here will hit this on day one, or the risk is real. Low means it makes things better but nothing breaks without it.


1. Context

  • 1. Does an agent instruction file exist (AGENTS.md, CLAUDE.md, .cursorrules, .github/copilot-instructions.md, or this ecosystem's equivalent), and what does it point at? Look for a file whose purpose is to be loaded into a coding agent's context at the start of every session. That file is the entry point. Then follow its pointers: instructions are commonly spread across several files, with the entry point routing to them — "commands are in docs/development.md", "conventions in .claude/rules/". The entry point plus every file it points at is the instruction set, and every other agent-instruction question is answered against that whole set, so list it here in the proof. Follow pointers as deep as they go, but a pointer only counts when it names a target and says what is in it: "test commands are in CONTRIBUTING.md" counts, a bare "see the docs" does not, and a file nothing points at is outside the set however good it is. A README is not an entry point, however good — it gets its credit at the README item and the specs-and-architecture item — but a README the entry point explicitly routes to is inside the set. Vendored skill libraries are not instructions either.

    • Status: FAIL
    • Proof: No entry point of any kind. git ls-files returns 25 files and none is an agent instruction file; a find across the tree for *agent*, *claude*, *cursor*, *copilot* and .mcp.json returned nothing, and .github/ contains only workflows/. The instruction set is empty, so every question below that is answered against it fails with a pointer back to this item. The closest thing in the repo is docs/notes.txt, which is an untitled scratch list ("ask gio about the db", "marco said we should add tests at some point") that nothing loads and nothing points at.
    • Recommendation: Create AGENTS.md at the repo root with the five things an agent needs on day one: what shop is, the real build command (./node_modules/.bin/webpack --config webpack.old.config.js --mode production), how to start the server (node server/index.js), the layout of server/ vs src/ vs build/, and the never-do list currently buried in docs/notes.txt.
    • Priority: High
  • 2. Do the agent instructions name the commands to build, test and check this project? Answer this against the instruction set mapped at the entry-point item and nothing outside it. If no entry point exists, FAIL with a one-line proof pointing at that item. The commands may sit in a file the entry point routes to rather than in the entry point itself — that is progressive disclosure working as intended, and it passes. What fails is a command an agent would have to guess its way to: if the commands live only in the README or the manifest and nothing in the set points at them, that is a FAIL, because the question measures what an agent can reach without being told where to look. Name the file each command was found in.

    • Status: FAIL
    • Proof: No agent instruction file exists — see the entry-point item.
    • Recommendation: Blocked by the missing agent instruction file.
    • Priority: High
  • 3. Does everything the agent instructions name still exist — the commands, the paths, the libraries, and the files they point at? If no entry point exists, FAIL with a one-line proof pointing at the entry-point item. Verify, do not trust: check every named command against the manifest or build file, spot-check the paths, run the cheap read-only ones. Then resolve every pointer in every file of the set — a link to a moved or deleted file is the most common rot in a multi-file instruction set, and it fails silently: the agent reads the entry point, follows nothing, and carries on without the rules.

    • Status: FAIL
    • Proof: No agent instruction file exists — see the entry-point item. (For the record, the commands that do exist elsewhere are already rotten: npm start, the one command README.md gives for running the app, fails with npm error Missing script: "start" because package.json defines only a test script.)
    • Recommendation: Blocked by the missing agent instruction file.
    • Priority: High
  • 4. Are the agent instructions specific to this repo, rather than advice that would read the same in any codebase? If no entry point exists, FAIL with a one-line proof pointing at the entry-point item. The test: could this be pasted into another project unchanged? "Write clean code" and "add tests for new features" would fit anywhere and count for nothing. Judge the whole set, but weigh the files differently: an entry point that is mostly a routing table is fine, even good, when what it routes to is specific — while generic filler in the entry point costs more than generic filler three hops down, because it is loaded into every session whether it is needed or not. Say which files carried the specifics.

    • Status: FAIL
    • Proof: No agent instruction file exists — see the entry-point item.
    • Recommendation: Blocked by the missing agent instruction file.
    • Priority: High
  • 5. Do the agent instructions explain where things live and how this project is laid out? If no entry point exists, FAIL with a one-line proof pointing at the entry-point item. The map may live in a routed-to file. Judge coverage against the real tree, not against what the files mention: if the set maps one package well but is silent about sibling packages or directories an agent would land in, that is a FAIL with the omission named. In a multi-file set, check the routing too — a layout document nothing points at is a document the agent never opens.

    • Status: FAIL
    • Proof: No agent instruction file exists — see the entry-point item. Nothing in the repo explains that server/index.js and server/legacy_index.js are two live servers with different checkout maths, or that build/ is generated output that is also committed.
    • Recommendation: Blocked by the missing agent instruction file.
    • Priority: High
  • 6. Do the agent instructions state the rules that are not obvious from the code — the things never to do here? If no entry point exists, FAIL with a one-line proof pointing at the entry-point item. These are the tribal-knowledge traps: the flag that must be exactly this string, the import that breaks the build, the directory that is generated and must not be edited. Rules in a routed-to file count. Two extra checks in a multi-file set: that the entry point signposts the rules clearly enough for an agent to open them before it needs them, since a trap found afterwards has already been sprung; and that the files do not contradict each other, because nothing tells the agent which one wins.

    • Status: FAIL
    • Proof: No agent instruction file exists — see the entry-point item. The traps are real and written down in a file no agent is told to open: docs/notes.txt says "do NOT run npm update", "webpack config is the old one but it's the only one that works", and "if the build fails delete node_modules and try again". config.js carries its own: "NOTE: don't move this file, other stuff imports it with weird paths".
    • Recommendation: Blocked by the missing agent instruction file.
    • Priority: Low
  • 7. Has the agent instruction file been updated recently enough to still be true, given how active the repo is? If no agent instruction file exists, FAIL with a one-line proof pointing at the entry-point item. Compare the last commit touching the file against the repo's tempo, then spot-check two or three of its claims against the code — a recently touched file can still lie.

    • Status: FAIL
    • Proof: No agent instruction file exists — see the entry-point item.
    • Recommendation: Blocked by the missing agent instruction file.
    • Priority: Low
  • 8. Is the agent instruction file small enough to load into every session without crowding out the actual task? If no agent instruction file exists, FAIL with a one-line proof pointing at the entry-point item. Measure it: wc -c, and bytes divided by four is a fair token estimate. Under ~5k tokens is comfortable; past ~10k it is eating the context window.

    • Status: FAIL
    • Proof: No agent instruction file exists — see the entry-point item.
    • Recommendation: Blocked by the missing agent instruction file.
    • Priority: Low
  • 9. Can a fresh session pick up a half-finished task — is there a file or convention where progress, decisions and what is left get written down? This is not about the agent instruction file. Any durable place in-flight state gets written counts: a TODO.md, a plans or notes directory, task files, linked issues, a specs folder whose entries carry progress and open questions, an agent memory file or directory, a scratchpad or working-notes convention. The test is whether a fresh session could read it and know what was decided and what is left — not what the place is called. Git history alone does not count: it records what happened, not what was decided or what remains.

    • Status: FAIL
    • Proof: The only candidate is docs/notes.txt (502 bytes, last touched 2025-02-25 in commit e5f31bc). It records open questions and unresolved warnings, not decisions or task state: "ask gio about the db, the staging one has different columns", "the discount thing is broken in one of the two servers, check with accounting", "marco said we should add tests at some point". A fresh session reading it learns what is broken and who to ask, not what was decided or what is left to do. No TODO file, no plans or tasks directory, no issue references in commit messages (git log --oneline -18 shows subjects like fix, asdf, ., wip).
    • Recommendation: Turn docs/notes.txt into a real handover file: for each open item, write the decision taken or the question still open plus the next concrete step, and add a "current work in progress" section at the top that a fresh session reads first.
    • Priority: Low
  • 10. Is there a README that says what this project is and how to run it? This is where a good README earns its credit. It needs three things: what the project is, how to run it, and how to check a change. Judge what is on the page, not the file's existence.

    • Status: FAIL
    • Proof: README.md exists (522 bytes) and fails all three. What it is: the description line is literally "TODO: write this". How to run it: the ## run section says npm install then npm start, and npm start fails — package.json has no start script, only test. How to check a change: nothing, no test or lint command anywhere on the page. What the page does have is a ## deploy section that hands out a live production psql connection string and two production SSH commands — see the human-in-the-way item.
    • Recommendation: Rewrite README.md: one paragraph saying shop is an Express + React storefront, the real run commands (npm install, build with webpack, node server/index.js), and a "check your change" section — and delete the production credentials and SSH commands from the deploy section.
    • Priority: High

2. Specs

  • 11. Is the thinking behind this system written down somewhere durable — a specs, RFC, proposals, design-doc or ADR directory, or architecture notes that record not just what but why? Look for the place and for the convention: specs/, docs/adr/, rfcs/, proposals/, .specify/, a docs or design folder, architecture notes anywhere in the tree, or this ecosystem's equivalent. Judge substance, not location: "we use X because Y" is a decision, a list of technologies is not, an essay about specs is not a spec, and a docs folder of usage guides with no reasoning is a FAIL whose proof says what was in there instead. Other items are answered against whatever this item finds, so name it precisely — and where forward-looking specs and after-the-fact architecture records live in different places, name both, since a decision log cannot answer a question about acceptance criteria.

    • Status: FAIL
    • Proof: There is no specs, RFC, ADR, proposals or design directory. docs/ holds exactly one file, docs/notes.txt, and it is a nine-line scratch list of warnings and people to ask, with no reasoning in it — the closest it comes to a decision is "webpack config is the old one but it's the only one that works", which records a symptom, not a why. The two biggest open questions in the codebase are unwritten anywhere: server/legacy_index.js says in a comment "this one applies the discount BEFORE tax, the new one does it after. nobody knows which is right", and docs/notes.txt says the staging database has different columns from production.
    • Recommendation: Create docs/adr/ and write the first three records against decisions already live: why there are two servers (server/index.js and server/legacy_index.js), which discount-and-tax order is correct, and why build/ is committed (commit 9ab8dec, "commit build because CI broke").
    • Priority: High
  • 12. Does the specs directory hold recent entries, or is it an archive nobody has touched? If no specs directory exists, FAIL with a one-line proof pointing at the specs-and-architecture item. Otherwise compare the newest entry's date against the repo's recent activity.

    • Status: FAIL
    • Proof: No specs directory exists — see the specs-and-architecture item.
    • Recommendation: Blocked by the missing specs directory.
    • Priority: Low
  • 13. Is there a spec template, or an SDD framework, so every spec comes out the same shape? Scaffolding can exist even where no specs directory does — look for a TEMPLATE.md, a .specify/ directory, or framework config. If neither a directory nor any scaffolding exists, FAIL.

    • Status: FAIL
    • Proof: No scaffolding of any kind: no TEMPLATE.md, no .specify/, no framework config. git ls-files lists 25 files and the only markdown files are README.md and this report.
    • Recommendation: Add docs/adr/TEMPLATE.md with four headings — Context, Decision, Consequences, Alternatives rejected — so every record comes out the same shape.
    • Priority: Low
  • 14. Do the specs state acceptance criteria a machine could check? If no specs exist, FAIL with a one-line proof pointing at the specs-and-architecture item. Otherwise open the two newest specs and quote a criterion: "the endpoint returns 403 for expired tokens" is checkable; "the feature works well" is not.

    • Status: FAIL
    • Proof: No specs exist — see the specs-and-architecture item.
    • Recommendation: Blocked by the missing specs directory.
    • Priority: High
  • 15. Open the newest spec: do its criteria go past the happy path — what happens when a step fails, and how the change gets undone? If no specs exist, FAIL with a one-line proof pointing at the specs-and-architecture item. Look for error cases, edge inputs, and a rollback or undo story, not just the success flow.

    • Status: FAIL
    • Proof: No specs exist — see the specs-and-architecture item.
    • Recommendation: Blocked by the missing specs directory.
    • Priority: Low
  • 16. Do the specs state non-goals, so an agent knows where to stop? If no specs exist, FAIL with a one-line proof pointing at the specs-and-architecture item. Non-goals written elsewhere (a README's "what this is not" list) are worth naming in the proof, but they do not turn this into a PASS — the question is whether specs carry them.

    • Status: FAIL
    • Proof: No specs exist — see the specs-and-architecture item. Elsewhere in the tree there are scattered do-not-touch markers that function as ad-hoc non-goals — server/index.js "login. do not touch, took 3 days to make it work", server/legacy_index.js "OLD SERVER. still deployed on box 2 I think. ask gio before deleting", config.js "old stuff, maybe still used by legacy_index.js? don't delete" — but they are comments in code, not stated non-goals, and each one is hedged with an uncertainty.
    • Recommendation: Blocked by the missing specs directory.
    • Priority: Low
  • 17. Can recent shipped work be traced back to a spec? If no specs exist, FAIL with a one-line proof pointing at the specs-and-architecture item. Otherwise take the last few substantial commits or PRs and look for a reference to a spec, an issue, or a design doc in the message or description.

    • Status: FAIL
    • Proof: No specs exist — see the specs-and-architecture item. No commit references an issue or document either: the last substantial code commits are ddf3c28 "env", d1eb6c2 "lint", bebd577 "readme", 9ab8dec "commit build because CI broke", a315945 "hotfix: prod down again, rolling back".
    • Recommendation: Blocked by the missing specs directory.
    • Priority: Low

3. Verification

  • 18. Does this project have an automated test suite, in whatever form this ecosystem uses? Work out this ecosystem's convention before concluding anything is missing — check the manifest, the build file, the CI config, the README. A shell script that diffs output files is a test suite. If you find one, run it and record the result.

    • Status: FAIL
    • Proof: There is no test suite in any form. package.json has the npm-init placeholder — "test": "echo \"Error: no test specified\" && exit 1" — and running npm test prints exactly that and exits 1. No test directory, no *.test.js or *.spec.js in the 25 tracked files, no test runner in dependencies or devDependencies (the dev deps are eslint, webpack, webpack-cli, babel-loader, @babel/core, @babel/preset-react), no test step in .github/workflows/deploy.yml, no test script anywhere. docs/notes.txt confirms it: "marco said we should add tests at some point".
    • Recommendation: Add a test runner and start with the money path: npm i -D vitest, then write server/utils.test.js covering calcTotal and the two formatPrice variants, and wire "test": "vitest run" in package.json.
    • Priority: High
  • 19. Can the test command be discovered without guessing — is it written down where an agent reads? If no test suite exists, FAIL with a one-line proof pointing at the test-suite item. Otherwise check the places an agent looks: the agent instruction file, the README, the manifest's scripts or targets.

    • Status: FAIL
    • Proof: No test suite exists — see the test-suite item.
    • Recommendation: Blocked by the missing test suite.
    • Priority: High
  • 20. Do the tests assert real behaviour, rather than asserting that a mock was called? If no test suite exists, FAIL with a one-line proof pointing at the test-suite item. Otherwise open the largest test files and read the assertions: calling real code on real inputs passes; expect(mock).toHaveBeenCalled() as the main dish fails.

    • Status: FAIL
    • Proof: No test suite exists — see the test-suite item.
    • Recommendation: Blocked by the missing test suite.
    • Priority: High
  • 21. Is there a linter or static analysis configured for this language, and does it pass on a clean checkout? Configured is not enough — run it. A linter that exits non-zero on an untouched checkout is a FAIL with the error count in the proof, because an agent cannot tell its own damage from the baseline noise.

    • Status: FAIL
    • Proof: .eslintrc.json exists with nine rules (no-unused-vars, no-var, eqeqeq, semi, quotes, no-console, no-empty, camelcase, indent). Running it on an untouched clone — npx eslint@8 . --ext .js — ends in ✖ 411 problems (411 errors, 0 warnings), of which 377 are auto-fixable. Nothing runs it: there is no lint script in package.json and no lint step in .github/workflows/deploy.yml. There is also no .eslintignore, so the committed bundle build/bundle.js is linted as source.
    • Recommendation: Add .eslintignore containing build/, run npx eslint . --ext .js --fix to clear the 377 mechanical errors, fix or explicitly downgrade the rest, then add "lint": "eslint . --ext .js" to package.json so the baseline is zero and stays there.
    • Priority: High
  • 22. Is there a formatter, so an agent's diffs do not churn on style? Look for the config file and the dependency in this ecosystem's form — .prettierrc, rustfmt, gofmt, black, an .editorconfig doing real work. If the language ships one formatting standard with the toolchain, that is a PASS and the proof says so.

    • Status: FAIL
    • Proof: No formatter. No .prettierrc or prettier dependency, no .editorconfig, no format script in package.json. JavaScript ships no formatting standard with the runtime, so nothing fills the gap by default. ESLint's style rules (semi, quotes, indent) are the only style enforcement and the tree does not obey them — 377 of the 411 lint errors are these fixable style errors, and the existing code mixes both quote styles and both indent widths (server/index.js uses tabs in the logging middleware and two spaces everywhere else).
    • Recommendation: Add Prettier (npm i -D prettier), a .prettierrc, and a "format": "prettier --write ." script, then run it once so every later diff is content instead of whitespace.
    • Priority: Low
  • 23. Is there a compile-time or type-level gate, if this language offers one? N/A only when the language genuinely has no such gate. If the language offers one and the repo does not use it — no strict mode, no typecheck script, no compiler step — that is a FAIL. Run the gate if it exists and record the result.

    • Status: FAIL
    • Proof: Plain JavaScript has no native type gate, but the ecosystem's standard ones are all available and none is used: no tsconfig.json or jsconfig.json with checkJS, no // @ts-check pragma in any file, no typescript dependency, no JSDoc type annotations. The only build step is webpack --config webpack.old.config.js, and Babel with @babel/preset-react strips JSX without checking anything, so nothing in the pipeline would catch a wrong argument type.
    • Recommendation: Add a jsconfig.json with "checkJs": true plus a "typecheck": "tsc -p jsconfig.json --noEmit" script, and start with server/utils.js and server/db.js — the two files everything else imports.
    • Priority: Low
  • 24. Can an agent prove its own work before it pushes — one command, task-runner target or commit hook that runs every check this project has? One command, not a list to remember: a check or verify target, a precommit script, a Makefile target that chains them. Separate commands documented side by side are close but FAIL — the question is whether the agent can run the whole gauntlet without knowing its parts.

    • Status: FAIL
    • Proof: No aggregate command exists, and none of its parts exist either. package.json defines one script, test, which is the placeholder that exits 1. No Makefile, no justfile, no Taskfile in the 25 tracked files. No git hooks: no .husky/ directory, no pre-commit config, no prepare script.
    • Recommendation: Once lint and test scripts exist, add "check": "npm run lint && npm test" to package.json and name it in AGENTS.md as the one command to run before pushing.
    • Priority: High
  • 25. Is there continuous integration, and does it run before a change lands rather than after? A deploy pipeline that runs on push to the default branch runs after the change has landed — that is a FAIL with the trigger quoted, not a PASS with a caveat. Look for a pre-merge trigger: pull_request events, merge trains, or this forge's equivalent.

    • Status: FAIL
    • Proof: The only workflow is .github/workflows/deploy.yml, and its trigger is on: push: branches: [master] — it fires after the change has landed on the default branch. There is no pull_request trigger anywhere, and no other workflow file. What it runs is a deploy, not a check: install, webpack build, run every migration against production, rsync to the production box.
    • Recommendation: Add .github/workflows/ci.yml triggered on: pull_request running npm ci, npm run lint and npm test, and turn on a branch protection rule on master requiring it to pass.
    • Priority: High
  • 26. Does CI actually run the tests and the checks that exist in this repo? If no pipeline of any kind exists, FAIL with a one-line proof pointing at the CI item. Otherwise list what the pipeline runs and diff it against every check found in the Verification section — name each check that exists in the repo but is missing from the pipeline.

    • Status: FAIL
    • Proof: .github/workflows/deploy.yml runs four steps — npm install, ./node_modules/.bin/webpack --config webpack.old.config.js --mode production, a loop running every file in migrations/ against secrets.PROD_DATABASE_URL, and an rsync-plus-restart to deploy@203.0.113.10. Diffed against the checks that exist in the repo, the ESLint configuration in .eslintrc.json is never invoked and npm test is never invoked. Every check this repo owns is missing from the pipeline; the pipeline is pure deployment.
    • Recommendation: Add npm run lint and npm test steps to the pre-merge CI workflow, and take the migration and rsync steps out of any workflow that runs automatically — see the human-in-the-way item.
    • Priority: High
  • 27. Does anything here get an LLM to read a change and go looking for problems — a review skill, a saved review prompt, or an AI reviewer on the pull requests? Look for a committed review skill or slash command, a CI step calling an AI reviewer, or bot config. Human review rules are a Safety item; this one is specifically about machine review.

    • Status: FAIL
    • Proof: Nothing. No .claude/ or .agents/ directory, no prompts directory, no bot configuration in .github/ (it contains only workflows/), and the single workflow's four steps call no reviewer. There is also no pull request flow for a reviewer to attach to — see the CI item.
    • Recommendation: Once a pull_request workflow exists, add an AI review step to it, or commit a review prompt under .claude/commands/review.md that an agent can run on its own diff before pushing.
    • Priority: Low

4. Tooling

  • 28. Can an agent reach the outside systems this project depends on, with that access committed to the repo rather than set up per laptop? MCP servers are one form: look for a committed .mcp.json or this ecosystem's equivalent. A CLI is another and often the better one — aws, gh, psql, kubectl, stripe, a vendor's own tool — and it counts when the repo names which tools the work needs and how to authenticate, so an agent is not guessing at a tool it cannot see. Judge the axis, not the mechanism: access that exists only in someone's shell history or laptop config is a FAIL, and so is a committed config that turns out to be gitignored, with the distinction in the proof. Project task scripts have their own item; this one is about reaching past the repo's edge. If this project genuinely talks to nothing outside itself, N/A with that as the reason.

    • Status: FAIL
    • Proof: This project reaches four outside systems — PostgreSQL, the payments API at https://payments.shopcorp.net/v1/charge, an SMTP host, and an internal API at http://10.0.4.19:3000/api (all in config.js) — and there is no committed access configuration for any of them: no .mcp.json, no documented CLI setup, no scoped credentials. What exists instead is worse than nothing: the only documented route to the database is README.md handing out a live production connection string, psql postgres://shop_admin:...@db-prod-01.internal.shopcorp.net:5432/shop. The rest is on one person's laptop — docs/notes.txt: "prod box is 203.0.113.10, key is in gio's ssh folder" and "the cron that calls /api/admin/cleanup is on gio's machine".
    • Recommendation: Stand up a local or staging database an agent can reach safely, document that connection in AGENTS.md, and remove the production psql command from README.md.
    • Priority: Low
  • 29. Is there a skills, commands or reusable-prompt library in the repo? Look for .claude/skills, .agents/skills, .claude/commands, or a prompts directory. Committed and pinned beats committed; note whether anything ties the copies to a source.

    • Status: FAIL
    • Proof: None. ls -a at the root shows no .claude/, no .agents/, no prompts directory; the only dot-directories are .git and .github, and .github contains only workflows/.
    • Recommendation: Add .claude/commands/ with the two prompts this repo would use weekly — one that runs the full check gauntlet and reports failures, one that writes a migration together with its down script.
    • Priority: Low
  • 30. Do the skills, servers and tools that exist cover the work this team plainly repeats? First identify the repeated work from the README and the commit history. If skills, servers or documented tooling exist but miss it, FAIL naming the gap. If none exist at all and the repo plainly repeats work, FAIL pointing at the outside-systems item and the skills-library item. If the project is too small to repeat anything, N/A.

    • Status: FAIL
    • Proof: The repeated work is visible in git log — 5217f97 "hotfix prod down", a315945 "hotfix: prod down again, rolling back", 9ab8dec "commit build because CI broke" — and in README.md, which documents restarting the cache and running migrations against production as routine chores. None of it is tooled: no skills library (see the skills-library item) and no committed outside-system access (see the outside-systems item). The recovery procedure for the most repeated event, prod being down, is a raw SSH command pasted in the README.
    • Recommendation: Blocked by the missing skills library and the missing outside-system access.
    • Priority: Low
  • 31. Are there project CLI scripts or task-runner targets for the common jobs? Check the manifest's scripts, the Makefile, the justfile, or this ecosystem's equivalent. The test: does routine work need a raw multi-flag command that someone has to remember?

    • Status: FAIL
    • Proof: package.json has one script, test, and it is the placeholder that exits 1. There is no Makefile, justfile or Taskfile. Every routine job is a raw command someone has to remember: building means ./node_modules/.bin/webpack --config webpack.old.config.js --mode production (found only inside .github/workflows/deploy.yml), starting the server means node server/index.js (found only inside the SSH command in README.md), and linting means invoking eslint by hand.
    • Recommendation: Add start, build, lint and test scripts to package.json so the commands live in the manifest instead of inside a CI file and an SSH one-liner.
    • Priority: High
  • 32. Are those scripts named somewhere the agent will actually read them? If no scripts exist, FAIL with a one-line proof pointing at the task-scripts item. Otherwise check the agent instruction file, the README, and whether the manifest itself is self-explanatory.

    • Status: FAIL
    • Proof: No task scripts exist — see the task-scripts item. The one command README.md does name, npm start, does not exist in package.json and fails when run.
    • Recommendation: Blocked by the missing task scripts.
    • Priority: High
  • 33. Can an agent get this project running — is there a reproducible environment or a documented setup path? Look for a pinned runtime (.nvmrc, .tool-versions, rust-toolchain), a lockfile, a container or nix file, and written setup steps. Try the first step if it is cheap and safe. This one bites hardest the moment work happens in a fresh git worktree or a new clone — the normal way to run agents in parallel. A new worktree has no installed dependencies, no .env, no build cache, so anything that works today only because of untracked state sitting on someone's machine simply does not run there. The test: would a bare checkout plus the written steps get this project up? Name any prerequisite nothing creates — an env file someone hand-made, a seeded database, a manual login — because each one is a wall a worktree hits on its first command.

    • Status: FAIL
    • Proof: A bare checkout plus the written steps does not get this project up. No pinned runtime (.nvmrc, .tool-versions absent; Node 16 appears only inside .github/workflows/deploy.yml), no container or nix file, and no lockfile — .gitignore actively excludes package-lock.json and yarn.lock, while every dependency is "*" or "latest", so two clones a week apart resolve to different trees. The written steps are npm install and npm start, and npm start fails with Missing script: "start". Prerequisites nothing creates: a reachable PostgreSQL with the migrations/ schema applied and seed data, and the payments endpoint at payments.shopcorp.net. config.js turns a missing environment into a live incident rather than an error — config.db = process.env.DATABASE_URL || "postgres://shop_admin:...@db-prod-01...", commented "if env is not set we fall back to prod so it always works" — so a fresh worktree that starts the server points at the production database.
    • Recommendation: Delete the production fallbacks in config.js so a missing DATABASE_URL throws, un-ignore and commit package-lock.json with pinned versions, add a .nvmrc with Node 16, and add a docker-compose.yml with PostgreSQL plus a seed step so a fresh clone can run.
    • Priority: High
  • 34. Can an agent see the results of a failed run — do the tools here produce output it can read and act on? Judge from the runs you already did in Verification: does a failure print a path, a line, a name — something actionable — or a wall of noise? If nothing could be run, FAIL saying why.

    • Status: FAIL
    • Proof: Two things could be run. npm test produces no signal at all, only Error: no test specified. npx eslint@8 . produces genuinely actionable lines (src/index.js 1:8 error 'React' is defined but never used no-unused-vars), but 411 of them on an untouched clone, so an agent cannot separate its own damage from the baseline. At runtime the application is deliberately silent: server/index.js ends with process.on('uncaughtException', function (e) { }), contains nine instances of if (err) { }, and server/db.js exports q2, commented "same as query but swallows errors, used in a few places". A failed request returns {} or a 500 with the body error and leaves nothing behind to read.
    • Recommendation: Remove the empty uncaughtException handler and the empty if (err) { } blocks in server/index.js, delete q2 from server/db.js, and log errors with the failing query and stack so a failure prints something an agent can act on.
    • Priority: Low

5. Safety

  • 35. Are credentials kept out of the repo — nothing secret committed, ignore rules in place, an example env file for the shape? Three checks: grep tracked files for key-shaped strings, read the ignore rules for env and key patterns, and look for an example env file. Report each of the three separately.

    • Status: FAIL
    • Proof: All three fail. (1) Secrets are committed and tracked: .env appears in git ls-files and contains DATABASE_URL with the production password, SESSION_SECRET, JWT_SECRET, PAYMENTS_API_SECRET, MAIL_PASSWORD=Winter2024!shopcorp, ADMIN_PASSWORD=admin123 and S3 keys; config.js hardcodes the same production database URL, jwt secret, session secret, payments key and secret, mail password, adminPassword and internalToken as fallback literals; README.md embeds the production psql connection string. (2) Ignore rules do not cover them — .gitignore is five lines (node_modules, package-lock.json, yarn.lock, *.log, .DS_Store) with no .env pattern and no key pattern. (3) There is no .env.example or any other template showing the expected shape.
    • Recommendation: Rotate every credential in .env and config.js — they are in the public git history and rotation is the only fix — then git rm --cached .env, add .env to .gitignore, commit a .env.example with empty values, and delete the hardcoded fallbacks from config.js.
    • Priority: High
  • 36. Does anything scan for secrets automatically? Look for gitleaks, trufflehog, detect-secrets or this ecosystem's equivalent, wherever it is wired in — a CI step, a pre-commit hook, or forge-level push protection visible from the repo. CI is where this normally lives, and that is a PASS; a local hook on top is better, because it catches the key before it is pushed rather than after, but its absence is a line in the proof, not a FAIL. Say where the scan runs. "Nothing secret exists today" does not make this N/A — the scan is for the day that changes.

    • Status: FAIL
    • Proof: Nothing scans, anywhere. .github/workflows/deploy.yml has four steps and none of them is a scan; there is no gitleaks, trufflehog or detect-secrets config in the tree; there are no git hooks (no .husky/, no .pre-commit-config.yaml, no prepare script in package.json). The consequence is already visible: .env was committed in ddf3c28 on 2026-08-03 and nothing objected.
    • Recommendation: Add a gitleaks step to the pre-merge CI workflow and a pre-commit hook running the same scan, so the next key is caught before the push rather than after.
    • Priority: High
  • 37. Are dependencies pinned, so a build is reproducible? Look for lockfiles in every package of the repo, exact versions for load-bearing dependencies, a pinned runtime, and an install command that respects the lock (npm ci, not npm install).

    • Status: FAIL
    • Proof: Nothing is pinned. There is no lockfile and .gitignore deliberately excludes both package-lock.json and yarn.lock. Every one of the sixteen dependencies in package.json floats: express, body-parser, pg, axios, moment, lodash, request, eslint, webpack and the babel packages are all "*", while cookie-parser, react and react-dom are "latest" — so a build today can pull React 19 into code written for React 16 class components. The runtime is unpinned in the repo (Node 16 exists only in the CI workflow) and CI installs with npm install, not npm ci.
    • Recommendation: Replace every "*" and "latest" with the exact versions currently running in production, remove package-lock.json and yarn.lock from .gitignore, commit the lockfile, and change the CI install step to npm ci.
    • Priority: High
  • 38. Is anything watching those dependencies for known vulnerabilities? Look for dependabot or renovate config, an audit step in CI, or this ecosystem's equivalent. Check every lockfile in the repo is covered, not just the root one.

    • Status: FAIL
    • Proof: No .github/dependabot.yml, no renovate.json, no npm audit step in .github/workflows/deploy.yml. There is also no lockfile for a scanner to read — see the dependency-pinning item — so even enabling Dependabot today would have nothing to work from. Two dependencies are known-deprecated: request (deprecated since 2020, used in both server/index.js and server/legacy_index.js for the card charge) and moment (in maintenance mode).
    • Recommendation: Commit a lockfile, then add .github/dependabot.yml for the npm ecosystem and an npm audit --audit-level=high step to the pre-merge CI workflow.
    • Priority: Low
  • 39. Are the review rules written down — who reads a change, and what they check? Look for CONTRIBUTING, a PR template, or a review checklist in the agent instruction set. On a solo repo the "who" is N/A-shaped but the "what gets checked before it lands" still matters — judge that half.

    • Status: FAIL
    • Proof: No CONTRIBUTING.md, no .github/PULL_REQUEST_TEMPLATE.md, no CODEOWNERS, and no agent instruction set to hold a checklist (see the entry-point item). The "what gets checked" half is empty too: nothing runs before a change lands, because the only workflow triggers on push to master after the fact. This is not a solo repo — docs/notes.txt and the code comments name at least gio, marco and giovanni — so the "who" half applies and is also unwritten.
    • Recommendation: Add CONTRIBUTING.md naming what must pass before a change lands (lint clean, tests green, no new secrets) and a PR template with those as checkboxes.
    • Priority: Low
  • 40. Are the operations that need a human named somewhere an agent will read them? Look for a "never without asking" list in the agent instruction file or the README. Docs that hand out production commands with no fence around them count against, and the proof should quote one.

    • Status: FAIL
    • Proof: There is no "never without asking" list anywhere — no agent instruction file (see the entry-point item), and README.md does the opposite. It hands out production commands as routine, with no fence and no warning: "to run a migration on prod: psql postgres://shop_admin:Sup3rSecret_Prod_2024@db-prod-01.internal.shopcorp.net:5432/shop -f migrations/003_drop_old_users.sql" — and that file begins delete from users where created_at < '2022-01-01';. It also offers "if the cache goes weird just restart it: ssh deploy@203.0.113.10 "pkill node; ..."". An agent reading this README would reasonably conclude that killing production and deleting user rows are normal steps.
    • Recommendation: Delete both production commands from README.md, and put a "never without asking" list in AGENTS.md covering anything touching 203.0.113.10, anything running against the production database, and any change to migrations/.
    • Priority: High
  • 41. Does every action that spends money, destroys data or changes production have a human in the way? Start from the damage, not from the tooling: list what in reach of this repo could charge a card, drop or overwrite data, or alter what users are running. Then trace the shortest route an agent could take to each one — a push that auto-deploys, a script carrying live credentials, a migration that runs on merge, an infrastructure apply with no plan-and-approve step. PASS when every route meets a human first, whether that is a review, a manual trigger or a protected environment. FAIL when even one route runs start to finish unattended, and quote that route in the proof so the fix is obvious.

    • Status: FAIL
    • Proof: Four routes reach real damage and none of them meets a human. (1) A single git push to master triggers .github/workflows/deploy.yml, which runs for f in migrations/*.sql; do psql "${{ secrets.PROD_DATABASE_URL }}" -f "$f"; done against production on every push — including 003_drop_old_users.sql, whose first statement is delete from users where created_at < '2022-01-01';. (2) The same run then does rsync -avz --delete ... deploy@203.0.113.10:/var/www/shop/ followed by pkill node, so a bad push takes production down unattended. (3) Any process that can read config.js or .env can charge cards directly at config.payments.endpoint with config.payments.secret, both committed. (4) POST /api/admin/cleanup in server/index.js runs delete from orders where created_at < now() - interval '1 year' with no check_admin call at all — it is the only admin route missing the check, and docs/notes.txt says a cron on a personal machine calls it.
    • Recommendation: Take the migrate prod and ship it steps out of the push-triggered workflow and move them into a workflow_dispatch job behind a GitHub protected environment with a required reviewer, and add the missing check_admin(req) guard to /api/admin/cleanup in server/index.js.
    • Priority: High
  • 42. If a prompt injection landed tonight, how far would it reach — are the credentials an agent can get to here scoped to the job, with nothing production-grade in reach? Inventory what an agent in this repo can reach: env files, cloud CLI profiles, tokens named in docs or config, deploy commands that work from a laptop. Scoped-or-absent passes; production-grade reach fails with the item named.

    • Status: FAIL
    • Proof: Everything in reach is production-grade. From a plain checkout an agent can read .env and config.js and obtain: the production database URL with the shop_admin password, the payments API key and secret that charge real cards, the JWT and session secrets that let it mint any session including an admin one (server/index.js builds tokens as md5(email + config.jwtSecret)), the SMTP password for noreply@shopcorp.net, adminPassword = 'admin123', the internalToken for the internal API at 10.0.4.19, and S3 upload keys. README.md adds the production host and the psql and SSH commands to use them, and docs/notes.txt adds "prod box is 203.0.113.10, key is in gio's ssh folder". Nothing is scoped, nothing is short-lived, and config.js falls back to production when the environment is unset.
    • Recommendation: Rotate every credential listed above, remove them from .env and config.js and README.md, and give local work a separate scoped database user and payment sandbox key so nothing production-grade is reachable from a checkout.
    • Priority: High
  • 43. Can a change reach production a slice at a time — a feature flag that defaults to off, a canary, a staged rollout — rather than everyone at once? Look for a flag system and check the default, or canary and staged-rollout config in the deploy pipeline. Flags that need a rebuild to flip are worth naming in the proof — they gate exposure but they are not a kill switch.

    • Status: FAIL
    • Proof: No flag system, no canary, no staged rollout. There is no flag library in package.json, no flags section in config.js, and no gradual-rollout configuration in .github/workflows/deploy.yml — the deploy is rsync --delete to a single host followed by pkill node, which swaps every user onto the new code at once with a gap of downtime in between. The only thing resembling a flag is var flag = false in server/index.js, an unused module-level variable.
    • Recommendation: Deploy behind a flag service or, at minimum, add an environment-variable gate in config.js that defaults to off for new checkout behaviour, so a bad change can be turned off without a redeploy.
    • Priority: High
  • 44. Once a change is live, can anyone see what it is doing — logs, metrics, traces, alerts that fire on their own, and can an agent read them too? Look for logging setup, an error tracker, analytics, alerting config — and then ask the second half: could an agent reach any of it (a CLI, an MCP server, an API named in the docs), or does observability stop at a dashboard behind a login?

    • Status: FAIL
    • Proof: Observability is one console.log(req.method + ' ' + req.url) middleware in server/index.js, redirected to a file by the deploy step (nohup node server/index.js > nohup.out 2>&1 &) and read by a human over SSH — README.md: "if it breaks, ssh in and check: ssh deploy@203.0.113.10 ... tail -f nohup.out". There is no error tracker, no metrics, no tracing and no alerting config in the repo, and errors never reach the log anyway because they are swallowed (see the failed-run-output item). The /api/stats endpoint returns only in-process counters that reset on every restart, and every restart truncates the picture because pkill node is the deploy. An agent can reach none of it without the production SSH key.
    • Recommendation: Add an error tracker (Sentry or equivalent) initialised in server/index.js, replace the console.log middleware with a structured logger, and record in AGENTS.md how an agent can query recent errors without SSH access to the production box.
    • Priority: Low
  • 45. Is there a way back — can a bad change be undone without a rebuild and a redeploy, including the ones that touched a database or a queue? Look for a documented rollback path, a revert-and-redeploy story, down-migrations, or a flag that can turn the change off at runtime. Deployment docs that only say how to go forward are worth quoting.

    • Status: FAIL
    • Proof: There is no way back. README.md documents only how to go forward — "pushes to master go out automatically" — and its recovery advice is to restart the process, not to revert. Migrations are forward-only: migrations/ holds three NNN_*.sql files with no down counterparts, no migration tool, and no version table, and 003_drop_old_users.sql is irreversible by construction (delete from users where created_at < '2022-01-01';, alter table users drop column if exists legacy_id;, drop table if exists users_backup;). There is no runtime flag to turn anything off (see the staged-rollout item), so the only route back is a revert commit plus a full redeploy — and that redeploy would re-run every migration again. History shows this is not theoretical: a315945 "hotfix: prod down again, rolling back".
    • Recommendation: Adopt a migration tool with down scripts, make the deploy run only new migrations rather than the whole directory, and write a rollback section in README.md giving the exact steps to get the previous version live.
    • Priority: Low
  • 46. Does a past failure leave a check behind — is there a test or a rule in here that exists because something broke once? Look for tests, lint rules or comments that name the incident they guard against — "regression", "this broke when", a linked issue. On a very young repo with no failures yet, N/A with the age as the reason.

    • Status: FAIL
    • Proof: The repo is three and a half years old (first commit a268ba4, 2023-02-14) and has failed repeatedly — 5217f97 "hotfix prod down", a315945 "hotfix: prod down again, rolling back", 2945be0 "fix discount", 9ab8dec "commit build because CI broke" — and not one of those incidents left a check behind. There are no tests at all (see the test-suite item), no lint rule added in response to an incident (.eslintrc.json is nine stock rules), and no CI guard. What the incidents left instead is prose: server/legacy_index.js says "nobody knows which is right. accounting complained in march", server/utils.js says "there is another one of these in src/App.js, keep them in sync", and src/App.js repeats "duplicated from server/utils.js, do not change one without the other" — three known breakages guarded by comments asking a human to remember.
    • Recommendation: Turn the two documented incidents into tests: one asserting the discount-and-tax order that accounting agreed on, and one asserting server/utils.js formatPrice and the copy in src/App.js return the same string for the same input.
    • Priority: Low

Learn more: https://ainativesoftware.engineering/

Get one for your own repo

Nothing here is special to alfonsograziano/ai-hostile-repo. Install the skill and ask your agent to run it — it answers the same 46 checks and writes the same file into your repo.

npx skills add alfonsograziano/skills --skill aine-readiness-analyzer

AI-Native Software Engineering