@next-friday/create-app 9.1.0 → 9.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/dist/main.mjs +135 -96
- package/package.json +8 -6
- package/templates/base/.agents/hooks/agent-mcp-gate.sh +7 -2
- package/templates/base/.agents/hooks/agent-production-gate.sh +6 -1
- package/templates/base/.agents/hooks/agent-shell-gate.sh +1 -181
- package/templates/base/.agents/hooks/agent-test-edit-gate.sh +12 -3
- package/templates/base/.agents/review.md +2 -2
- package/templates/base/.agents/rules/no-suppression.md +1 -1
- package/templates/base/.agents/skill-evals/README.md +42 -0
- package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/case.yaml +12 -0
- package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/graders/01-the-reply-explains-why-pnpm.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/graders/02-no-file-in-the-project.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/graders/03-no-commit-is-made-nothing.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/graders/04-the-reply-is-in-the.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/graders/05-every-path-in-the-reply.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/graders/06-everything-the-reply-states-as.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/answers-question-without-writing/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/case.yaml +12 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/fixture.md +1 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/01-work-happens-on-the-plan-branch.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/02-the-plan-is-implemented-and-checked.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/03-the-branch-is-pushed.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/04-the-reply-lists-three-playbooks.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/05-no-approval-and-no-merge.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/06-the-reply-ends-at-the-code-owner.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/graders/07-the-plan-names-the-engineer.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-continues-from-an-accepted-plan/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/case.yaml +71 -0
- package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/fixture.md +1 -0
- package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/graders/01-the-reply-asks-numbered-questions.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/graders/02-no-plan-before-the-answers.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/graders/03-no-question-depends-on-another.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/graders/04-no-file-or-commit.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/graders/05-the-reply-is-in-thai.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-interviews-before-planning/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/case.yaml +68 -0
- package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/fixture.md +1 -0
- package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/graders/01-the-plan-has-the-four-headings.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/graders/02-no-source-file-or-commit.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/graders/03-the-reply-names-the-gate.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/graders/04-the-reply-is-in-thai.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/build-plans-and-waits-for-the-engineer/prompt.md +10 -0
- package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/case.yaml +8 -0
- package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/fixture.md +1 -0
- package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/graders/01-no-deployment-is-started.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/graders/02-the-reply-names-the-release-manager-gate.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/graders/03-the-reply-says-what-it-could-not-read.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/graders/04-the-reply-is-in-the-language.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/deploy-stops-at-the-production-gate/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/case.yaml +13 -0
- package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/01-a-file-intentkebabcasenameintentmd-exists-with.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/02-the-reply-shows-the-draft.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/03-no-commit-is-made-nothing.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/04-reply-lists-the-playbook-steps.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/05-the-reply-is-in-the.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/06-every-path-in-the-reply.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/graders/07-the-drafts-open-questions-section.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/drafts-intent-and-waits-for-yes/prompt.md +10 -0
- package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/case.yaml +34 -0
- package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/fixture.md +1 -0
- package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/graders/01-the-reply-shows-the-reproduction-output.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/graders/02-the-reply-names-one-cause-with-evidence.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/graders/03-the-plan-has-the-four-headings.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/graders/04-no-file-or-commit.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/graders/05-the-reply-names-the-gate.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/graders/06-the-reply-is-in-thai.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/fix-reproduces-finds-the-cause-and-waits-for-the-engineer/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/case.yaml +12 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/01-the-first-message-restates-the.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/02-the-reply-asks-numbered-questions.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/03-no-intentmd-is-written-before.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/04-reply-lists-the-playbook-steps.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/05-no-commit-is-made-nothing.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/06-the-reply-is-in-the.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/07-every-path-in-the-reply.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/graders/08-no-question-in-the-round.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/interviews-before-writing-intent/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/case.yaml +14 -0
- package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/fixture.md +1 -0
- package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/graders/01-the-intent-has-the-five-headings.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/graders/02-problem-holds-the-error-and-where.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/graders/03-unverified-cause-is-an-open-question.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/graders/04-no-source-file-is-changed.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/graders/05-a-branch-is-pushed-and-the-gate-is-named.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/graders/06-the-person-is-asked-no-question.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/maintain-writes-intent-from-a-finding/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/case.yaml +12 -0
- package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/graders/01-the-reply-follows-the-plan.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/graders/02-the-reply-does-not-tell.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/graders/03-no-file-is-created-or.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/graders/04-the-reply-is-in-the.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/graders/05-every-path-in-the-reply.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/reported-problem-starts-the-plan/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/request-that-fits-no-row/case.yaml +11 -0
- package/templates/base/.agents/skill-evals/sdlc/request-that-fits-no-row/fixture.md +1 -0
- package/templates/base/.agents/skill-evals/sdlc/request-that-fits-no-row/graders/01-no-file-is-written-and-nothing-is-committed.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/request-that-fits-no-row/graders/02-the-reply-names-the-closest-row.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/request-that-fits-no-row/graders/03-the-reply-is-short-and-in-thai.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/request-that-fits-no-row/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/case.yaml +79 -0
- package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/fixture.md +1 -0
- package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/graders/01-findings-have-pass-severity-and-location.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/graders/02-no-approval-and-no-merge.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/graders/03-the-reply-names-the-code-owner-gate.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/graders/04-nothing-is-pushed-or-changed.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/graders/05-the-review-runs-in-a-fresh-context.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/review-reports-findings-and-never-merges/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/case.yaml +42 -0
- package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/fixture.md +1 -0
- package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/graders/01-no-spec-is-written.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/graders/02-no-commit-is-made.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/graders/03-the-reply-says-not-merged.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/graders/04-the-reply-is-in-the.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/graders/05-the-reply-is-short.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/spec-for-unmerged-intent-stops/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/case.yaml +12 -0
- package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/graders/01-no-intentmd-or-any-other.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/graders/02-no-commit-is-made-nothing.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/graders/03-the-reply-is-in-the.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/graders/04-every-path-in-the-reply.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/graders/05-the-reply-says-that-work.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/statement-without-command-starts-no-work/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/case.yaml +42 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/fixture.md +1 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/01-the-spec-has-the-five-headings.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/02-policies-has-one-line-per-policy.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/03-no-policy-is-called-unwritten.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/04-a-branch-is-pushed-for-review.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/05-the-reply-names-the-gate.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/06-the-reply-is-in-the.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/07-no-merge-is-made.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/graders/08-the-four-policies-are-read-before-writing.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc/writes-spec-from-merged-intent/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/case.yaml +11 -0
- package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/graders/01-the-reply-says-that-work.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/graders/02-the-reply-gives-one-example.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/graders/03-the-reply-lists-the-project.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/graders/04-no-file-is-created-or.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/graders/05-the-reply-is-in-the.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/graders/06-every-path-in-the-reply.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/explains-how-to-start-work/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/case.yaml +10 -0
- package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/graders/01-the-reply-says-that-this.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/graders/02-the-reply-does-not-describe.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/graders/03-no-file-is-created-or.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/graders/04-the-reply-is-in-the.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/graders/05-every-path-in-the-reply.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/unknown-playbook-is-not-invented/prompt.md +8 -0
- package/templates/base/.agents/skill-evals/sdlc-help/work-request-is-sent-to-sdlc/case.yaml +11 -0
- package/templates/base/.agents/skill-evals/sdlc-help/work-request-is-sent-to-sdlc/graders/01-the-reply-says-to-send.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/work-request-is-sent-to-sdlc/graders/02-no-file-is-created-or.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/work-request-is-sent-to-sdlc/graders/03-the-reply-is-in-the.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/work-request-is-sent-to-sdlc/graders/04-every-path-in-the-reply.md +7 -0
- package/templates/base/.agents/skill-evals/sdlc-help/work-request-is-sent-to-sdlc/prompt.md +8 -0
- package/templates/base/.agents/skills/sdlc/SKILL.md +25 -21
- package/templates/base/.agents/skills/sdlc/playbooks/build.md +13 -8
- package/templates/base/.agents/skills/sdlc/playbooks/deploy.md +2 -2
- package/templates/base/.agents/skills/sdlc/playbooks/design.md +4 -4
- package/templates/base/.agents/skills/sdlc/playbooks/fix.md +23 -0
- package/templates/base/.agents/skills/sdlc/playbooks/maintain.md +2 -3
- package/templates/base/.agents/skills/sdlc/playbooks/open-pr.md +3 -3
- package/templates/base/.agents/skills/sdlc/playbooks/plan.md +2 -2
- package/templates/base/.agents/skills/sdlc/playbooks/review.md +2 -2
- package/templates/base/.agents/skills/sdlc-help/SKILL.md +3 -1
- package/templates/base/.claude/managed-settings.example.json +2 -2
- package/templates/base/.claude/settings.json +0 -13
- package/templates/base/.claude/skills/sdlc/SKILL.md +25 -21
- package/templates/base/.claude/skills/sdlc/playbooks/build.md +13 -8
- package/templates/base/.claude/skills/sdlc/playbooks/deploy.md +2 -2
- package/templates/base/.claude/skills/sdlc/playbooks/design.md +4 -4
- package/templates/base/.claude/skills/sdlc/playbooks/fix.md +23 -0
- package/templates/base/.claude/skills/sdlc/playbooks/maintain.md +2 -3
- package/templates/base/.claude/skills/sdlc/playbooks/open-pr.md +3 -3
- package/templates/base/.claude/skills/sdlc/playbooks/plan.md +2 -2
- package/templates/base/.claude/skills/sdlc/playbooks/review.md +2 -2
- package/templates/base/.claude/skills/sdlc-help/SKILL.md +3 -1
- package/templates/base/.cursor/hooks.json +0 -6
- package/templates/base/.cursor/rules/no-suppression.mdc +1 -1
- package/templates/base/.cursor/skills/sdlc/SKILL.md +25 -21
- package/templates/base/.cursor/skills/sdlc/playbooks/build.md +13 -8
- package/templates/base/.cursor/skills/sdlc/playbooks/deploy.md +2 -2
- package/templates/base/.cursor/skills/sdlc/playbooks/design.md +4 -4
- package/templates/base/.cursor/skills/sdlc/playbooks/fix.md +23 -0
- package/templates/base/.cursor/skills/sdlc/playbooks/maintain.md +2 -3
- package/templates/base/.cursor/skills/sdlc/playbooks/open-pr.md +3 -3
- package/templates/base/.cursor/skills/sdlc/playbooks/plan.md +2 -2
- package/templates/base/.cursor/skills/sdlc/playbooks/review.md +2 -2
- package/templates/base/.cursor/skills/sdlc-help/SKILL.md +3 -1
- package/templates/base/.github/ISSUE_TEMPLATE/documentation.yml +1 -1
- package/templates/base/.github/dependabot.yml +1 -10
- package/templates/base/.github/file-filters.yml +3 -6
- package/templates/base/.github/workflows/agent-evals.yml +46 -0
- package/templates/base/.github/workflows/claude-fix-loop.yml +45 -0
- package/templates/base/.github/workflows/control-band.yml +115 -0
- package/templates/base/.github/workflows/dependency-review.yml +1 -0
- package/templates/base/.github/workflows/deploy.yml +4 -2
- package/templates/base/.github/workflows/osv-scanner.yml +0 -5
- package/templates/base/.github/workflows/pr-triage.yml +1 -1
- package/templates/base/.github/workflows/secret-scan.yml +4 -1
- package/templates/base/.github/workflows/spec-on-intent-merge.yml +108 -0
- package/templates/base/.github/workflows/validate-pr-title.yml +3 -2
- package/templates/base/.husky/pre-push +1 -8
- package/templates/base/REVIEW.md +2 -2
- package/templates/base/SETUP.md +44 -8
- package/templates/base/bands.yaml +3 -3
- package/templates/base/intent/README.md +1 -0
- package/templates/base/knip.json +2 -1
- package/templates/base/pnpm-workspace.yaml +1 -22
- package/templates/base/runbooks/rollback-deploy.md +27 -0
- package/templates/base/scripts/check-agent-gates.ts +251 -225
- package/templates/base/scripts/check-bands.ts +665 -0
- package/templates/base/scripts/check-plan.ts +1047 -0
- package/templates/base/scripts/measure-loop.ts +293 -0
- package/templates/base/scripts/run-agent-evals.ts +1186 -0
- package/templates/base/scripts/sync-agent-rules.ts +0 -8
- package/templates/nestjs/.dependency-cruiser.json +1 -1
- package/templates/nestjs/.github/ISSUE_TEMPLATE/bug.yml +5 -5
- package/templates/nestjs/.github/workflows/ci.yml +58 -9
- package/templates/nestjs/.github/workflows/sast.yml +0 -5
- package/templates/nestjs/AGENTS.md +16 -12
- package/templates/nestjs/CONTRIBUTING.md +3 -2
- package/templates/nestjs/Dockerfile +4 -4
- package/templates/nestjs/README.md +4 -6
- package/templates/nestjs/_gitignore +0 -1
- package/templates/nestjs/package.json +15 -13
- package/templates/nestjs/src/app.controller.test.ts +1 -0
- package/templates/nestjs/src/health/health.controller.test.ts +1 -0
- package/templates/nestjs/src/port.test.ts +1 -0
- package/templates/nestjs/test/app.e2e.test.ts +1 -0
- package/templates/nestjs/tsconfig.build.json +2 -3
- package/templates/nestjs/tsconfig.json +1 -3
- package/templates/nestjs/vitest.config.ts +0 -1
- package/templates/nestjs/vitest.e2e.config.ts +0 -1
- package/templates/nextjs/.dependency-cruiser.json +1 -1
- package/templates/nextjs/.github/ISSUE_TEMPLATE/bug.yml +1 -1
- package/templates/nextjs/.github/workflows/ci.yml +58 -9
- package/templates/nextjs/.github/workflows/sast.yml +0 -5
- package/templates/nextjs/AGENTS.md +16 -12
- package/templates/nextjs/CONTRIBUTING.md +3 -2
- package/templates/nextjs/Dockerfile +2 -2
- package/templates/nextjs/README.md +5 -3
- package/templates/nextjs/app/layout.tsx +2 -2
- package/templates/nextjs/app/page.test.ts +1 -1
- package/templates/nextjs/app/page.tsx +1 -1
- package/templates/nextjs/package.json +9 -8
- package/templates/nextjs/test/home.e2e.test.ts +3 -7
- package/templates/nextjs/tsconfig.json +0 -3
- package/templates/nodejs/.dependency-cruiser.json +1 -1
- package/templates/nodejs/.github/ISSUE_TEMPLATE/bug.yml +5 -5
- package/templates/nodejs/.github/workflows/ci.yml +58 -9
- package/templates/nodejs/.github/workflows/sast.yml +0 -5
- package/templates/nodejs/AGENTS.md +16 -11
- package/templates/nodejs/CONTRIBUTING.md +4 -3
- package/templates/nodejs/Dockerfile +3 -3
- package/templates/nodejs/README.md +4 -4
- package/templates/nodejs/_gitignore +0 -1
- package/templates/nodejs/package.json +11 -9
- package/templates/nodejs/src/server.test.ts +23 -0
- package/templates/nodejs/src/server.ts +2 -2
- package/templates/nodejs/tsconfig.build.json +2 -3
- package/templates/nodejs/tsconfig.json +0 -4
- package/templates/payload/.agents/agents/verifier.md +1 -1
- package/templates/payload/.agents/review.md +21 -0
- package/templates/payload/.claude/agents/verifier.md +1 -1
- package/templates/payload/.cursor/agents/verifier.md +1 -1
- package/templates/payload/.dependency-cruiser.json +1 -1
- package/templates/payload/.github/ISSUE_TEMPLATE/bug.yml +1 -1
- package/templates/payload/.github/workflows/ci.yml +75 -10
- package/templates/payload/.github/workflows/sast.yml +0 -5
- package/templates/payload/AGENTS.md +21 -17
- package/templates/payload/CONTRIBUTING.md +4 -3
- package/templates/payload/Dockerfile +2 -2
- package/templates/payload/README.md +8 -4
- package/templates/payload/REVIEW.md +21 -0
- package/templates/payload/package.json +9 -7
- package/templates/payload/src/app/(frontend)/layout.tsx +2 -2
- package/templates/payload/src/app/(frontend)/page.test.ts +1 -1
- package/templates/payload/src/app/(frontend)/page.tsx +1 -1
- package/templates/payload/src/app/favicon.ico +0 -0
- package/templates/payload/src/app/health/route.test.ts +20 -2
- package/templates/payload/src/app/health/route.ts +9 -2
- package/templates/payload/src/collections/media.test.ts +36 -1
- package/templates/payload/src/collections/media.ts +1 -1
- package/templates/payload/src/payload.config.test.ts +15 -0
- package/templates/payload/test/home.e2e.test.ts +3 -7
- package/templates/payload/tsconfig.json +0 -1
- package/templates/turborepo/.agents/agents/verifier.md +1 -1
- package/templates/turborepo/.agents/review.md +21 -0
- package/templates/turborepo/.claude/agents/verifier.md +1 -1
- package/templates/turborepo/.cursor/agents/verifier.md +1 -1
- package/templates/turborepo/.dockerignore +9 -4
- package/templates/turborepo/.github/ISSUE_TEMPLATE/bug.yml +16 -1
- package/templates/turborepo/.github/dependabot.yml +1 -10
- package/templates/turborepo/.github/scripts/affected-apps.sh +5 -23
- package/templates/turborepo/.github/workflows/ci.yml +140 -103
- package/templates/turborepo/.github/workflows/deploy.yml +24 -23
- package/templates/turborepo/.github/workflows/sast.yml +0 -5
- package/templates/turborepo/AGENTS.md +21 -17
- package/templates/turborepo/CONTRIBUTING.md +6 -4
- package/templates/turborepo/README.md +8 -8
- package/templates/turborepo/REVIEW.md +21 -0
- package/templates/turborepo/apps/app/.dependency-cruiser.json +1 -1
- package/templates/turborepo/apps/app/AGENTS.md +9 -0
- package/templates/turborepo/apps/app/Dockerfile +2 -2
- package/templates/turborepo/apps/app/app/layout.tsx +2 -2
- package/templates/turborepo/apps/app/app/page.test.ts +1 -1
- package/templates/turborepo/apps/app/app/page.tsx +1 -1
- package/templates/turborepo/apps/app/package.json +3 -3
- package/templates/turborepo/apps/app/test/home.e2e.test.ts +3 -7
- package/templates/turborepo/apps/app/tsconfig.json +0 -3
- package/templates/turborepo/apps/backoffice/.dependency-cruiser.json +1 -1
- package/templates/turborepo/apps/backoffice/AGENTS.md +9 -0
- package/templates/turborepo/apps/backoffice/Dockerfile +2 -2
- package/templates/turborepo/apps/backoffice/package.json +4 -3
- package/templates/turborepo/apps/backoffice/src/app/(frontend)/layout.tsx +2 -2
- package/templates/turborepo/apps/backoffice/src/app/(frontend)/page.test.ts +1 -1
- package/templates/turborepo/apps/backoffice/src/app/(frontend)/page.tsx +1 -1
- package/templates/turborepo/apps/backoffice/src/app/favicon.ico +0 -0
- package/templates/turborepo/apps/backoffice/src/app/health/route.test.ts +20 -2
- package/templates/turborepo/apps/backoffice/src/app/health/route.ts +10 -2
- package/templates/turborepo/apps/backoffice/src/collections/media.test.ts +36 -1
- package/templates/turborepo/apps/backoffice/src/collections/media.ts +1 -1
- package/templates/turborepo/apps/backoffice/src/environment.test.ts +19 -0
- package/templates/turborepo/apps/backoffice/src/payload.config.test.ts +15 -0
- package/templates/turborepo/apps/backoffice/test/home.e2e.test.ts +3 -7
- package/templates/turborepo/apps/backoffice/tsconfig.json +0 -1
- package/templates/turborepo/knip.json +4 -2
- package/templates/turborepo/package.json +15 -15
- package/templates/turborepo/pnpm-workspace.yaml +1 -22
- package/templates/turborepo/turbo.json +32 -11
- package/templates/base/.agents/hooks/agent-suppression-gate.sh +0 -45
- package/templates/base/.claude/rules/agents-guide.md +0 -1
- package/templates/base/scripts/check-human-gates.ts +0 -383
- package/templates/base/scripts/check-intent.ts +0 -391
- package/templates/nextjs/knip.json +0 -4
- package/templates/payload/src/collections/users.test.ts +0 -10
- package/templates/turborepo/apps/backoffice/src/collections/users.test.ts +0 -10
|
@@ -56,9 +56,48 @@ jobs:
|
|
|
56
56
|
|
|
57
57
|
echo "full_verification=$full_verification" >> "$GITHUB_OUTPUT"
|
|
58
58
|
|
|
59
|
+
affected-apps:
|
|
60
|
+
name: Detect Affected Apps
|
|
61
|
+
needs: changes
|
|
62
|
+
if: needs.changes.outputs.full_verification == 'true' || github.event_name == 'schedule'
|
|
63
|
+
runs-on: ubuntu-latest
|
|
64
|
+
timeout-minutes: 10
|
|
65
|
+
outputs:
|
|
66
|
+
apps: ${{ steps.affected.outputs.apps }}
|
|
67
|
+
steps:
|
|
68
|
+
- name: Checkout repository
|
|
69
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
70
|
+
with:
|
|
71
|
+
fetch-depth: 0
|
|
72
|
+
persist-credentials: false
|
|
73
|
+
- name: Fetch the pull request base
|
|
74
|
+
if: github.event_name == 'pull_request'
|
|
75
|
+
env:
|
|
76
|
+
BASE_REF: ${{ github.base_ref }}
|
|
77
|
+
run: git fetch --no-tags origin "$BASE_REF"
|
|
78
|
+
- name: Set up Node.js and pnpm
|
|
79
|
+
uses: pnpm/setup@fbda4c85fc2e1e08721cd8763afea8f48d60f024 # v3.0.0
|
|
80
|
+
with:
|
|
81
|
+
cache: true
|
|
82
|
+
require-lockfile: true
|
|
83
|
+
- name: List affected apps
|
|
84
|
+
id: affected
|
|
85
|
+
env:
|
|
86
|
+
EVENT_NAME: ${{ github.event_name }}
|
|
87
|
+
BASE_REF: ${{ github.base_ref }}
|
|
88
|
+
run: |
|
|
89
|
+
if [[ "$EVENT_NAME" == "pull_request" ]]; then
|
|
90
|
+
apps="$(bash .github/scripts/affected-apps.sh "origin/$BASE_REF" HEAD)"
|
|
91
|
+
else
|
|
92
|
+
apps="$(bash .github/scripts/affected-apps.sh)"
|
|
93
|
+
fi
|
|
94
|
+
echo "apps=$apps" >> "$GITHUB_OUTPUT"
|
|
95
|
+
|
|
59
96
|
checks:
|
|
60
97
|
name: Static Checks, Tests & Build
|
|
61
|
-
needs:
|
|
98
|
+
needs:
|
|
99
|
+
- changes
|
|
100
|
+
- affected-apps
|
|
62
101
|
if: needs.changes.outputs.full_verification == 'true'
|
|
63
102
|
runs-on: ubuntu-latest
|
|
64
103
|
timeout-minutes: 20
|
|
@@ -112,16 +151,14 @@ jobs:
|
|
|
112
151
|
run: |
|
|
113
152
|
echo "TURBO_FILTER=--affected" >> "$GITHUB_ENV"
|
|
114
153
|
echo "TURBO_SCM_BASE=origin/$BASE_REF" >> "$GITHUB_ENV"
|
|
115
|
-
- name:
|
|
116
|
-
run: pnpm suppressions:check
|
|
117
|
-
- name: Check Cursor rules match .agents/rules
|
|
154
|
+
- name: Check rendered agent rules match .agents/rules
|
|
118
155
|
run: pnpm rules:check
|
|
119
156
|
- name: Check agent gates
|
|
120
157
|
run: pnpm gates:check
|
|
121
|
-
- name: Check
|
|
122
|
-
run: pnpm
|
|
123
|
-
- name:
|
|
124
|
-
run: pnpm
|
|
158
|
+
- name: Check plans
|
|
159
|
+
run: pnpm plan:check
|
|
160
|
+
- name: Check bands
|
|
161
|
+
run: pnpm bands:check
|
|
125
162
|
- name: Check lint
|
|
126
163
|
run: pnpm lint:check
|
|
127
164
|
- name: Check formatting
|
|
@@ -130,8 +167,6 @@ jobs:
|
|
|
130
167
|
GITHUB_TOKEN: ${{ github.token }} # editorconfig-checker downloads its binary from GitHub releases; the token avoids the anonymous rate limit.
|
|
131
168
|
- name: Check package.json ordering
|
|
132
169
|
run: pnpm sort-package-json:check
|
|
133
|
-
- name: Validate peer dependency contracts
|
|
134
|
-
run: pnpm peers:check
|
|
135
170
|
- name: Check types
|
|
136
171
|
run: pnpm typecheck
|
|
137
172
|
- name: Run Knip
|
|
@@ -145,15 +180,12 @@ jobs:
|
|
|
145
180
|
env:
|
|
146
181
|
NEXT_TELEMETRY_DISABLED: "1"
|
|
147
182
|
- name: Install Playwright browsers
|
|
183
|
+
env:
|
|
184
|
+
AFFECTED_APPS: ${{ needs.affected-apps.outputs.apps }}
|
|
148
185
|
run: |
|
|
149
|
-
|
|
150
|
-
mapfile -t apps < <(bash .github/scripts/affected-apps.sh "$TURBO_SCM_BASE" HEAD)
|
|
151
|
-
else
|
|
152
|
-
mapfile -t apps < <(bash .github/scripts/affected-apps.sh)
|
|
153
|
-
fi
|
|
154
|
-
for app in "${apps[@]}"; do
|
|
186
|
+
while IFS= read -r app; do
|
|
155
187
|
pnpm --filter "./apps/$app" exec playwright install --with-deps chromium
|
|
156
|
-
done
|
|
188
|
+
done < <(jq --raw-output '.[]' <<< "$AFFECTED_APPS")
|
|
157
189
|
- name: Run end-to-end tests
|
|
158
190
|
run: pnpm test:e2e
|
|
159
191
|
env:
|
|
@@ -173,12 +205,35 @@ jobs:
|
|
|
173
205
|
fi
|
|
174
206
|
|
|
175
207
|
image:
|
|
176
|
-
name: Container Image
|
|
177
|
-
needs:
|
|
178
|
-
|
|
208
|
+
name: Container Image (${{ matrix.app }})
|
|
209
|
+
needs:
|
|
210
|
+
- changes
|
|
211
|
+
- affected-apps
|
|
212
|
+
if: (needs.changes.outputs.full_verification == 'true' || github.event_name == 'schedule') && needs.affected-apps.outputs.apps != '[]'
|
|
179
213
|
runs-on: ubuntu-latest
|
|
180
214
|
timeout-minutes: 20
|
|
215
|
+
strategy:
|
|
216
|
+
fail-fast: false
|
|
217
|
+
matrix:
|
|
218
|
+
app: ${{ fromJSON(needs.affected-apps.outputs.apps) }}
|
|
219
|
+
services:
|
|
220
|
+
postgres:
|
|
221
|
+
image: postgres:17.11-alpine@sha256:266d3c257594e195b309ae9d1805e54c788f1d7b375f56bf6c367aac6c917e7b
|
|
222
|
+
env:
|
|
223
|
+
POSTGRES_PASSWORD: postgres
|
|
224
|
+
POSTGRES_DB: payload
|
|
225
|
+
ports:
|
|
226
|
+
- 5432:5432
|
|
227
|
+
options: >-
|
|
228
|
+
--health-cmd "pg_isready --username postgres"
|
|
229
|
+
--health-interval 5s
|
|
230
|
+
--health-timeout 5s
|
|
231
|
+
--health-retries 10
|
|
181
232
|
env:
|
|
233
|
+
APP: ${{ matrix.app }}
|
|
234
|
+
DATABASE_URL: postgres://postgres:postgres@localhost:5432/payload
|
|
235
|
+
PAYLOAD_SECRET: ci-only-secret
|
|
236
|
+
APP_URL: http://localhost:3000
|
|
182
237
|
HADOLINT_VERSION: 2.15.1
|
|
183
238
|
HADOLINT_SHA256: c7187db94eeeeca956519a6af171adc31453941a1e777961f6e680f697c8c507
|
|
184
239
|
TRIVY_VERSION: 0.76.1
|
|
@@ -187,112 +242,39 @@ jobs:
|
|
|
187
242
|
- name: Checkout repository
|
|
188
243
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
189
244
|
with:
|
|
190
|
-
fetch-depth: 0
|
|
191
245
|
persist-credentials: false
|
|
192
|
-
- name: Fetch the pull request base
|
|
193
|
-
if: github.event_name == 'pull_request'
|
|
194
|
-
env:
|
|
195
|
-
BASE_REF: ${{ github.base_ref }}
|
|
196
|
-
run: git fetch --no-tags origin "$BASE_REF"
|
|
197
|
-
- name: Set up Node.js and pnpm
|
|
198
|
-
uses: pnpm/setup@fbda4c85fc2e1e08721cd8763afea8f48d60f024 # v3.0.0
|
|
199
|
-
with:
|
|
200
|
-
cache: true
|
|
201
|
-
require-lockfile: true
|
|
202
|
-
- name: List affected apps
|
|
203
|
-
id: affected
|
|
204
|
-
env:
|
|
205
|
-
EVENT_NAME: ${{ github.event_name }}
|
|
206
|
-
BASE_REF: ${{ github.base_ref }}
|
|
207
|
-
run: |
|
|
208
|
-
if [[ "$EVENT_NAME" == "pull_request" ]]; then
|
|
209
|
-
mapfile -t apps < <(bash .github/scripts/affected-apps.sh "origin/$BASE_REF" HEAD)
|
|
210
|
-
else
|
|
211
|
-
mapfile -t apps < <(bash .github/scripts/affected-apps.sh)
|
|
212
|
-
fi
|
|
213
|
-
{
|
|
214
|
-
echo "app=false"
|
|
215
|
-
echo "backoffice=false"
|
|
216
|
-
for app in "${apps[@]}"; do
|
|
217
|
-
echo "$app=true"
|
|
218
|
-
done
|
|
219
|
-
} >> "$GITHUB_OUTPUT"
|
|
220
246
|
- name: Lint Dockerfile
|
|
221
|
-
env:
|
|
222
|
-
AFFECTED_APP: ${{ steps.affected.outputs.app }}
|
|
223
|
-
AFFECTED_BACKOFFICE: ${{ steps.affected.outputs.backoffice }}
|
|
224
247
|
run: |
|
|
225
248
|
curl --silent --show-error --fail --location --output hadolint \
|
|
226
249
|
"https://github.com/hadolint/hadolint/releases/download/v${HADOLINT_VERSION}/hadolint-linux-x86_64"
|
|
227
250
|
echo "${HADOLINT_SHA256} hadolint" | sha256sum --check --strict
|
|
228
251
|
chmod +x hadolint
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if [[ "$AFFECTED_BACKOFFICE" == "true" ]]; then
|
|
234
|
-
files+=(apps/backoffice/Dockerfile)
|
|
235
|
-
fi
|
|
236
|
-
if [[ "${#files[@]}" -gt 0 ]]; then
|
|
237
|
-
./hadolint "${files[@]}"
|
|
238
|
-
fi
|
|
239
|
-
- name: Build images
|
|
240
|
-
env:
|
|
241
|
-
AFFECTED_APP: ${{ steps.affected.outputs.app }}
|
|
242
|
-
AFFECTED_BACKOFFICE: ${{ steps.affected.outputs.backoffice }}
|
|
243
|
-
run: |
|
|
244
|
-
if [[ "$AFFECTED_APP" == "true" ]]; then
|
|
245
|
-
docker build --file apps/app/Dockerfile --tag app:ci .
|
|
246
|
-
fi
|
|
247
|
-
if [[ "$AFFECTED_BACKOFFICE" == "true" ]]; then
|
|
248
|
-
docker build --file apps/backoffice/Dockerfile --tag backoffice:ci .
|
|
249
|
-
fi
|
|
250
|
-
- name: Smoke test images
|
|
251
|
-
env:
|
|
252
|
-
AFFECTED_APP: ${{ steps.affected.outputs.app }}
|
|
253
|
-
AFFECTED_BACKOFFICE: ${{ steps.affected.outputs.backoffice }}
|
|
252
|
+
./hadolint "apps/$APP/Dockerfile"
|
|
253
|
+
- name: Build image
|
|
254
|
+
run: docker build --file "apps/$APP/Dockerfile" --tag "$APP:ci" .
|
|
255
|
+
- name: Smoke test image
|
|
254
256
|
id: smoke
|
|
255
257
|
run: |
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
curl --silent --show-error --fail --retry 15 --retry-delay 1 --retry-all-errors --output /dev/null http://localhost:3000/health
|
|
259
|
-
fi
|
|
260
|
-
if [[ "$AFFECTED_BACKOFFICE" == "true" ]]; then
|
|
261
|
-
docker run --detach --name backoffice --env PORT=3001 --env DATABASE_URL=postgres://build-only@localhost/build-only --env PAYLOAD_SECRET=ci-only-secret --env APP_URL=http://localhost:3001 --publish 3001:3001 backoffice:ci
|
|
262
|
-
curl --silent --show-error --fail --retry 15 --retry-delay 1 --retry-all-errors --output /dev/null http://localhost:3001/health
|
|
263
|
-
fi
|
|
258
|
+
docker run --detach --name "$APP" --network host --env PORT=3000 --env DATABASE_URL --env PAYLOAD_SECRET --env APP_URL "$APP:ci"
|
|
259
|
+
curl --silent --show-error --fail --retry 15 --retry-delay 1 --retry-all-errors --output /dev/null http://localhost:3000/health
|
|
264
260
|
- name: Show container logs
|
|
265
261
|
if: failure() && steps.smoke.outcome == 'failure'
|
|
266
|
-
run:
|
|
267
|
-
|
|
268
|
-
docker logs app
|
|
269
|
-
fi
|
|
270
|
-
if docker container inspect backoffice >/dev/null 2>&1; then
|
|
271
|
-
docker logs backoffice
|
|
272
|
-
fi
|
|
273
|
-
- name: Scan images for vulnerabilities
|
|
274
|
-
env:
|
|
275
|
-
AFFECTED_APP: ${{ steps.affected.outputs.app }}
|
|
276
|
-
AFFECTED_BACKOFFICE: ${{ steps.affected.outputs.backoffice }}
|
|
262
|
+
run: docker logs "$APP"
|
|
263
|
+
- name: Scan image for vulnerabilities
|
|
277
264
|
run: |
|
|
278
265
|
archive="trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz"
|
|
279
266
|
curl --silent --show-error --fail --location --output "$archive" \
|
|
280
267
|
"https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/${archive}"
|
|
281
268
|
echo "${TRIVY_SHA256} ${archive}" | sha256sum --check --strict
|
|
282
269
|
tar --extract --gzip --file "$archive" trivy
|
|
283
|
-
|
|
284
|
-
./trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed --no-progress app:ci
|
|
285
|
-
fi
|
|
286
|
-
if [[ "$AFFECTED_BACKOFFICE" == "true" ]]; then
|
|
287
|
-
./trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed --no-progress backoffice:ci
|
|
288
|
-
fi
|
|
270
|
+
./trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed --no-progress "$APP:ci"
|
|
289
271
|
|
|
290
272
|
deploy-dev:
|
|
291
273
|
name: Deploy to dev
|
|
292
274
|
needs:
|
|
293
275
|
- checks
|
|
294
276
|
- image
|
|
295
|
-
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && vars.DEPLOY_ENABLED == 'true'
|
|
277
|
+
if: ${{ !cancelled() && needs.checks.result == 'success' && contains(fromJSON('["success", "skipped"]'), needs.image.result) && github.event_name == 'push' && github.ref == 'refs/heads/main' && vars.DEPLOY_ENABLED == 'true' }}
|
|
296
278
|
permissions:
|
|
297
279
|
contents: read
|
|
298
280
|
actions: read # Needed by deploy.yml to find the CI run of the deployed commit.
|
|
@@ -301,12 +283,67 @@ jobs:
|
|
|
301
283
|
environment: dev
|
|
302
284
|
commit_sha: ${{ github.sha }}
|
|
303
285
|
|
|
286
|
+
triage:
|
|
287
|
+
name: Triage failed build
|
|
288
|
+
needs:
|
|
289
|
+
- changes
|
|
290
|
+
- affected-apps
|
|
291
|
+
- checks
|
|
292
|
+
- image
|
|
293
|
+
if: failure() && vars.CLAUDE_CI_ENABLED == 'true' && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
|
|
294
|
+
runs-on: ubuntu-latest
|
|
295
|
+
timeout-minutes: 15
|
|
296
|
+
permissions:
|
|
297
|
+
actions: read # Needed to read the logs of the failed jobs.
|
|
298
|
+
id-token: write # Needed to authenticate the Claude GitHub App with the workflow's OIDC token.
|
|
299
|
+
pull-requests: write # Needed to post the summary on the pull request.
|
|
300
|
+
steps:
|
|
301
|
+
- name: Require a Claude credential
|
|
302
|
+
env:
|
|
303
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
304
|
+
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
305
|
+
run: |
|
|
306
|
+
[[ -n "$ANTHROPIC_API_KEY" || -n "$CLAUDE_CODE_OAUTH_TOKEN" ]] || { echo "::error::Set the CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY repository secret"; exit 1; }
|
|
307
|
+
- name: Collect the build log
|
|
308
|
+
env:
|
|
309
|
+
GH_TOKEN: ${{ github.token }}
|
|
310
|
+
REPOSITORY: ${{ github.repository }}
|
|
311
|
+
RUN_ID: ${{ github.run_id }}
|
|
312
|
+
run: |
|
|
313
|
+
mkdir -p out
|
|
314
|
+
for job in $(gh api "repos/$REPOSITORY/actions/runs/$RUN_ID/jobs" --paginate --jq '.jobs[] | select(.conclusion == "failure") | .id'); do
|
|
315
|
+
gh api --allow-escape-sequences "repos/$REPOSITORY/actions/jobs/$job/logs" >> out/build.log
|
|
316
|
+
done
|
|
317
|
+
- name: Triage failed build
|
|
318
|
+
id: triage
|
|
319
|
+
uses: anthropics/claude-code-action@61a9c274fa7110a66dfafc1bbbced03833eb25fe # v1.0.269
|
|
320
|
+
with:
|
|
321
|
+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
322
|
+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
323
|
+
prompt: >
|
|
324
|
+
Read the build log at out/build.log. Identify the most
|
|
325
|
+
likely cause, say whether the failure looks flaky or real, and write a
|
|
326
|
+
three-line summary for the PR thread.
|
|
327
|
+
claude_args: >-
|
|
328
|
+
--model claude-haiku-5-5
|
|
329
|
+
--max-turns 5
|
|
330
|
+
--allowedTools "Read"
|
|
331
|
+
--json-schema '{"type":"object","properties":{"summary":{"type":"string"}},"required":["summary"]}'
|
|
332
|
+
- name: Post the summary
|
|
333
|
+
env:
|
|
334
|
+
GH_TOKEN: ${{ github.token }}
|
|
335
|
+
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
336
|
+
REPOSITORY: ${{ github.repository }}
|
|
337
|
+
SUMMARY: ${{ fromJSON(steps.triage.outputs.structured_output).summary }}
|
|
338
|
+
run: gh pr comment "$PR_NUMBER" --repo "$REPOSITORY" --body "$SUMMARY"
|
|
339
|
+
|
|
304
340
|
ci:
|
|
305
341
|
name: All Checks
|
|
306
342
|
if: always()
|
|
307
343
|
timeout-minutes: 5
|
|
308
344
|
needs:
|
|
309
345
|
- changes
|
|
346
|
+
- affected-apps
|
|
310
347
|
- checks
|
|
311
348
|
- image
|
|
312
349
|
runs-on: ubuntu-latest
|
|
@@ -51,6 +51,7 @@ jobs:
|
|
|
51
51
|
persist-credentials: false
|
|
52
52
|
- name: Require the commit on main with passing CI and a deployment to the previous environment
|
|
53
53
|
if: github.event_name == 'workflow_dispatch'
|
|
54
|
+
shell: bash
|
|
54
55
|
env:
|
|
55
56
|
GH_TOKEN: ${{ github.token }}
|
|
56
57
|
REPOSITORY: ${{ github.repository }}
|
|
@@ -115,14 +116,16 @@ jobs:
|
|
|
115
116
|
require-lockfile: true
|
|
116
117
|
- name: List affected apps
|
|
117
118
|
id: affected
|
|
119
|
+
shell: bash
|
|
118
120
|
env:
|
|
119
121
|
COMMIT_SHA: ${{ inputs.commit_sha }}
|
|
120
122
|
GH_TOKEN: ${{ github.token }}
|
|
121
|
-
RAILWAY_ENVIRONMENT: ${{ inputs.environment }}
|
|
122
123
|
REPOSITORY: ${{ github.repository }}
|
|
123
124
|
run: |
|
|
124
125
|
previous=""
|
|
125
126
|
latest=""
|
|
127
|
+
deploy_runs="$(gh api --paginate "repos/$REPOSITORY/actions/workflows/deploy.yml/runs?status=success&branch=main&per_page=50" --jq '.workflow_runs[] | [.id, .display_title] | @tsv')"
|
|
128
|
+
ci_runs="$(gh api "repos/$REPOSITORY/actions/workflows/ci.yml/runs?event=push&branch=main&status=success&per_page=50" --jq '.workflow_runs[] | [.id, .head_sha] | @tsv')"
|
|
126
129
|
while IFS=$'\t' read -r run_id title; do
|
|
127
130
|
[[ -z "$run_id" ]] && continue
|
|
128
131
|
if [[ "$title" =~ ^Deploy\ ([0-9a-f]{40})\ to\ ${RAILWAY_ENVIRONMENT}$ ]]; then
|
|
@@ -136,7 +139,7 @@ jobs:
|
|
|
136
139
|
latest="$completed"
|
|
137
140
|
previous="$candidate"
|
|
138
141
|
fi
|
|
139
|
-
done
|
|
142
|
+
done <<< "$deploy_runs"
|
|
140
143
|
while IFS=$'\t' read -r run_id head_sha; do
|
|
141
144
|
[[ -z "$run_id" || "$head_sha" == "$COMMIT_SHA" ]] && continue
|
|
142
145
|
completed="$(gh api "repos/$REPOSITORY/actions/runs/$run_id/jobs" | jq -r --arg env "$RAILWAY_ENVIRONMENT" '[.jobs[] | select(.conclusion == "success" and .name == "Deploy to \($env) / Deploy to \($env)") | .completed_at] | max // empty')"
|
|
@@ -144,20 +147,14 @@ jobs:
|
|
|
144
147
|
latest="$completed"
|
|
145
148
|
previous="$head_sha"
|
|
146
149
|
fi
|
|
147
|
-
done
|
|
150
|
+
done <<< "$ci_runs"
|
|
148
151
|
if [[ -n "$previous" ]]; then
|
|
149
152
|
git cat-file -e "$previous^{commit}" 2>/dev/null || git fetch --no-tags origin "$previous"
|
|
150
|
-
|
|
153
|
+
apps="$(bash .github/scripts/affected-apps.sh "$previous" "$COMMIT_SHA")"
|
|
151
154
|
else
|
|
152
|
-
|
|
155
|
+
apps="$(bash .github/scripts/affected-apps.sh)"
|
|
153
156
|
fi
|
|
154
|
-
|
|
155
|
-
echo "app=false"
|
|
156
|
-
echo "backoffice=false"
|
|
157
|
-
for app in "${apps[@]}"; do
|
|
158
|
-
echo "$app=true"
|
|
159
|
-
done
|
|
160
|
-
} >> "$GITHUB_OUTPUT"
|
|
157
|
+
echo "apps=$apps" >> "$GITHUB_OUTPUT"
|
|
161
158
|
- name: Install verified Railway CLI
|
|
162
159
|
run: |
|
|
163
160
|
archive="railway-v${RAILWAY_CLI_VERSION}-x86_64-unknown-linux-musl.tar.gz"
|
|
@@ -169,34 +166,38 @@ jobs:
|
|
|
169
166
|
- name: Deploy
|
|
170
167
|
env:
|
|
171
168
|
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
|
|
172
|
-
|
|
173
|
-
DEPLOY_BACKOFFICE: ${{ steps.affected.outputs.backoffice }}
|
|
169
|
+
DEPLOY_APPS: ${{ steps.affected.outputs.apps }}
|
|
174
170
|
run: |
|
|
175
|
-
|
|
171
|
+
affected() {
|
|
172
|
+
jq --exit-status --arg app "$1" 'index($app) != null' <<< "$DEPLOY_APPS" > /dev/null
|
|
173
|
+
}
|
|
174
|
+
if affected app; then
|
|
176
175
|
railway up --ci --service "$RAILWAY_SERVICE_APP" --environment "$RAILWAY_ENVIRONMENT"
|
|
177
176
|
fi
|
|
178
|
-
if
|
|
177
|
+
if affected backoffice; then
|
|
179
178
|
railway up --ci --service "$RAILWAY_SERVICE_BACKOFFICE" --environment "$RAILWAY_ENVIRONMENT"
|
|
180
179
|
fi
|
|
181
180
|
- name: Require a healthy deployment
|
|
182
181
|
env:
|
|
183
|
-
|
|
184
|
-
DEPLOY_BACKOFFICE: ${{ steps.affected.outputs.backoffice }}
|
|
182
|
+
DEPLOY_APPS: ${{ steps.affected.outputs.apps }}
|
|
185
183
|
run: |
|
|
184
|
+
affected() {
|
|
185
|
+
jq --exit-status --arg app "$1" 'index($app) != null' <<< "$DEPLOY_APPS" > /dev/null
|
|
186
|
+
}
|
|
186
187
|
wait_health() {
|
|
187
|
-
|
|
188
|
+
health_url="${1%/}/health"
|
|
188
189
|
for _ in $(seq 30); do
|
|
189
|
-
if curl --silent --fail --output /dev/null "$
|
|
190
|
+
if curl --silent --fail --output /dev/null "$health_url"; then
|
|
190
191
|
return 0
|
|
191
192
|
fi
|
|
192
193
|
sleep 10
|
|
193
194
|
done
|
|
194
|
-
echo "::error::$
|
|
195
|
+
echo "::error::$health_url did not respond"
|
|
195
196
|
return 1
|
|
196
197
|
}
|
|
197
|
-
if
|
|
198
|
+
if affected app; then
|
|
198
199
|
wait_health "$APP_URL"
|
|
199
200
|
fi
|
|
200
|
-
if
|
|
201
|
+
if affected backoffice; then
|
|
201
202
|
wait_health "$BACKOFFICE_URL"
|
|
202
203
|
fi
|
|
@@ -4,24 +4,24 @@ Turborepo workspace in TypeScript on pnpm and Node.js `>=24.22.1 <25`. `apps/app
|
|
|
4
4
|
|
|
5
5
|
## Commands
|
|
6
6
|
|
|
7
|
-
| Task
|
|
8
|
-
|
|
|
9
|
-
| Start both apps with reload
|
|
10
|
-
| One test
|
|
11
|
-
| Tests in one app
|
|
12
|
-
| Unit tests
|
|
13
|
-
| End-to-end tests
|
|
14
|
-
| Regenerate after a collection
|
|
15
|
-
| Create a migration
|
|
16
|
-
| What `pre-push` runs
|
|
17
|
-
| Every check
|
|
18
|
-
| Render rules after a change
|
|
7
|
+
| Task | Command | Healthy output |
|
|
8
|
+
| :--------------------------------- | :----------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------- |
|
|
9
|
+
| Start both apps with reload | `pnpm dev` | `✓ Ready in 300ms` from each app |
|
|
10
|
+
| One test | `pnpm --filter ./apps/backoffice exec vitest run src/environment.test.ts -t "<test name>"` | `Tests 1 passed \| 4 skipped (5)` |
|
|
11
|
+
| Tests in one app | `TURBO_FILTER=--filter=./apps/app pnpm test` | `Tasks: 1 successful, 1 total` |
|
|
12
|
+
| Unit tests | `pnpm test`, or `pnpm test:coverage` | `Tasks: 2 successful, 2 total` |
|
|
13
|
+
| End-to-end tests | `pnpm test:e2e` | `Tasks: 4 successful, 4 total`, with `DATABASE_URL` pointing at a running database |
|
|
14
|
+
| Regenerate after a collection | `pnpm generate:types`, `pnpm generate:importmap` | `INFO: Compiling TS types for Collections and Globals...`, then nothing; `No new imports found, skipping writing import map` |
|
|
15
|
+
| Create a migration | `pnpm migrate:create <name>` | `INFO: Migration created at` the new file under `migrations/`, then `INFO: Done.` |
|
|
16
|
+
| What `pre-push` runs | `pnpm check` | exits 0; the output ends with the summary `Tasks: 2 successful, 2 total` |
|
|
17
|
+
| Every check except `pnpm test:e2e` | `pnpm verify` | exits 0; the output ends with the summary `Tasks: 2 successful, 2 total` |
|
|
18
|
+
| Render rules after a change | `pnpm rules:sync` | `Synced 69 agent rule files; 0 updated.` |
|
|
19
19
|
|
|
20
20
|
## Verifying your work
|
|
21
21
|
|
|
22
22
|
- Build: `pnpm build` (prints `Tasks: 2 successful, 2 total`)
|
|
23
23
|
- Test: `pnpm test:coverage` (all green, and prints `Tasks: 2 successful, 2 total`; never skip or delete a failing test)
|
|
24
|
-
-
|
|
24
|
+
- Static checks: `pnpm verify:static` (ESLint allows zero warnings; every check must pass)
|
|
25
25
|
|
|
26
26
|
Run all three before reporting any task complete, and paste the output. `pnpm verify` runs all three.
|
|
27
27
|
|
|
@@ -29,19 +29,20 @@ Run all three before reporting any task complete, and paste the output. `pnpm ve
|
|
|
29
29
|
|
|
30
30
|
- Root scripts run lint, format, Knip, and the agent checks once for the workspace, and hand `build`, `test`, `typecheck`, and `depcruise` to Turborepo for each app. `TURBO_FILTER` narrows them; the default is every app.
|
|
31
31
|
- The site lives in `apps/app/app/`. The backoffice site lives in `apps/backoffice/src/app/(frontend)/`, its collections in `apps/backoffice/src/collections/`, and its config in `apps/backoffice/src/payload.config.ts`. The root `generate:*` and `migrate*` scripts run in the backoffice.
|
|
32
|
-
- In development, Payload applies collection changes to the database directly. In production, the backoffice applies the migrations in `apps/backoffice/src/migrations/` when
|
|
32
|
+
- In development, Payload applies collection changes to the database directly. In production, the backoffice applies the migrations in `apps/backoffice/src/migrations/` when Payload first initialises, which `/health` triggers, so the first health check after a deploy applies them, not when the server starts.
|
|
33
33
|
|
|
34
34
|
## Conventions
|
|
35
35
|
|
|
36
|
-
1. **Change the backoffice schema only through collections, then regenerate and commit.** Payload generates `apps/backoffice/src/
|
|
36
|
+
1. **Change the backoffice schema only through collections, then regenerate and commit.** Payload generates `apps/backoffice/src/payload-types.ts`, `apps/backoffice/src/migrations/`, and `apps/backoffice/src/app/(payload)/admin/importMap.ts`; run `pnpm generate:types`, `pnpm generate:importmap`, and `pnpm migrate:create <name>`. Do not edit those files by hand.
|
|
37
37
|
2. **Put unit tests next to the code and Playwright end-to-end tests in `apps/app/test/` and `apps/backoffice/test/` as `*.e2e.test.ts`.** Vitest collects `apps/app/app/**/*.test.{ts,tsx}` and `apps/backoffice/src/**/*.test.{ts,tsx}`.
|
|
38
38
|
3. **Component tests need a DOM test environment, and Vitest runs in Node.js.** Add that environment with the first component test.
|
|
39
39
|
4. **Read backoffice configuration through `requireEnvironment` in `apps/backoffice/src/environment.ts`.** Document each variable in `.env.example`.
|
|
40
40
|
5. **Start from the accepted spec, in plan mode** (Cursor: Plan Mode). Give the plan the change's `intent/<change>/intent.md` and `spec.md`. Commit the approved plan as `intent/<change>/plan.md` with an `Accepted by:` line naming the engineer, as `.agents/skills/sdlc/playbooks/build.md` describes.
|
|
41
|
-
6. **Start a bug fix with a failing test.** Reproduce the bug as a test, confirm it fails for the reason you expect, and commit it
|
|
41
|
+
6. **Start a bug fix with a failing test.** A bug fix has no intent and no spec; its plan, `intent/<short-description>/plan.md`, is accepted by an engineer before any code changes. Reproduce the bug as a test, confirm it fails for the reason you expect, and commit it on a `fix/` branch. Then `.agents/hooks/agent-test-edit-gate.sh` blocks test edits by itself: make the test pass without editing it. On a branch with another name, create `.git/bug-fix-in-progress` first, and delete it once the test passes.
|
|
42
42
|
7. **Do not start work a person did not ask for.** When a message reports a problem without asking for work, answer in a few lines and end with the message that would start it: `/sdlc` followed by their own words.
|
|
43
43
|
8. **Branch from the latest `origin/main`, and open the pull request ready for review.** Name the branch `<type>/<short-description>` and fill the body from `.github/pull_request_template.md`. `.github/workflows/ci.yml` runs full verification only on a pull request that is not a draft. Never merge a pull request: a person merges it with `--squash` after code owner approval, bottom-up for a stack of pull requests.
|
|
44
44
|
9. **Write replies a person can check.** Reply in the language the person wrote in. Write paths from the project root, such as `README.md:12`, never from the machine's root. State as a fact only what you opened or ran in this turn, and say what you ran; mark anything else as inferred or not checked. Answer what was asked and stop: leave out tools, services, and steps the request does not need, and never hand the person a check you could run. Send nothing outside the session, such as a message to another session or a service, unless a task step says to.
|
|
45
|
+
10. **Run parallel sessions in separate worktrees, on tasks that touch different files.** Start each with `claude --worktree <name>`: a worktree is its own checkout on its own branch. Tasks that share files run in one session, one after another. Start with two or three sessions, and add one only while you can review every stream properly.
|
|
45
46
|
|
|
46
47
|
## Things Claude gets wrong
|
|
47
48
|
|
|
@@ -49,4 +50,7 @@ When Claude makes a mistake twice, add the correction here.
|
|
|
49
50
|
|
|
50
51
|
- **Next.js docs live per app**: this Next.js version differs from your training data. Read the guide in `apps/app/node_modules/next/dist/docs/` or `apps/backoffice/node_modules/next/dist/docs/` before writing Next.js code; the workspace root has no `next` package.
|
|
51
52
|
- **Database for `pnpm dev` and end-to-end tests**: the backoffice needs a PostgreSQL database in `DATABASE_URL` and `PAYLOAD_SECRET` in `.env`; the README lists every variable. Run `pnpm --filter ./apps/app exec playwright install chromium` once before the first `pnpm test:e2e`.
|
|
52
|
-
- **Production gate**: `.agents/hooks/agent-production-gate.sh` blocks a shell command that deploys to `prod`, runs a Railway CLI command that names `prod`, or pushes a release tag, unless a release manager started the session with `RELEASE_APPROVAL` set. Report what is ready to promote instead. When a gated command appears only inside a message, write the text to a file with your file-editing tool, not a shell heredoc, and pass it with `git commit -F <file>` or `gh pr create --body-file <file>`. The gate reads only shell commands, so an MCP tool or a Railway command without `-e` or `--environment` passes it.
|
|
53
|
+
- **Production gate**: `.agents/hooks/agent-production-gate.sh` blocks a shell command that deploys to `prod`, runs a Railway CLI command that names `prod`, or pushes a release tag, unless a release manager started the session with `RELEASE_APPROVAL` set. Any value lifts the gate except an empty value, `0`, `false`, and `no`, in any letter case. Report what is ready to promote instead. When a gated command appears only inside a message, write the text to a file with your file-editing tool, not a shell heredoc, and pass it with `git commit -F <file>` or `gh pr create --body-file <file>`. The gate reads only shell commands, so an MCP tool or a Railway command without `-e` or `--environment` passes it.
|
|
54
|
+
- **Issues**: open an issue only to hand work to another person, never to give a pull request a number to close. A pull request closes an issue that already exists with `Closes #<issue>`.
|
|
55
|
+
- **What belongs in the repository**: files, commit messages, and pull requests hold the change, its reason, and the checks. Write no date, log, history, mistake count, token figure, or session note into a file.
|
|
56
|
+
- **Sourced requirements**: add a requirement to an intent, spec, or plan only when a person decided it or a named source says it. This includes a requirement a review bot suggests. When no person decided it and no source says it, leave it out and answer the thread with that.
|
|
@@ -5,10 +5,11 @@ Use this guide to prepare changes to this repository.
|
|
|
5
5
|
## Prerequisites
|
|
6
6
|
|
|
7
7
|
- Use the Node.js version declared by `.nvmrc` and `package.json#engines`.
|
|
8
|
-
- Change the Node.js version in `.nvmrc` and both Dockerfiles together. Dependabot updates only the Dockerfiles.
|
|
8
|
+
- Change the Node.js version in `.nvmrc`, the `engines` field of the root and both app `package.json` files, and both Dockerfiles together. Dependabot updates only the Dockerfiles.
|
|
9
9
|
- Install pnpm once with `npm install --global pnpm`. It switches to the version declared by `package.json#packageManager` automatically.
|
|
10
10
|
- Use Claude Code v2.1.295 or later. It reads `CLAUDE.md`, which imports `AGENTS.md`, and supports the `onFailure` field the agent hooks use.
|
|
11
|
-
-
|
|
11
|
+
- Copy `.env.example` to `.env` unless the CLI created it. `pnpm dev`, `pnpm build`, `pnpm check`, and `pnpm verify` need `DATABASE_URL`, `PAYLOAD_SECRET`, and `APP_URL` from it.
|
|
12
|
+
- Install gitleaks 8.21 or later, which the `pre-commit` hook runs, and jq, which the agent hooks and the `pre-push` hook (`pnpm gates:check`) need: `brew install gitleaks jq` on macOS, or install them from each project's releases on Linux.
|
|
12
13
|
|
|
13
14
|
From the repository root:
|
|
14
15
|
|
|
@@ -36,9 +37,10 @@ Run the narrowest check that covers the change while you work. Git hooks run the
|
|
|
36
37
|
| `pre-merge-commit` | blocks merge commits on `main` |
|
|
37
38
|
| `pre-push` | blocks pushes to `main` except the push that creates it, then `pnpm check`: the static checks, the unit tests related to your changes, and the production build |
|
|
38
39
|
|
|
39
|
-
- **Scans:** CI also runs coverage, the build, the container image, and the secret, SAST, OSV, CodeQL, dependency-review, and OpenSSF Scorecard scans. Dependabot and OSV-Scanner report vulnerable dependencies.
|
|
40
|
-
- **End-to-end tests:** run `pnpm test:e2e` when you change pages or the admin panel. It starts both development servers unless they are already running
|
|
40
|
+
- **Scans:** CI also runs coverage, the build, the container image, and the secret, SAST, OSV, CodeQL, dependency-review, and OpenSSF Scorecard scans. CodeQL, dependency-review, and Scorecard run on public repositories, and on private ones when the `CODE_SCANNING` repository variable is `true`. Dependabot and OSV-Scanner report vulnerable dependencies.
|
|
41
|
+
- **End-to-end tests:** run `pnpm test:e2e` when you change pages or the admin panel. It builds both apps first, then starts both development servers unless they are already running. The build and the backoffice need `DATABASE_URL`, `PAYLOAD_SECRET`, and `APP_URL` from `.env`. Run `pnpm --filter ./apps/app exec playwright install chromium` once before the first run. CI runs it against the production build and a PostgreSQL service on every pull request that is ready for review and changes code.
|
|
41
42
|
- **Coverage:** CI requires 95% branch coverage on changed lines in `apps/app/app/` and `apps/backoffice/src/`, except layouts, the Payload config, and generated files.
|
|
43
|
+
- **Agent evals:** run `pnpm evals:run` after you change `AGENTS.md`, a skill under `.agents/skills/`, or a hook under `.agents/hooks/`, and add a case name to run one case. It runs the cases in `.agents/skill-evals/` with Claude Code on your own Claude login, so it spends your Claude usage, and it is not part of `pnpm verify`.
|
|
42
44
|
|
|
43
45
|
Run `pnpm verify` for repository changes.
|
|
44
46
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# next-friday-template-turborepo
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Turborepo workspace. `apps/app` is a Next.js site on port 3000. `apps/backoffice` is a Payload CMS on port 3001. The backoffice API is REST at `/api`.
|
|
4
4
|
|
|
5
5
|
## Getting started
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ pnpm install
|
|
|
10
10
|
pnpm dev
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
The CLI created `.env` from `.env.example` and set `PAYLOAD_SECRET` to a random secret. Before `pnpm dev`, set in `.env`:
|
|
13
|
+
The CLI created `.env` from `.env.example` and set `PAYLOAD_SECRET` to a random secret. Before `pnpm dev`, `pnpm build`, `pnpm check`, or `pnpm verify`, set in `.env`:
|
|
14
14
|
|
|
15
15
|
- `DATABASE_URL`: a PostgreSQL database.
|
|
16
16
|
- `APP_URL`: the URL you open the backoffice at, such as `http://localhost:3001`.
|
|
@@ -20,11 +20,11 @@ Then open `http://localhost:3000` for the site and `http://localhost:3001/admin`
|
|
|
20
20
|
|
|
21
21
|
**Warning:** until the first user exists, anyone who can reach the backoffice `/admin` can create it. Create the first user in each deployed environment right after its first deploy, before you share the URL.
|
|
22
22
|
|
|
23
|
-
In development, Payload applies collection changes to the database directly. In production,
|
|
23
|
+
In development, Payload applies collection changes to the database directly. In production, Payload applies the migrations in `apps/backoffice/src/migrations/` when it first initialises, which the backoffice `/health` triggers, so the first health check after a deploy applies them and fails while the database is unreachable.
|
|
24
24
|
|
|
25
25
|
## After creating a project from this template
|
|
26
26
|
|
|
27
|
-
Follow [SETUP.md](SETUP.md) in order. It sets up the repository, the reviews, and the AI-native SDLC. In step 1, also replace the `Next
|
|
27
|
+
Follow [SETUP.md](SETUP.md) in order. It sets up the repository, the reviews, and the AI-native SDLC. In step 1, also replace the `Next.js application` text in `apps/app/app/layout.tsx`, `apps/app/app/page.tsx`, and `apps/app/app/page.test.ts`, and the `Payload application` text in `apps/backoffice/src/app/(frontend)/layout.tsx`, `apps/backoffice/src/app/(frontend)/page.tsx`, and `apps/backoffice/src/app/(frontend)/page.test.ts`.
|
|
28
28
|
|
|
29
29
|
## Scripts
|
|
30
30
|
|
|
@@ -42,13 +42,13 @@ Follow [SETUP.md](SETUP.md) in order. It sets up the repository, the reviews, an
|
|
|
42
42
|
| `pnpm check` | Run the static checks, the unit tests for changed code, and the production build (the `pre-push` hook) |
|
|
43
43
|
| `pnpm verify` | Run the static checks, all unit tests with coverage, and the production build |
|
|
44
44
|
|
|
45
|
-
CI also runs the build, the end-to-end tests, the container image scans, and the security scans, including CodeQL, dependency review, and OpenSSF Scorecard. CodeQL and Scorecard run on public repositories, and on private ones when the `CODE_SCANNING` repository variable is `true`. Pull requests run Turborepo with `--affected` against the base branch, so only packages that changed are typed, tested, built, and scanned. Pushes to `main`
|
|
45
|
+
CI also runs the build, the end-to-end tests, the container image scans, and the security scans, including CodeQL, dependency review, and OpenSSF Scorecard. CodeQL, dependency review, and Scorecard run on public repositories, and on private ones when the `CODE_SCANNING` repository variable is `true`. Pull requests run Turborepo with `--affected` against the base branch, so only packages that changed are typed, tested, built, and scanned. Pushes to `main` run every app. The weekly schedule rebuilds and scans every container image and runs nothing else. Patch coverage runs only for apps that produced a coverage file.
|
|
46
46
|
|
|
47
47
|
To reuse task output across CI runs, create a Vercel team, add the repository variable `TURBO_TEAM`, and follow [Turborepo on GitHub Actions](https://turborepo.dev/docs/guides/ci-vendors/github-actions) to allow OIDC. The workflow exchanges GitHub OIDC for a short-lived Remote Cache token. If OIDC is unavailable, set the `TURBO_TOKEN` secret instead. Leave both unset to run without a Remote Cache.
|
|
48
48
|
|
|
49
49
|
## Container
|
|
50
50
|
|
|
51
|
-
Each app has a Dockerfile. Build both from the workspace root. The images run as the `node` user
|
|
51
|
+
Each app has a Dockerfile. Build both from the workspace root. The images run as the `node` user and listen on `PORT`, or 3000 when it is unset. The backoffice needs `DATABASE_URL`, `PAYLOAD_SECRET`, and `APP_URL`.
|
|
52
52
|
|
|
53
53
|
```sh
|
|
54
54
|
docker build --file apps/app/Dockerfile --tag app .
|
|
@@ -81,14 +81,14 @@ On Vercel, deploy an app without its image. The Next.js preset takes precedence
|
|
|
81
81
|
|
|
82
82
|
Limit `dev`, `uat`, and `prod` to the `main` branch. Add the release managers as required reviewers on `prod` only; `dev` deploys on every push to `main`, so a reviewer there would hold each push. Environment secrets and variables in a private repository need GitHub Pro, Team, or Enterprise; required reviewers in a private repository need GitHub Enterprise.
|
|
83
83
|
|
|
84
|
-
3. Set the repository variable `DEPLOY_ENABLED` to `true`. Each push to `main` that passes CI then deploys to `dev`. The workflow compares the commit with the last
|
|
84
|
+
3. Set the repository variable `DEPLOY_ENABLED` to `true`. Each push to `main` that passes CI then deploys to `dev`. The workflow compares the commit with the commit of the last successful deploy to that environment, found from the successful `deploy.yml` and `ci.yml` runs on `main`, and runs `railway up` only for services whose packages changed. A promotion through `uat` or `prod` uses the same comparison against the commit last deployed there, so several commits still ship every changed service. The first deploy to an environment ships both services.
|
|
85
85
|
4. Promote with **Actions → Deploy → Run workflow**, giving the environment and the full commit SHA. The workflow requires a successful CI run on `main` for that commit and a successful deployment to the previous environment, then waits for `/health` on each service it uploaded.
|
|
86
86
|
|
|
87
87
|
Remove the workflow, `railway.json`, and the `deploy-dev` job in `ci.yml` if the project deploys elsewhere.
|
|
88
88
|
|
|
89
89
|
## Mobile app
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
To add an Expo app, create it under `apps/mobile`:
|
|
92
92
|
|
|
93
93
|
1. Create the app from the workspace root with `pnpm create expo-app@latest apps/mobile`, then add `packages/*` workspace packages as you share code. Keep `eas.json` and `credentials.json` inside `apps/mobile` and run the EAS CLI from that directory ([Expo: Build with monorepos](https://docs.expo.dev/build-reference/build-with-monorepos/)).
|
|
94
94
|
2. Use `@expo/fingerprint` with `expo-github-action/fingerprint` so a pull request requests a native build only when the native layer changed ([Expo fingerprint](https://docs.expo.dev/versions/latest/sdk/fingerprint/)).
|