Compare commits

...

4 Commits

Author SHA1 Message Date
5c7736db98 docs(governance): standardize documentation across polyrepo
- CLAUDE.md: repo-specific tech stack, commands, deps (points to root)
- LICENSE: MIT 2026 svrnty (standardized)
- CONTRIBUTING.md: unified workflow, correct co-author email
- SECURITY.md: unified vulnerability reporting policy
- CHANGELOG.md: Keep a Changelog template (if new)
- lefthook.yml: added doc-hygiene hook, improved bootstrap

Co-Authored-By: Svrnty Inc. <jp@svrnty.io>
2026-03-08 12:01:24 -04:00
41eb5b97cb chore: add bootstrap-siblings post-commit hook
Co-Authored-By: Svrnty Inc. <jp@svrnty.io>
2026-03-08 11:32:15 -04:00
c7d9228a88 chore: add post-commit repo registry hook
Co-Authored-By: Svrnty Inc. <jp@svrnty.io>
2026-03-08 11:29:59 -04:00
313b8c83ea chore: standardize CLAUDE.md and lefthook hooks
Co-Authored-By: Svrnty Inc. <jp@svrnty.io>
2026-03-08 11:22:05 -04:00
5 changed files with 166 additions and 70 deletions

102
CLAUDE.md
View File

@ -1,79 +1,47 @@
# Development Guidelines
Universal development practices for all Svrnty/a-gent repositories.
> **Source of truth**: All engineering principles, commit rules, documentation standards, and governance policies are defined in the [root CLAUDE.md](../CLAUDE.md). This file contains repo-specific notes only.
## Engineering Principles
## Quick Reference
### KISS (Keep It Simple)
- Prefer straightforward control flow over clever meta-programming
- Keep error paths obvious and localized
- **Branch**: `JP` for active development
- **Commit format**: `type(scope): message`
- **Co-Author**: `Co-Authored-By: Svrnty Inc. <jp@svrnty.io>`
- **Hooks**: `lefthook install` — enforces author, secrets, doc hygiene
- **Docs required**: README.md, CHANGELOG.md, LICENSE, CONTRIBUTING.md, SECURITY.md
### YAGNI (You Aren't Gonna Need It)
- Do not add features without concrete accepted use case
- Do not introduce speculative abstractions
## Tech Stack
### DRY + Rule of Three
- Duplicate small logic when it preserves clarity
- Extract shared utilities only after 3+ repeated patterns
| Tool | Version |
|------|---------|
| C# | 14 |
| .NET | 10.0 |
| AOT | enabled (IsAotCompatible=true) |
| Nullable | enabled |
### SRP + ISP (Single Responsibility + Interface Segregation)
- Keep each module focused on one concern
- Avoid "god modules" mixing multiple responsibilities
## Commands
### Fail Fast + Explicit Errors
- Prefer explicit errors for unsupported states
- Document fallback behavior when intentional
| Command | Description |
|---------|-------------|
| `dotnet build` | Build all 18 projects |
| `dotnet test` | Run tests |
| `dotnet format` | Format code |
### Secure by Default
- Deny-by-default for access boundaries
- Never log secrets or sensitive payloads
## Key Dependencies
## Agent Workflow
| Package | Description |
|---------|-------------|
| Svrnty.CQRS.Core | Core CQRS abstractions |
| Svrnty.CQRS.DynamicQuery | Dynamic query support |
| Svrnty.CQRS.gRPC | gRPC transport |
| Svrnty.CQRS.Events | Event sourcing |
| Svrnty.CQRS.Sagas | Saga orchestration |
| Svrnty.CQRS.Notifications | Notification handlers |
| Svrnty.CQRS.MinimalApi | Minimal API bindings |
1. **Read before write** - Inspect existing code before editing
2. **Define scope** - One concern per PR
3. **Implement minimal patch** - Apply KISS/YAGNI explicitly
4. **Validate** - Run lint, format, test before commit
5. **Document impact** - Note behavior changes and risks
## Repo-Specific Notes
## Branch Strategy
- All repos use `JP` branch for active development
- Always verify current branch before committing
## Commit Rules
All AI-authored commits MUST use:
```
Co-Authored-By: Svrnty Inc. <eng@svrnty.com>
```
NEVER use third-party AI tool/company names in commits.
## PR Discipline
- Clear, scoped commit messages
- Small PRs preferred
- Never commit personal/sensitive data
- Reference related repos for cross-repo changes
## Validation
Run before every commit:
- Format check (language-specific)
- Lint check (language-specific)
- Test suite
## Anti-Patterns
- Do NOT add heavy dependencies for minor convenience
- Do NOT mix formatting-only with functional changes
- Do NOT modify unrelated modules "while here"
- Do NOT bypass failing checks without explanation
## Cross-Repo Changes
When changes span multiple repos:
1. Commit to each repo separately
2. Reference related repo in commit message
3. Run tests in each affected repo before commit
- Solution file: `Svrnty.CQRS.sln` with 18 projects.
- Lint is handled by .NET analyzers — AOT compatibility and nullable reference types are enforced.
- No Docker or proto files in this repo.
- Published under the `svrnty` org (git.openharbor.io/svrnty), not `a-gent`.

View File

@ -37,7 +37,7 @@ See [CLAUDE.md](./CLAUDE.md) for development practices, engineering principles,
AI-authored commits must include:
```
Co-Authored-By: Svrnty Inc. <eng@svrnty.com>
Co-Authored-By: Svrnty Inc. <jp@svrnty.io>
```
6. **Push & Create PR**

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2021 Powered Softwares Inc.
Copyright (c) 2026 svrnty
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -50,4 +50,3 @@ Security updates are provided for the latest release only.
|---------|-----------|
| Latest | Yes |
| Older | No |

129
lefthook.yml Normal file
View File

@ -0,0 +1,129 @@
pre-commit:
parallel: true
commands:
check-author:
run: |
EMAIL=$(git config user.email)
ALLOWED="jp@svrnty.io mathias@svrnty.io"
for a in $ALLOWED; do
[ "$EMAIL" = "$a" ] && exit 0
done
echo "BLOCKED: author email '$EMAIL' not in allowed list: $ALLOWED"
exit 1
no-secrets:
run: |
BLOCKED=$(git diff --cached --name-only | grep -E '\.(env|pem|key)$|credentials\.json|id_rsa|id_ed25519' || true)
if [ -n "$BLOCKED" ]; then
echo "BLOCKED: refusing to commit sensitive files:"
echo "$BLOCKED"
exit 1
fi
no-large-files:
run: |
LARGE=$(git diff --cached --name-only -z | xargs -0 -I{} sh -c 'if [ -f "{}" ]; then size=$(wc -c < "{}"); if [ "$size" -gt 5242880 ]; then echo "{} ($(( size / 1048576 ))MB)"; fi; fi' || true)
if [ -n "$LARGE" ]; then
echo "WARNING: large files staged (>5MB):"
echo "$LARGE"
fi
doc-hygiene:
run: |
STAGED=$(git diff --cached --name-only)
# Check if code files are staged (not just docs)
CODE_CHANGED=$(echo "$STAGED" | grep -vE '\.(md|txt|yml|yaml|json|toml|lock)$|^LICENSE$|^\.gitignore$' || true)
if [ -z "$CODE_CHANGED" ]; then
exit 0
fi
# Warn if CHANGELOG.md is not being updated with code changes
if ! echo "$STAGED" | grep -q '^CHANGELOG.md$'; then
echo "WARNING: code changes staged without CHANGELOG.md update"
echo " → Update CHANGELOG.md under [Unreleased] before committing"
echo " → See root CLAUDE.md § Documentation Standards for format"
fi
# Warn if README.md is missing
if [ ! -f "README.md" ]; then
echo "WARNING: README.md is missing — every repo must have one"
echo " → See root CLAUDE.md § README Requirements for structure"
fi
commit-msg:
commands:
validate-message:
run: |
MSG=$(cat "{1}")
if echo "$MSG" | head -1 | grep -qE '^Merge '; then
exit 0
fi
if ! echo "$MSG" | head -1 | grep -qE '^[a-z]+(\([a-zA-Z0-9_-]+\))?: .+'; then
echo "WARNING: commit message does not follow conventional format: type(scope): message"
echo " → Types: feat, fix, refactor, docs, test, chore, ci, perf"
fi
append-coauthor:
run: |
MSG=$(cat "{1}")
if ! echo "$MSG" | grep -qF 'Co-Authored-By: Svrnty Inc. <jp@svrnty.io>'; then
printf '\n\nCo-Authored-By: Svrnty Inc. <jp@svrnty.io>\n' >> "{1}"
fi
post-commit:
commands:
register-repo:
run: |
REPO_NAME=$(basename "$(git rev-parse --show-toplevel)")
ROOT_CLAUDE="$(git rev-parse --show-toplevel)/../CLAUDE.md"
[ -f "$ROOT_CLAUDE" ] || exit 0
if grep -qF "| \`$REPO_NAME\`" "$ROOT_CLAUDE"; then
exit 0
fi
COMMIT=$(git rev-parse --short HEAD)
DATE=$(date +%Y-%m-%d)
TOTAL_LINE=$(grep -n '^\*\*Total:' "$ROOT_CLAUDE" | head -1 | cut -d: -f1)
if [ -z "$TOTAL_LINE" ]; then
exit 0
fi
OLD_COUNT=$(sed -n "${TOTAL_LINE}p" "$ROOT_CLAUDE" | grep -oP '\d+')
NEW_COUNT=$((OLD_COUNT + 1))
sed -i "${TOTAL_LINE}i| \`${REPO_NAME}\` | — | NEW REPO — registered ${DATE} (${COMMIT}). Update stack and purpose. |" "$ROOT_CLAUDE"
NEW_TOTAL_LINE=$((TOTAL_LINE + 1))
sed -i "${NEW_TOTAL_LINE}s/Total: ${OLD_COUNT}/Total: ${NEW_COUNT}/" "$ROOT_CLAUDE"
echo "REGISTRY: added '$REPO_NAME' to root CLAUDE.md (${DATE}, ${COMMIT})"
bootstrap-siblings:
run: |
REPO_ROOT=$(git rev-parse --show-toplevel)
HOOKS_DIR="$REPO_ROOT/../.svrnty-hooks"
[ -d "$HOOKS_DIR" ] || exit 0
[ -f "$HOOKS_DIR/lefthook.yml" ] || exit 0
for sibling in "$REPO_ROOT"/../*/; do
[ -d "$sibling/.git" ] || continue
[ -f "$sibling/lefthook.yml" ] && continue
SNAME=$(basename "$sibling")
# Deploy lefthook
cp "$HOOKS_DIR/lefthook.yml" "$sibling/lefthook.yml"
# Deploy CLAUDE.md
[ -f "$HOOKS_DIR/CLAUDE.md.template" ] && cp "$HOOKS_DIR/CLAUDE.md.template" "$sibling/CLAUDE.md"
# Deploy governance docs
[ -f "$HOOKS_DIR/LICENSE" ] && [ ! -f "$sibling/LICENSE" ] && cp "$HOOKS_DIR/LICENSE" "$sibling/LICENSE"
[ -f "$HOOKS_DIR/CONTRIBUTING.md" ] && [ ! -f "$sibling/CONTRIBUTING.md" ] && cp "$HOOKS_DIR/CONTRIBUTING.md" "$sibling/CONTRIBUTING.md"
[ -f "$HOOKS_DIR/SECURITY.md" ] && [ ! -f "$sibling/SECURITY.md" ] && cp "$HOOKS_DIR/SECURITY.md" "$sibling/SECURITY.md"
[ -f "$HOOKS_DIR/CHANGELOG.md.template" ] && [ ! -f "$sibling/CHANGELOG.md" ] && cp "$HOOKS_DIR/CHANGELOG.md.template" "$sibling/CHANGELOG.md"
# Install lefthook
(cd "$sibling" && lefthook install 2>/dev/null)
echo "BOOTSTRAP: installed lefthook + governance docs in '$SNAME'"
done
pre-push:
commands:
protect-main:
run: |
BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
echo "BLOCKED: direct push to $BRANCH is not allowed"
exit 1
fi
check-behind-remote:
run: |
git fetch origin 2>/dev/null || true
BRANCH=$(git rev-parse --abbrev-ref HEAD)
BEHIND=$(git rev-list --count HEAD..origin/"$BRANCH" 2>/dev/null || echo 0)
if [ "$BEHIND" -gt 0 ]; then
echo "WARNING: local branch is $BEHIND commit(s) behind origin/$BRANCH"
fi