From 85dc7295b2a6924e0adf4e0ab3193cc1476c2edd Mon Sep 17 00:00:00 2001 From: Nitin Patel Date: Wed, 6 May 2026 01:51:12 -0400 Subject: [PATCH] chore: add standard setup/test/e2e automation --- .github/workflows/standard-ci.yml | 7 +++ .github/workflows/version-bump.yml | 54 ++++++++++++++++++ .managed-rules-version | 1 + .version | 1 + AGENTS.md | 26 +++------ COST-TRACKING.md | 26 +++++++++ RULEBOOK.md | 48 ++++++++++++++++ VERSIONING.md | 24 ++++++++ scripts/verify-managed-rules.sh | 30 ++++++++++ scripts/version.sh | 90 ++++++++++++++++++++++++++++++ 10 files changed, 290 insertions(+), 17 deletions(-) create mode 100644 .github/workflows/version-bump.yml create mode 100644 .managed-rules-version create mode 100644 .version create mode 100644 COST-TRACKING.md create mode 100644 RULEBOOK.md create mode 100644 VERSIONING.md create mode 100755 scripts/verify-managed-rules.sh create mode 100755 scripts/version.sh diff --git a/.github/workflows/standard-ci.yml b/.github/workflows/standard-ci.yml index 172e33d..2fa7361 100644 --- a/.github/workflows/standard-ci.yml +++ b/.github/workflows/standard-ci.yml @@ -43,6 +43,13 @@ jobs: jq . .claims/claims.json >/dev/null fi + - name: Verify Managed Rules + run: | + chmod +x scripts/verify-managed-rules.sh || true + if [ -f scripts/verify-managed-rules.sh ]; then + ./scripts/verify-managed-rules.sh + fi + - name: Run tests run: | chmod +x scripts/test.sh || true diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml new file mode 100644 index 0000000..04fc09b --- /dev/null +++ b/.github/workflows/version-bump.yml @@ -0,0 +1,54 @@ +name: Version Bump + +on: + workflow_dispatch: + inputs: + stage: + description: "Target stage" + required: true + type: choice + options: + - dev + - qa + - beta + - prod + bump: + description: "Semver bump type" + required: true + type: choice + options: + - patch + - minor + - major + +jobs: + bump: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Bump version + run: | + chmod +x scripts/version.sh + scripts/version.sh "${{ inputs.stage }}" "${{ inputs.bump }}" + + - name: Commit changes + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + if [[ -n "$(git status --porcelain)" ]]; then + VERSION="$(cat .version)" + git add .version package.json VERSIONING.md scripts/version.sh || true + git commit -m "chore(version): bump to ${VERSION} [${{ inputs.stage }}]" + git push + else + echo "No version changes to commit" + fi diff --git a/.managed-rules-version b/.managed-rules-version new file mode 100644 index 0000000..795460f --- /dev/null +++ b/.managed-rules-version @@ -0,0 +1 @@ +v1.1.0 diff --git a/.version b/.version new file mode 100644 index 0000000..2cb21bd --- /dev/null +++ b/.version @@ -0,0 +1 @@ +0.1.0-dev.1 diff --git a/AGENTS.md b/AGENTS.md index 7c115c7..34e4279 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,17 +1,9 @@ -# Agent Execution Rules - -Any model or coding agent using this repository should start here. - -Primary source of truth: - -- `docs/shared-coding-rules.md` - -Model-specific entry files: - -- `CODEX.md` -- `CLAUDE.md` -- `GEMINI.md` - -Rule: - -If a task changes product behavior, backend or frontend flow, contracts, policies, or deployment behavior, read `docs/shared-coding-rules.md` before editing code. +# Agent Git Rules + +- Run `git status -sb` before work. +- Sync base first: `git checkout main && git pull --ff-only origin main`. +- Create feature branch per task. +- No force-push on shared branches. +- Do not commit secrets (`.env`, tokens, logs). +- Open PR for merge to `main`. +- Follow managed [RULEBOOK.md](./RULEBOOK.md) before adding APIs or shared logic. diff --git a/COST-TRACKING.md b/COST-TRACKING.md new file mode 100644 index 0000000..fd0251b --- /dev/null +++ b/COST-TRACKING.md @@ -0,0 +1,26 @@ +# Cost Tracking + +## Purpose +This repository must track usage and cost events in an append-only model so billing and analytics are reproducible. + +## Minimum Requirements +1. Every billable event must include a unique idempotency key (`event_id`). +2. Metering writes must be append-only and immutable. +3. Usage rows must capture: + - product + - feature + - customer_id + - usage_count + - cost_unit + - total_cost + - billing_period + - source_event_id +4. Rollups must be deterministic and replayable from raw metering rows. +5. Duplicate source events must be ignored via a unique constraint on `source_event_id`. + +## Validation Checklist +- [ ] API ingests metering with `usage_count` and calculates `total_cost = usage_count * cost_unit`. +- [ ] Database enforces unique `source_event_id`. +- [ ] Rollup job aggregates from raw usage rows, not from ad hoc counters. +- [ ] Admin endpoint exists to trigger rollup/invoice generation manually. +- [ ] Test coverage includes duplicate-event idempotency. diff --git a/RULEBOOK.md b/RULEBOOK.md new file mode 100644 index 0000000..473ce47 --- /dev/null +++ b/RULEBOOK.md @@ -0,0 +1,48 @@ +# Engineering Rulebook + +Rulebook version: `v1.1.0` +Managed by: `dev-bootstrap` + +## Mandatory Rules + +1. Do not create duplicate APIs. +2. Do not create duplicate business logic in multiple files/services without explicit approval. +3. Reuse existing modules before adding new ones. +4. Every new endpoint must be preceded by a duplication check. +5. Every new shared utility must be preceded by a duplication check. + +## Duplicate API Prevention Standard + +Before adding an endpoint: + +1. Search existing routes: +```bash +rg -n "app\\.(get|post|put|patch|delete)|router\\.(get|post|put|patch|delete)|route\\(" src +``` +2. Search API docs/spec: +```bash +rg -n "GET |POST |PUT |PATCH |DELETE " API.md docs +``` +3. If an endpoint exists with overlapping purpose, extend it instead of creating a sibling API. +4. If a new endpoint is required, document why existing APIs cannot be extended. + +## Duplicate Code Prevention Standard + +Before adding new logic: + +1. Search for similar names/behavior: +```bash +rg -n "||" src +``` +2. Prefer extracting shared logic into one reusable module. +3. If intentional duplication is unavoidable (e.g., hard boundary constraints), add a short justification comment and link issue/ADR. + +## PR Checklist (Required) + +- [ ] I searched for existing API endpoints and confirmed no duplicate contract is introduced. +- [ ] I searched for existing logic and reused/extended it where possible. +- [ ] If duplication remains, I documented explicit justification. + +## Sync Policy + +`RULEBOOK.md` is a managed file and may be overwritten by org sync tooling. diff --git a/VERSIONING.md b/VERSIONING.md new file mode 100644 index 0000000..0f26da4 --- /dev/null +++ b/VERSIONING.md @@ -0,0 +1,24 @@ +# Versioning Standard + +## Goal +Use one environment-aware version format across dev, QA, beta, and production. + +## Format +- `dev`: `X.Y.Z-dev.N` +- `qa`: `X.Y.Z-qa.N` +- `beta`: `X.Y.Z-beta.N` +- `prod`: `X.Y.Z` + +## Commands +```bash +scripts/version.sh dev patch +scripts/version.sh qa patch +scripts/version.sh beta minor +scripts/version.sh prod patch +``` + +## Rules +1. Always bump before deploy. +2. `prod` versions must not include prerelease suffixes. +3. The canonical value is stored in `.version`. +4. If `package.json` exists, its `version` must match `.version`. diff --git a/scripts/verify-managed-rules.sh b/scripts/verify-managed-rules.sh new file mode 100755 index 0000000..b201279 --- /dev/null +++ b/scripts/verify-managed-rules.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +RULEBOOK="$ROOT_DIR/RULEBOOK.md" +AGENTS="$ROOT_DIR/AGENTS.md" +VERSION_FILE="$ROOT_DIR/.managed-rules-version" + +fail() { + echo "[verify-managed-rules][error] $*" >&2 + exit 1 +} + +[[ -f "$RULEBOOK" ]] || fail "Missing RULEBOOK.md" +[[ -f "$AGENTS" ]] || fail "Missing AGENTS.md" +[[ -f "$VERSION_FILE" ]] || fail "Missing .managed-rules-version" + +EXPECTED_VERSION="$(cat "$VERSION_FILE" | tr -d '[:space:]')" +[[ -n "$EXPECTED_VERSION" ]] || fail "Empty .managed-rules-version" + +grep -q "Rulebook version: \`$EXPECTED_VERSION\`" "$RULEBOOK" || \ + fail "RULEBOOK.md version mismatch. Expected $EXPECTED_VERSION" + +grep -q "Managed by: \`dev-bootstrap\`" "$RULEBOOK" || \ + fail "RULEBOOK.md is not marked as managed by dev-bootstrap" + +grep -q "Follow managed \[RULEBOOK.md\]" "$AGENTS" || \ + fail "AGENTS.md missing required managed rulebook reference" + +echo "[verify-managed-rules] OK ($EXPECTED_VERSION)" diff --git a/scripts/version.sh b/scripts/version.sh new file mode 100755 index 0000000..28d9084 --- /dev/null +++ b/scripts/version.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Environment-aware semantic versioning helper. +# Stages: +# - dev => X.Y.Z-dev.N +# - qa => X.Y.Z-qa.N +# - beta => X.Y.Z-beta.N +# - prod => X.Y.Z + +STAGE="${1:-}" +BUMP="${2:-patch}" +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +VERSION_FILE="$ROOT_DIR/.version" +PACKAGE_JSON="$ROOT_DIR/package.json" + +usage() { + cat < [patch|minor|major] +USAGE +} + +if [[ -z "$STAGE" ]]; then + usage + exit 1 +fi + +if [[ "$STAGE" != "dev" && "$STAGE" != "qa" && "$STAGE" != "beta" && "$STAGE" != "prod" ]]; then + echo "Invalid stage: $STAGE" >&2 + usage + exit 1 +fi + +if [[ "$BUMP" != "patch" && "$BUMP" != "minor" && "$BUMP" != "major" ]]; then + echo "Invalid bump: $BUMP" >&2 + usage + exit 1 +fi + +read_version() { + if [[ -f "$VERSION_FILE" ]]; then + cat "$VERSION_FILE" + return + fi + if [[ -f "$PACKAGE_JSON" ]]; then + node -e "console.log(require('$PACKAGE_JSON').version || '0.1.0-dev.1')" + return + fi + echo "0.1.0-dev.1" +} + +CURRENT="$(read_version)" +BASE="$(echo "$CURRENT" | sed -E 's/-.*$//')" +MAJOR="$(echo "$BASE" | cut -d. -f1)" +MINOR="$(echo "$BASE" | cut -d. -f2)" +PATCH="$(echo "$BASE" | cut -d. -f3)" + +case "$BUMP" in + major) + MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;; + minor) + MINOR=$((MINOR + 1)); PATCH=0 ;; + patch) + PATCH=$((PATCH + 1)) ;; +esac + +NEXT_BASE="${MAJOR}.${MINOR}.${PATCH}" +NEXT_VERSION="$NEXT_BASE" + +if [[ "$STAGE" != "prod" ]]; then + PREV_STAGE="" + PREV_N=0 + if echo "$CURRENT" | grep -Eq -- '-(dev|qa|beta)\.[0-9]+$'; then + PREV_STAGE="$(echo "$CURRENT" | sed -E 's/^.*-(dev|qa|beta)\.[0-9]+$/\1/')" + PREV_N="$(echo "$CURRENT" | sed -E 's/^.*-(dev|qa|beta)\.([0-9]+)$/\2/')" + fi + if [[ "$PREV_STAGE" == "$STAGE" ]]; then + NEXT_VERSION="${NEXT_BASE}-${STAGE}.$((PREV_N + 1))" + else + NEXT_VERSION="${NEXT_BASE}-${STAGE}.1" + fi +fi + +echo "$NEXT_VERSION" > "$VERSION_FILE" + +if [[ -f "$PACKAGE_JSON" ]]; then + node -e "const fs=require('fs');const p='$PACKAGE_JSON';const j=JSON.parse(fs.readFileSync(p,'utf8'));j.version='$NEXT_VERSION';fs.writeFileSync(p, JSON.stringify(j,null,2)+'\n');" +fi + +echo "Updated version: $CURRENT -> $NEXT_VERSION"