test-and-deploy · v8 · 2026-09-27 · sha256 a9e1fe49adb341c1

test-and-deploy v8A

Immutable. This exact content never changes and is served at /api/v1/blob/a9e1fe49adb341c1.

---
name: test-and-deploy
description: Make sure to use this skill whenever the user mentions running tests, executing npm tests, checking lint rules, linting, code formatting, git pushing, pushing to GitHub, or deploying commits to the remote repository. This skill ensures a secure pre-push pipeline by validating tests and linter output prior to any git push.
version: 8
updated: 2026-09-28
---

# NPM Test, Lint, and GitHub Deployment Pipeline

Follow this systematic procedure to safely validate code and push verified changes to the remote GitHub repository.

## 0. Prerequisites (One-time Setup — reference only; verify once per machine, not per run)
After cloning or running `npm install`, verify the pre-commit hook is active:
* Check `.husky/pre-commit` exists and contains `npx lint-staged`.
* The `husky` + `lint-staged` devDependencies are in `package.json`. If missing, run: `npm install -D husky lint-staged && npx husky init` and write `npx lint-staged` to `.husky/pre-commit`.
* The pre-commit hook automatically runs `eslint --fix` on staged `.ts`/`.tsx` files before every commit, preventing lint errors from reaching CI.

## 1. Environment and Script Verification
**Applicability guard first:** if the target repo has no `package.json`, this pipeline does not apply — state so and stop. (The parcel template itself ships none; its "product" is the `.devops/` machinery, validated by §2b instead.)
Then verify the target project's tools:
* Check `package.json` to ensure the `test` and `lint` scripts are defined under `"scripts"`.
* Verify that Git is initialized (`git status`) and a remote origin repository is configured (`git remote -v`).

## 2. Concurrent Verification (lint, tests, build)
### Green stamp check

Classify the delta **before** invoking anything. Derive the stamp with `git log --oneline -1 -- .devops/logs/agent-changelog.md`; **no locator ⇒ run the full suite** (no wrap-up yet = fail-safe over-run). With a locator, classify the **stamp-vs-worktree** delta — every path in `git diff --name-only <stamp>..HEAD` (committed) **plus** every path `git status --porcelain` reports (pending: staged, unstaged, untracked) — against the bookkeeping allowlist (version rows — `.devops/logs/version-history.md` — the changelog `.devops/logs/agent-changelog.md`, plan/sprint files incl. archive moves; backlog files accepted as over-run). On a `package.json` path from either read, run `git diff <stamp> -- <path>` — version line alone ⇒ it rides as a version row, any other delta ⇒ real-file (content decides, never the name). Both reads allowlist-only ⇒ record the skip (one line: stamp ref + `suite skipped — bookkeeping-only delta`, carried into the § 5 push summary) and invoke no tests/lint/build. Any other path — **including a worktree holding a real uncommitted edit** — record `suite invoked — real-file delta: <paths>` and fall through to the concurrent three below **exactly as written** (concurrency, run-once/one-shot rules, watch-shape detection, halts — all unchanged). Canon: `.devops/rules/plan-lifecycle.md` § Gate Invocation Hygiene (the `Green stamp` definition).

Run all three to ensure zero regressions, formatting errors, or build breaks before code leaves the developer environment. **Lint, tests, and build are independent — run them concurrently** (PowerShell 5.1: use `Start-Job`; never `&&`). This cuts wall-clock time from the sum of all three to the longest single one, and prevents token overrun by design:

1. Launch all three as background jobs. Redirect each stream's full output to a log file (e.g. `.agent-logs/lint.log`, `test.log`, `build.log`); return only a pass/fail flag per job.
2. Poll each job **once** at completion — never stream or repeatedly poll output. That polling discipline is what prevents overrun, not call count.
3. **Lint Verification** (`npm run lint`): if there are fixable lint errors, run `npm run lint -- --fix` (or the equivalent project command), then re-verify. If non-fixable errors persist, read `lint.log`, halt the execution, present the logs to the user, and prompt them to resolve the errors.
4. **Unit & Integration Tests — one-shot, non-interactive.** Invoke the suite in run-once mode: `npm test -- --run` or `npx vitest run` (Vitest), `CI=true npm test`, or the project's equivalent — never bare `npm test` alone, which on a watch-shaped script (`"test": "vitest"` opens watch mode and never returns) is a **detected failure of the script, not an operator mistake**: check `package.json`'s `test` script for a watch shape and use the run-once derivation. Partitioned or affected-only invocations are a Phase 5 declaration vocabulary, never a pre-push option — pre-push runs the full suite + build **on a stamp hit**, per the `Green stamp check` above (canonical: `.devops/rules/plan-lifecycle.md` § Gate Invocation Hygiene, whose stamp definition owns the trigger). On failure, read `test.log`, do NOT proceed. Halt execution, print the failure details, and prompt for bug remediation. A suite expected to outlast the executing tool's default timeout runs backgrounded (`Start-Job`, per § 2) with the result read once. Canonical rule: `.devops/rules/plan-lifecycle.md` § Gate Invocation Hygiene.
5. **Build Verification** (`npm run build`): on failure, read `build.log`, halt, and prompt the user to fix TypeScript or bundler errors.

### 2b. Machinery Integrity Gate (required before any push)
If the repo ships the parcel machinery (`.opencode/plans/base-context.md` or `.devops/agents/*.agent.md` exists), run both checks and halt on a non-zero exit:
1. `powershell -File scripts/check-parcel-prefix.ps1` — verifies the shared prefix is byte-identical across every parcel/ptp agent.
2. `powershell -File scripts/check-utf8-agents.ps1` — verifies agent files are UTF-8 clean.

Both are cheap (<1s each). Skipping them lets prefix drift or encoding corruption reach a pushed commit — AGENTS.md rule 9 forbids it.

**App-facing hardening sweep.** The same gate also covers the app surface, where one exists. Report each finding as **🟢 PASS** / **🟡 WARNING** / **🔴 CRITICAL FAIL** with **Evidence** (file + line) and an **Actionable Fix** (exact snippet, config rule or command), so a 🔴 is copy-pasteable:

1. **Workspace & dependency hygiene.** Repository-level agent instructions (`AGENTS.md`) present and current (no-op in this template: the machinery check above already covers it). `package.json` + lockfile: exact pinned versions, no unused prototype packages.
2. **Secrets & injection.** Scan source for hardcoded API keys, tokens or private URIs → extract to `.env` references. Schema validation (e.g. Zod) at every entry boundary including API routes and server actions. Sanitize dynamic HTML (`dangerouslySetInnerHTML`, `innerHTML`) with a verified sanitizer.
3. **Data access.** Row-level security active on every table (Supabase RLS / Firestore rules), scoped to authenticated owners. No queries inside loops (N+1) — batch or join them.
4. **Blast radius.** Top-level error boundaries or global exception middleware present. Feature-flag utility proposed for newly introduced modules, instant env-var rollback.
5. **Release infrastructure.** A CI workflow (`.github/workflows/`) running lint, type-check and tests on pull requests. Parameterized test suites protecting critical paths. Structured logging or error tracking initialised at the app entry point.

## 3. Version Increment Phase
Before staging and committing, you MUST check and bump the version in `package.json` following the project's **3-Level Versioning Strategy**:
1. **Read Current Version**: Retrieve the current `"version"` value from `package.json`.
2. **Determine/Select Increment Level**: Default to **Level 3 (Patch) silently** — do not prompt. Only ask the user which level should be bumped when they explicitly signal a feature or breaking release, or when the session clearly shipped a new user-facing feature:
   * **Level 1 (Major)**: User-directed primary versions (e.g., `1.02.003` -> `2.00.000`). Resets minor and patch levels to double/triple zero padding.
   * **Level 2 (Minor)**: New feature versions (e.g., `1.02.003` -> `1.03.003`). Increments the minor level by 1, while preserving the patch level as-is.
   * **Level 3 (Patch)**: Routine deployment versions (e.g., `1.02.003` -> `1.02.004`). Increments the patch level by 1, while preserving the minor level as-is. If the user does not request a Major or Minor bump, increment this automatically.
3. **Update Files**: 
   * Write the updated version string back to the `"version"` field in `package.json`.
   * Append a new row to the **Release Log** table in `.devops/logs/version-history.md` detailing the new version, current date, increment level, deployer name/model, and primary release highlights.
4. **Trace Version**: Ensure the new version (formatted e.g. `V1.2.3` or `1.2.3`) is prominently displayed in the commit message and noted in the agent changelog.

**Ordering note:** the § 2 → § 3 sequence stays exactly as written and is safe — the `Green stamp check`'s content test is order-independent because the allowlist absorbs version rows landing after § 2 (Q1's point; no reorder).

## 4. Git Staging & Local Commits
Only proceed to Git staging and committing after a 100% clean pass of linting, tests, and build, and after successfully incrementing the version:
1. Run `git status` to identify modified, deleted, or untracked files.
2. Stage appropriate changes using selective staging (`git add <file>`) or full directory staging (`git add .`) depending on the context of modifications. Ensure the modified `package.json` and `.devops/logs/version-history.md` are staged!
3. Formulate a highly informative, structured commit message summarizing the changes (following any workspace-specific logging rules, such as `AGENTS.md` guidelines or standard conventional commits) and include the updated version.
4. **Exception:** a `claim: <code>` or `plan: <code>` commit is a single-line ASCII literal — it skips the `-F` temp-file dance; `-F` stays mandatory for multi-line or quote-containing bodies. Otherwise, write the commit message to a temp file and run `git commit -F <message-file>`. **Never** pass a multi-line or quote-containing body through `-m "<message>"` — the host shell re-parses embedded double quotes as argument boundaries and shreds the message into pathspecs (`error: pathspec 'depth' did not match any file(s) known to git`). `-F` is quote-immune and preserves the multi-line body verbatim. Create that file with a file-writing tool, not an inline here-string: text embedded in the command string is precisely what gets re-parsed, and non-ASCII characters (em dashes, arrows) can be dropped in transport — a silently-dropped character collapses a pattern to empty and then matches every line.

## 5. GitHub Push
Push the local verified commits to the active branch on the remote repository. **You MUST ask the user for explicit permission before pushing.** Never push automatically.
1. Summarize what will be pushed: the branch name, number of commits, and a brief description of changes. When the `Green stamp check` ran in § 2, include its one-line outcome — `suite skipped — bookkeeping-only delta (stamp <sha>)` or `suite invoked — real-file delta: <paths>` — so the skip or the trigger is legible at the moment the push is authorized.
2. Ask the user: "Ready to push to GitHub?" and wait for their confirmation.
3. Only after the user confirms, retrieve the active branch name using `git branch --show-current`.
4. Push changes: `git push origin <branch-name>`.
5. Confirm the push command prints success, and report the successful deployment to the user. A successful push is **not** a green run: read the trunk's real CI result per `@agent-wrap-up` § Phase 7a item 4 — one home for that read, cited here, never restated — before calling the deployment done. A `CI red` verdict there is a hard stop.