diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..fbef3cb --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,55 @@ +You are operating inside the SCORE governance overlay. + +This file is runtime-specific glue. +Canonical, runtime-neutral policy should live in AGENTS.md. + +This repository intentionally keeps only SCORE-specific governance contracts and lightweight maintenance rules. +Generic workflow execution (for example Spec Kit, OpenSpec, BMAD, or custom runtime behavior) is inherited externally by adopter repositories. + +## Responsibilities + +1. Preserve issue-first traceability. +2. Preserve SCORE-specific contracts: + - `.github/references/repo-manifest.schema.json` + - `.github/references/agent-card.schema.json` + - `.github/score/repo-manifest.json` +3. Keep maintenance burden low by avoiding broad framework content in this repo. + +## Scope + +- Keep local content focused on SCORE-specific policy and schema contracts. +- Avoid embedding large generic agent/prompt catalogs. +- Prefer placeholders for runtime-specific naming: + - `` + - `` + - `` + +## Artifact Rules + +- Use issue-scoped folders for work artifacts: + - `.stage/ISSUE-/...` +- Never create anonymous stage artifacts at repository root. + +## SDLC Progress Block + +When status tracking is needed, use this block: + +### SDLC Progress -- +- [ ] PLAN (Roadmap) -- Not Started (or Skipped) +- [ ] PLAN (Tech Analysis) -- Not Started (or Skipped) +- [ ] PLAN (Requirements) -- Not Started +- [ ] SETUP -- Not Started (or Skipped) +- [ ] CODE Phase -- Not Started +- [ ] BUILD Phase -- Not Started +- [ ] TEST Phase -- Not Started +- [ ] RELEASE Phase -- Not Started + +Notes: +- Roadmap planning is optional. +- Most single issues go directly to requirements and implementation. + +## Governance Rules + +- Keep policy files concise and deterministic. +- Keep generated or inherited framework artifacts outside this repository, or produce them from a renderer in adopter repos. +- Run markdown hygiene checks before merge. diff --git a/.github/instructions/clean-code.instructions.md b/.github/instructions/clean-code.instructions.md new file mode 100644 index 0000000..eae6ce3 --- /dev/null +++ b/.github/instructions/clean-code.instructions.md @@ -0,0 +1,93 @@ +--- +applyTo: '**' +--- + +# Clean Code Guidelines (All Languages) + +## Goal +Code is *extremely readable*, composed of *very small and focused* methods/functions, and avoids all code smells. + +## General Principles +- Code is for **humans first**, computers second +- Express intent clearly -- self-explanatory names for variables, methods, classes +- Prefer self-documenting code; comments as last resort +- **Small is beautiful** -- small, focused methods and classes +- Duplication is a bad sign -- extract and reuse +- KISS -- reduce complexity as much as possible +- YAGNI -- avoid over-engineering +- Boy Scout Rule -- leave the codebase cleaner than you found it +- Tell, Don't Ask -- promote loose coupling +- Be Consistent -- follow existing conventions +- Encapsulate boundary conditions in one place +- Avoid negative conditionals +- Use dependency injection + +## SOLID Principles +- **SRP**: Each method/class does one thing only +- **OCP**: Open for extension, closed for modification +- **LSP**: Subtypes substitutable for base types +- **ISP**: Small, focused interfaces +- **DIP**: Depend on abstractions, not concretions + +## Code Smells to Remove +Long Method, Large Class, Primitive Obsession, Long Parameter List (max 3), Data Clumps, Switch Statements, Temporary Field, Divergent Change, Shotgun Surgery, Duplicated Code, Dead Code, Feature Envy, Middle Man, Magic Numbers (replace with named constants). + +## Class Design +- Small: max ~7 fields, ~3-5 public methods +- Single clear purpose aligned with domain +- Domain-focused naming (e.g. `PolicyRenewalService`, not `HelperUtil`) +- Encapsulation -- hide internal structure +- Prefer immutability, composition over inheritance +- Follow Law of Demeter +- Prefer value objects over primitives +- Avoid God objects and util classes + +## Method Design +- Ideal length: ~3 lines, rarely more than 5 +- Single atomic step of logic +- Names describe *what* not *how* +- No side effects +- Use guard clauses / early returns +- Avoid nested ifs/loops +- No flag arguments -- split into separate methods +- Extract complex predicates into named boolean methods + +## Test Design +- Small, specific, isolated, fast, independent, repeatable +- Arrange-Act-Assert format +- Max 3 assertions per test +- Always test new public behavior +- At least one negative test per API +- Avoid duplicated test data -- extract to class level + +## Naming +- Descriptive and unambiguous +- Methods: verbs. Classes: nouns. Variables: clear names +- Pronounceable, searchable, no encodings + +## Comments +- Explain *why*, not *what* +- Document assumptions, invariants, edge cases +- Never comment out code -- just remove it + +## Error Handling +- Use domain-specific custom exceptions +- Handle exceptions gracefully; never swallow them +- Externalize user-facing messages +- Maintain ErrorCode mapping + +## Security +- No hardcoded secrets, URLs, or sensitive info +- All sensitive config resolved via environment or config server +- PII protection: data masking, tokenization + +## Performance +- Avoid premature micro-optimizations unless profiled +- Batch operations in single transactions +- Refactor nested loops into indexed maps (O(n+m)) + +## API Design +- RESTful conventions with domain-driven design +- Plural noun resources: `/v1/templates` +- Accept filtering & expansion parameters +- Consistent resource naming diff --git a/.github/instructions/coding-style.instructions.md b/.github/instructions/coding-style.instructions.md new file mode 100644 index 0000000..37a5c33 --- /dev/null +++ b/.github/instructions/coding-style.instructions.md @@ -0,0 +1,65 @@ +--- +applyTo: '**' +--- + +# Coding Style + +## Immutability (CRITICAL) + +ALWAYS create new objects, NEVER mutate existing ones: +- Return new instances rather than modifying in place +- Use spread operators, `List.copyOf()`, `Map.copyOf()`, or equivalent +- Mark fields `final` / `readonly` / `const` by default + +Rationale: Immutable data prevents hidden side effects, makes debugging easier, and enables safe concurrency. + +## File Organization + +MANY SMALL FILES > FEW LARGE FILES: +- High cohesion, low coupling +- 200-400 lines typical, 800 max +- Extract utilities from large modules +- Organize by feature/domain, not by type + +## Function Size + +- Ideal: 3-10 lines, rarely more than 20 +- Max: 50 lines — split if exceeded +- Single atomic step of logic per function +- No flag arguments — split into separate functions + +## Nesting + +- Max 4 levels of nesting +- Use guard clauses and early returns to flatten +- Extract complex predicates into named boolean methods +- Extract inner loops into helper functions + +## Error Handling + +ALWAYS handle errors comprehensively: +- Handle errors explicitly at every level +- Provide user-friendly error messages in UI-facing code +- Log detailed error context on the server side +- Never silently swallow errors + +## Input Validation + +ALWAYS validate at system boundaries: +- Validate all user input before processing +- Use schema-based validation where available +- Fail fast with clear error messages +- Never trust external data (API responses, user input, file content) + +## Code Quality Checklist + +Before marking work complete: +- [ ] Code is readable and well-named +- [ ] Functions are small (<50 lines) +- [ ] Files are focused (<800 lines) +- [ ] No deep nesting (>4 levels) +- [ ] Proper error handling at every level +- [ ] No hardcoded values (use constants or config) +- [ ] No mutation (immutable patterns used) +- [ ] No `console.log` / `System.out.println` in production code +- [ ] No TODO/FIXME without a linked issue diff --git a/.github/instructions/git-workflow.instructions.md b/.github/instructions/git-workflow.instructions.md new file mode 100644 index 0000000..c66fced --- /dev/null +++ b/.github/instructions/git-workflow.instructions.md @@ -0,0 +1,51 @@ +--- +applyTo: '**' +--- + +# Git Workflow + +## Commit Message Format +``` +: + + + +> +``` + +### Types +`feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `perf`, `ci`, `style` + +### Rules +- Use imperative mood: "add feature" not "added feature" +- Keep subject line under 72 characters +- One logical change per commit +- Run `gitlint` locally before pushing + +## Branch Naming +Format: `/` + +Examples: +- `feature/add-login` +- `bugfix/fix-null-pointer` +- `hotfix/auth-patch` + +## Pull Request Workflow +1. Analyze full commit history: `git diff ...HEAD` +2. Draft comprehensive PR summary covering what changed and why +3. Include a test plan with verification steps +4. Push with `-u` flag if new branch +5. Request review from at least one peer + +## Pre-Commit Checklist +- [ ] All tests pass locally +- [ ] No lint errors or warnings +- [ ] No `console.log` / `System.out.println` left in production code +- [ ] No TODO/FIXME without a linked issue +- [ ] Commit message follows format above +- [ ] Branch is rebased on latest base branch + +## Merge Strategy +- Use squash merge when all commits are from the same author and represent one topic +- Use rebase/merge commit when commits represent distinct topics or multiple authors +- Preserve clear history and avoid merge commits from `main` into feature branches diff --git a/.github/instructions/python.instructions.md b/.github/instructions/python.instructions.md new file mode 100644 index 0000000..14d9aff --- /dev/null +++ b/.github/instructions/python.instructions.md @@ -0,0 +1,87 @@ +--- +applyTo: '**/*.py' +--- + +# Python Guidelines + +## Project Structure +``` +project-root/ +├── README.md +├── requirements.txt or pyproject.toml +├── src/mypackage/ +│ ├── __init__.py +│ └── module1.py +├── scripts/ +│ └── run_analysis.py +└── tests/ + ├── __init__.py + └── test_module1.py +``` +- Source code in dedicated package directory (`src/mypackage/`) +- Scripts in `scripts/` with `if __name__ == "__main__":` entry points +- Tests in parallel `tests/` directory mirroring code structure + +## Code Style +- Follow PEP 8; use auto-formatting (Black) and linting (ruff/flake8) +- `snake_case` for modules/functions, `CapWords` for classes, `UPPER_SNAKE_CASE` for constants +- Explicit, descriptive names for all identifiers +- Imports at top: standard library, third-party, local -- with blank lines between +- Absolute imports only; no wildcard imports +- Docstrings for all public modules, classes, functions, methods +- Minimize comments -- code must be self-explanatory + +## Function & Parameter Rules +- **Never use mutable default arguments** (lists, dicts) -- use `None` and create inside function +- **No blank strings/lists as defaults** +- **No `.get()` for required dict keys** -- access directly so missing keys raise errors +- Check explicitly for `None` or absence for optional values + +## Error Handling +- Catch specific exception types only -- never bare `except:` +- Use `logging` module, never `print()` +- Log or re-raise errors appropriately + +## Type Hints & Readability +- Use type hints to clarify input/output types +- Small, focused functions (Single Responsibility) +- Prefer comprehensions and built-ins for clarity +- Avoid deep nesting; break logic into helpers + +## Dependencies & Configuration +- Pin all dependency versions in `requirements.txt` or `pyproject.toml` +- Always use virtual environments +- Environment variables or config files for secrets -- never hardcode +- `.gitignore` excludes venvs, caches, non-source artifacts + +## Security +- **No hardcoded secrets** — use environment variables, `.env` files (in `.gitignore`), or secret managers +- **Parameterized queries only** — never use f-strings or `%` formatting for SQL +- **Path traversal prevention** — validate and sanitize all file paths; reject `..` components +- **Input validation** at API boundaries — use Pydantic models or marshmallow schemas +- **No `eval()` or `exec()`** on user input — ever +- **Dependency scanning** — use `pip-audit` or `safety` in CI pipeline +- **No sensitive data in logs** — mask PII, tokens, passwords + +## Data Validation with Pydantic +- Use Pydantic `BaseModel` for request/response validation at API boundaries +- Define strict types with `Field()` constraints (`min_length`, `max_length`, `ge`, `le`) +- Use `@validator` or `@field_validator` for custom validation logic +- Return Pydantic models from service layer for type safety + +## Testing +- **TDD mandatory**: RED → GREEN → REFACTOR for new features and bug fixes +- pytest for all testing +- `test_` prefix for files and functions +- Use pytest fixtures for shared setup +- Never mix tests with production code +- Happy path + edge case tests with clear assertions +- Use `@pytest.mark.parametrize` for data-driven tests +- `unittest.mock` for external dependencies +- Min 80% coverage; 100% for security-critical code +- Use `pytest-cov` for coverage enforcement in CI + +## General +- Prefer standard library; add third-party only for clear benefit +- Validate and sanitize all inputs; no silent failures +- Use context managers (`with`) for resource management diff --git a/.github/instructions/security.instructions.md b/.github/instructions/security.instructions.md new file mode 100644 index 0000000..336eda3 --- /dev/null +++ b/.github/instructions/security.instructions.md @@ -0,0 +1,54 @@ +--- +applyTo: '**' +--- + +# Security Guidelines + +## Mandatory Checks Before Every Commit +- [ ] No hardcoded secrets (API keys, passwords, tokens) +- [ ] All user inputs validated at system boundaries +- [ ] SQL injection prevention (parameterized queries only) +- [ ] XSS prevention (sanitized HTML output) +- [ ] CSRF protection enabled +- [ ] Authentication and authorization verified +- [ ] Rate limiting on public endpoints +- [ ] Error messages do not leak internal details + +## Secret Management +- NEVER hardcode secrets in source code +- Use environment variables or a secret/config manager +- Validate required secrets are present at startup +- Rotate any secrets that may have been exposed +- Keep files with secrets in `.gitignore` + +## Input Validation +- Validate all user input before processing +- Use schema-based validation where available (Bean Validation, Zod, Pydantic) +- Fail fast with clear error messages +- Never trust external data (API responses, user input, file content) + +## Dependency Security +- Audit transitive dependencies regularly +- Use OWASP Dependency-Check, Snyk, or Dependabot for CVE scanning +- Keep dependencies updated with automated tooling +- Pin versions in production deployments + +## Error Responses +- Never expose stack traces in API responses +- Map exceptions to safe, generic client messages at handler boundaries +- Log detailed errors server-side only +- Maintain an ErrorCode mapping for consistent client-facing messages + +## Authentication +- Never implement custom auth crypto — use established libraries +- Store passwords with bcrypt or Argon2 (never MD5/SHA1) +- Enforce authorization checks at service boundaries +- Never log passwords, tokens, or PII + +## Security Response Protocol +If a security issue is found during development: +1. STOP current work immediately +2. Assess severity (Critical / High / Medium / Low) +3. Fix CRITICAL issues before continuing any other work +4. Rotate any potentially exposed secrets +5. Review codebase for similar patterns diff --git a/.github/instructions/testing.instructions.md b/.github/instructions/testing.instructions.md new file mode 100644 index 0000000..fd1726b --- /dev/null +++ b/.github/instructions/testing.instructions.md @@ -0,0 +1,53 @@ +--- +applyTo: '**' +--- + +# Testing Requirements + +## Minimum Coverage: 80% + +100% required for: +- Financial calculations +- Authentication logic +- Security-critical code +- Core business logic + +## Test Types (ALL required for production features) +1. **Unit Tests** — Individual functions, utilities, components +2. **Integration Tests** — API endpoints, database operations, service interactions +3. **System/Scenario Tests** — Critical cross-component flows where applicable + +## Test-Driven Development (TDD) + +Mandatory workflow for new features and bug fixes: +1. **RED** — Write a failing test first +2. **GREEN** — Write minimal implementation to pass +3. **REFACTOR** — Improve code while keeping tests green +4. Repeat for each scenario + +### Rules +- Never write implementation before the test +- Run tests after every change +- Write minimal code to make tests pass +- Refactor only when tests are green + +## Test Structure +- Arrange-Act-Assert (AAA) pattern +- One logical assertion per test (max 3 related assertions) +- Descriptive test names: `methodName_scenario_expectedBehavior` +- Use `@DisplayName` or equivalent for human-readable descriptions + +## Test Quality +- Tests must be independent and isolated +- No shared mutable state between tests +- Fast execution (unit tests < 100ms each) +- Deterministic — no flaky tests +- Fix implementation, not tests (unless tests are wrong) + +## Language-Specific Frameworks +| Language | Unit | Mocking | Integration | Coverage | +|----------|------|---------|-------------|----------| +| C++ | GoogleTest | GoogleMock | Bazel test targets | lcov/gcov | +| Python | pytest | unittest.mock | pytest + service/integration fixtures | pytest-cov | +| Rust | cargo test | mockall (or equivalent) | integration tests in `tests/` | llvm-cov/grcov | +| Go | go test | gomock/testify | package/integration tests | go test -cover | diff --git a/.github/references/agent-card.schema.json b/.github/references/agent-card.schema.json new file mode 100644 index 0000000..a62fd10 --- /dev/null +++ b/.github/references/agent-card.schema.json @@ -0,0 +1,155 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://score.dev/schemas/agent-card.schema.json", + "title": "SCORE Agent Card", + "description": "Structured handoff artifact exchanged between agents and tools for one issue-scoped work item.", + "type": "object", + "additionalProperties": false, + "required": [ + "version", + "issue_id", + "repository", + "goal", + "status", + "summary", + "validation", + "next_action" + ], + "properties": { + "version": { + "const": 1 + }, + "issue_id": { + "type": "string", + "pattern": "^(ISSUE-[0-9]+|POC-[0-9]{8}-[0-9]{4})$" + }, + "repository": { + "type": "string", + "minLength": 1 + }, + "branch": { + "type": "string", + "minLength": 1 + }, + "goal": { + "type": "string", + "minLength": 1 + }, + "status": { + "type": "string", + "enum": [ + "in_progress", + "blocked", + "ready_for_handoff", + "completed" + ] + }, + "summary": { + "type": "string", + "minLength": 1 + }, + "findings": { + "type": "array", + "items": { + "$ref": "#/$defs/note" + }, + "default": [] + }, + "open_questions": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "default": [] + }, + "touched_files": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "uniqueItems": true, + "default": [] + }, + "validation": { + "type": "object", + "additionalProperties": false, + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "enum": [ + "not_run", + "passed", + "failed" + ] + }, + "commands": { + "type": "array", + "items": { + "$ref": "#/$defs/commandResult" + }, + "default": [] + } + } + }, + "trajectory": { + "type": "array", + "items": { + "$ref": "#/$defs/note" + }, + "default": [] + }, + "next_action": { + "type": "string", + "minLength": 1 + } + }, + "$defs": { + "note": { + "type": "object", + "additionalProperties": false, + "required": [ + "title", + "detail" + ], + "properties": { + "title": { + "type": "string", + "minLength": 1 + }, + "detail": { + "type": "string", + "minLength": 1 + } + } + }, + "commandResult": { + "type": "object", + "additionalProperties": false, + "required": [ + "command", + "status" + ], + "properties": { + "command": { + "type": "string", + "minLength": 1 + }, + "status": { + "type": "string", + "enum": [ + "passed", + "failed" + ] + }, + "detail": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/.github/references/assistant-runtime-alignment.md b/.github/references/assistant-runtime-alignment.md new file mode 100644 index 0000000..30b6d09 --- /dev/null +++ b/.github/references/assistant-runtime-alignment.md @@ -0,0 +1,27 @@ +# Assistant Runtime Alignment + +This repository aligns policy for multiple assistant runtimes (Copilot/VS Code, Codex, Claude) without duplicating governance content. + +## Canonical instruction source + +- AGENTS.md is the canonical, runtime-neutral policy document. +- CLAUDE.md imports AGENTS.md for Claude compatibility. +- .github/copilot-instructions.md is runtime-specific glue for Copilot. + +## Plugin alignment model + +- Keep one approved plugin marketplace source for the organization. +- Configure the same marketplace in runtime settings files. +- Enable a common governance plugin set where possible. + +## Repository template integration + +The Copier template distributes: + +- AGENTS.md +- CLAUDE.md +- .github/ +- .claude/settings.json +- .github/copilot/settings.json + +This keeps assistants aligned while preserving runtime-specific entrypoints. diff --git a/.github/references/docs-maintenance.md b/.github/references/docs-maintenance.md new file mode 100644 index 0000000..57e4414 --- /dev/null +++ b/.github/references/docs-maintenance.md @@ -0,0 +1,47 @@ +# Markdown Maintenance Playbook + +This repository uses a low-maintenance governance model. + +## Goals + +- Keep SCORE-specific governance assets concise. +- Avoid local duplication of framework-generic workflow content. +- Detect markdown hygiene issues early. + +## Operating Model + +1. Keep only SCORE-specific contracts and policy in this repository. +2. Inherit generic framework assets in adopter repositories. +3. Use placeholders for runtime-specific naming. +4. Validate markdown health in CI and before merge. + +## Automated Checks + +The script at [scripts/check_markdown_hygiene.py](/scripts/check_markdown_hygiene.py) validates: + +- Duplicate markdown files by content hash. +- Broken local markdown links. + +Run locally: + +```bash +python3 scripts/check_markdown_hygiene.py --root . --include .github --include README.md --include profile +``` + +CI workflow: + +- [.github/workflows/docs-hygiene.yml](/.github/workflows/docs-hygiene.yml) + +## Cadence + +- Pull request: automatic via CI. +- Weekly: scheduled CI run. +- Monthly: remove stale docs and confirm retained files are still SCORE-specific. + +## Adopter Guidance + +When porting to another repository: + +1. Copy this playbook, hygiene script, and workflow. +2. Keep framework-generic assets out of the local overlay. +3. Keep only SCORE-specific deltas and schemas under `.github/references/` and `.github/score/`. diff --git a/.github/references/repo-manifest.schema.json b/.github/references/repo-manifest.schema.json new file mode 100644 index 0000000..ac98596 --- /dev/null +++ b/.github/references/repo-manifest.schema.json @@ -0,0 +1,152 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://score.dev/schemas/repo-manifest.schema.json", + "title": "SCORE Repo Manifest", + "description": "Minimal federated harness contract for a SCORE repository.", + "type": "object", + "additionalProperties": false, + "required": [ + "version", + "repository", + "bootstrap", + "execution", + "mcp" + ], + "properties": { + "version": { + "const": 1 + }, + "repository": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "language", + "visibility" + ], + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "language": { + "type": "string", + "enum": [ + "python", + "go", + "rust", + "cpp", + "typescript", + "mixed", + "other" + ] + }, + "visibility": { + "type": "string", + "enum": [ + "public", + "internal", + "private" + ] + }, + "tags": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "uniqueItems": true, + "default": [] + } + } + }, + "bootstrap": { + "type": "object", + "additionalProperties": false, + "required": [ + "contract_version" + ], + "properties": { + "contract_version": { + "type": "string", + "pattern": "^v[0-9]+\\.[0-9]+\\.[0-9]+$" + }, + "template_version": { + "type": "string", + "pattern": "^v[0-9]+\\.[0-9]+\\.[0-9]+$" + } + } + }, + "execution": { + "type": "object", + "additionalProperties": false, + "required": [ + "build", + "test", + "lint" + ], + "properties": { + "build": { + "$ref": "#/$defs/commandSpec" + }, + "test": { + "$ref": "#/$defs/commandSpec" + }, + "lint": { + "$ref": "#/$defs/commandSpec" + }, + "typecheck": { + "$ref": "#/$defs/commandSpec" + } + } + }, + "mcp": { + "type": "object", + "additionalProperties": false, + "required": [ + "server_name", + "tools" + ], + "properties": { + "server_name": { + "type": "string", + "minLength": 1 + }, + "tools": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "build", + "test", + "lint", + "typecheck", + "search" + ] + }, + "uniqueItems": true, + "minItems": 1 + } + } + } + }, + "$defs": { + "commandSpec": { + "type": "object", + "additionalProperties": false, + "required": [ + "command" + ], + "properties": { + "command": { + "type": "string", + "minLength": 1 + }, + "working_directory": { + "type": "string", + "minLength": 1 + } + } + } + } +} \ No newline at end of file diff --git a/.github/score/repo-manifest.json b/.github/score/repo-manifest.json new file mode 100644 index 0000000..120fdf4 --- /dev/null +++ b/.github/score/repo-manifest.json @@ -0,0 +1,39 @@ +{ + "version": 1, + "repository": { + "name": "score_github", + "language": "python", + "visibility": "public", + "tags": [ + "bootstrap", + "github-profile" + ] + }, + "bootstrap": { + "contract_version": "v0.1.0", + "template_version": "v0.1.0" + }, + "execution": { + "build": { + "command": "uv build" + }, + "test": { + "command": "uv run pytest" + }, + "lint": { + "command": "uv run ruff check src tests scripts" + }, + "typecheck": { + "command": "uv run basedpyright src tests scripts" + } + }, + "mcp": { + "server_name": "score-repo-tools", + "tools": [ + "build", + "test", + "lint", + "typecheck" + ] + } +} \ No newline at end of file diff --git a/.github/workflows/docs-hygiene.yml b/.github/workflows/docs-hygiene.yml new file mode 100644 index 0000000..b332818 --- /dev/null +++ b/.github/workflows/docs-hygiene.yml @@ -0,0 +1,29 @@ +name: Markdown Hygiene + +on: + pull_request: + paths: + - '**/*.md' + - 'scripts/check_markdown_hygiene.py' + - '.github/workflows/docs-hygiene.yml' + schedule: + - cron: '0 6 * * 1' + workflow_dispatch: + +permissions: + contents: read + +jobs: + markdown-hygiene: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Run markdown hygiene checks + run: python scripts/check_markdown_hygiene.py --root . --include .github --include README.md --include profile diff --git a/.gitignore b/.gitignore index a3b98d6..47501b3 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,8 @@ __pycache__/ /profile/cache/reference_integration_checkout/ /profile/cache/repo_overview.json /_site/ + +# Exported Copilot chat transcripts +/dialog*.md +/copilot-dialog*.md +/chat-transcript*.md diff --git a/README.md b/README.md index c53fcfd..c74adcd 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,13 @@ This repository hosts the start page when you visit the eclipse-score GitHub organization. It contains links to the Eclipse Score website, documentation, and other resources related to the Eclipse Score project. The Python tool in this repo now acts as a small repo-overview generator: it collects a cached snapshot of organization metadata once, then renders multiple Markdown views from that shared snapshot. +This repository also maintains SCORE-specific governance contracts. ## Development -Use `uv` to create a virtual environment and install the project dependencies: +Use `uv` to create a virtual environment and install dependencies: -``` +```bash uv sync --all-groups ``` @@ -67,3 +68,86 @@ To run the local checks: ```sh uv run pre-commit run --all-files ``` + +Run markdown hygiene checks: + +```bash +python3 scripts/check_markdown_hygiene.py --root . --include .github --include README.md --include profile +``` + +Maintenance playbook: [.github/references/docs-maintenance.md](.github/references/docs-maintenance.md) + +## Adopting the SCORE Governance Overlay + +This repository is a [Copier](https://copier.readthedocs.io/) template. Module repositories apply it once and pull updates with a single command. + +### Apply to a new module repo + +```bash +# Install Copier once +uv tool install copier + +# Apply SCORE overlay into your repo +cd path/to/your-module-repo +copier copy https://github.com/eclipse-score/.github . +``` + +You will be prompted for: +- Repository name +- Primary language (C++, Rust, Python, Go, Other) +- Build / test / lint commands +- AI assistant instructions filename (default: `copilot-instructions.md` for Copilot, or your runtime's equivalent) + +### Pull SCORE governance updates + +```bash +# From inside any adopter repo +copier update +``` + +This re-applies only SCORE-managed files, preserving your repo-local changes. +The `.github/score/.copier-answers.yml` file (written on first `copy`) records the template source and answers, enabling `copier update` to work without re-entering values. + +> **Note:** `copier update` requires the template to have at least one git tag. +> Governance updates to this repo must be tagged to be picked up by adopters. + +### What gets distributed + +| File | Varies per repo? | +|------|-----------------| +| `AGENTS.md` | No — canonical runtime-neutral policy | +| `CLAUDE.md` | No — imports AGENTS.md + Claude-specific notes | +| `.github/` | No — static SCORE policy | +| `.claude/settings.json` | No — Claude plugin marketplace recommendations | +| `.github/copilot/settings.json` | No — VS Code/Copilot plugin marketplace recommendations | +| `.github/instructions/*.md` | No — coding standards | +| `.github/references/assistant-runtime-alignment.md` | No — multi-runtime alignment guidance | +| `.github/references/*.schema.json` | No — contract schemas | +| `.github/workflows/docs-hygiene.yml` | No — CI checks | +| `scripts/check_markdown_hygiene.py` | No — hygiene tooling | +| `.github/score/repo-manifest.json` | **Yes** — generated from answers | + +### Cross-assistant alignment (Copilot, Codex, Claude) + +The template follows a single-source model to avoid drift across assistant runtimes: + +1. `AGENTS.md` is the canonical, runtime-neutral governance policy. +2. `CLAUDE.md` imports `AGENTS.md` (supported Claude pattern) and carries optional Claude-only notes. +3. `.github/` is runtime-specific glue for assistants that use a dedicated instructions filename. + +This structure aligns with current guidance: +- VS Code agent plugin format supports cross-tool compatibility and shared plugin structures. +- Codex reads layered `AGENTS.md` files directly. +- Claude reads `CLAUDE.md` and supports importing `@AGENTS.md` to avoid duplicated policy. + +### What you own in your module repo + +- Workflow assets (Spec Kit, OpenSpec, BMAD, or custom) — NOT in this overlay. +- Repo-specific `.github/score/repo-manifest.json` values. +- Any repo-local `.github/instructions/` overrides (stacked on top). + +### Why this model + +- Maintenance stays in one place — this repo. +- Adopter repos stay current with `copier update`. +- No large agent/prompt/skill catalogs to maintain per-repo. diff --git a/copier.yml b/copier.yml new file mode 100644 index 0000000..c9fe80b --- /dev/null +++ b/copier.yml @@ -0,0 +1,64 @@ +# SCORE governance overlay — Copier template configuration +# Adopter repos run: +# copier copy gh:eclipse-score/.github path/to/repo +# copier update (from inside the adopter repo to pull latest SCORE changes) + +_subdirectory: template +_answers_file: .github/score/.copier-answers.yml + +_message_after_copy: | + SCORE governance overlay applied. + + Next steps: + 1. Review AGENTS.md as the canonical cross-assistant policy. + 2. If needed, add assistant-specific notes in CLAUDE.md or .github/. + 3. Review .github/score/repo-manifest.json — update build/test/lint commands. + 4. Commit the generated files. + 5. Run: python3 scripts/check_markdown_hygiene.py --root . --include .github + 6. To update SCORE governance in future: copier update + +# ── Questions ──────────────────────────────────────────────────────────────── + +repo_name: + type: str + help: Repository name (used in repo-manifest.json, e.g. score-baselibc) + +repo_language: + type: str + help: Primary language + choices: + - C++ + - Rust + - Python + - Go + - Other + +repo_visibility: + type: str + help: Repository visibility + default: public + choices: + - public + - private + +build_command: + type: str + help: Build command (e.g. "bazel build //...") + default: "bazel build //..." + +test_command: + type: str + help: Test command (e.g. "bazel test //...") + default: "bazel test //..." + +lint_command: + type: str + help: Lint command (e.g. "bazel run //:lint") + default: "bazel run //:lint" + +assistant_instructions_file: + type: str + help: > + Filename your AI assistant loads as instructions + (Copilot commonly uses copilot-instructions.md; other runtimes may use a different name) + default: copilot-instructions.md diff --git a/pyproject.toml b/pyproject.toml index 7020eb8..314d83c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ generate-repo-overview = "generate_repo_overview.cli:main" [dependency-groups] dev = [ "basedpyright", + "copier", "pre-commit", "pytest", "ruff", diff --git a/scripts/check_markdown_hygiene.py b/scripts/check_markdown_hygiene.py new file mode 100644 index 0000000..7b42c23 --- /dev/null +++ b/scripts/check_markdown_hygiene.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +"""Lightweight markdown hygiene checks for repository-scale governance docs. + +Checks: +- Duplicate markdown files by content hash +- Broken local markdown links (relative repo paths) + +Exit codes: +- 0: no issues +- 1: one or more issues found +""" + +from __future__ import annotations + +import argparse +import hashlib +import re +import sys +from pathlib import Path +from typing import Iterable + +MARKDOWN_LINK_RE = re.compile(r"\[[^\]]+\]\(([^)]+)\)") +DEFAULT_EXCLUDED_DIRS = {".git", ".venv", "venv", "node_modules", ".mypy_cache", ".pytest_cache"} + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Check markdown hygiene in a repository") + parser.add_argument("--root", default=".", help="Repository root path") + parser.add_argument( + "--include", + action="append", + default=[".github", "README.md", "profile"], + help="Path (file/dir) under root to include; repeatable", + ) + return parser.parse_args() + + +def to_repo_relative(path: Path, root: Path) -> str: + return path.relative_to(root).as_posix() + + +def gather_markdown_files(root: Path, include_paths: Iterable[str]) -> list[Path]: + files: list[Path] = [] + for include in include_paths: + candidate = (root / include).resolve() + if not candidate.exists(): + continue + if candidate.is_file() and candidate.suffix.lower() == ".md": + files.append(candidate) + continue + if candidate.is_dir(): + for path in candidate.rglob("*.md"): + if any(part in DEFAULT_EXCLUDED_DIRS for part in path.parts): + continue + files.append(path.resolve()) + unique = sorted(set(files)) + return unique + + +def sha256_of(path: Path) -> str: + digest = hashlib.sha256() + digest.update(path.read_bytes()) + return digest.hexdigest() + + +def find_duplicate_markdown(files: list[Path]) -> list[list[Path]]: + by_hash: dict[str, list[Path]] = {} + for path in files: + file_hash = sha256_of(path) + by_hash.setdefault(file_hash, []).append(path) + return [group for group in by_hash.values() if len(group) > 1] + + +def strip_anchor_and_query(target: str) -> str: + no_anchor = target.split("#", 1)[0] + no_query = no_anchor.split("?", 1)[0] + return no_query + + +def is_external_link(target: str) -> bool: + lowered = target.lower() + return lowered.startswith(("http://", "https://", "mailto:", "tel:")) + + +def find_broken_local_links(files: list[Path], root: Path) -> list[tuple[Path, str, str]]: + issues: list[tuple[Path, str, str]] = [] + for markdown_file in files: + text = markdown_file.read_text(encoding="utf-8") + for raw_target in MARKDOWN_LINK_RE.findall(text): + target = raw_target.strip() + if not target or target.startswith("#") or is_external_link(target): + continue + + # Template placeholders are examples, not resolvable links. + if "{" in target or "}" in target: + continue + + normalized = strip_anchor_and_query(target) + if not normalized: + continue + + # Absolute repo path style: /path/from/repo/root + if normalized.startswith("/"): + resolved = (root / normalized.lstrip("/")).resolve() + else: + resolved = (markdown_file.parent / normalized).resolve() + + if not resolved.exists(): + issues.append((markdown_file, target, to_repo_relative(markdown_file, root))) + return issues + + +def print_duplicate_report(duplicates: list[list[Path]], root: Path) -> None: + if not duplicates: + print("No duplicate markdown files detected.") + return + print("Duplicate markdown files detected:") + for group in duplicates: + print("- Duplicate group:") + for path in group: + print(f" - {to_repo_relative(path, root)}") + + +def print_broken_link_report(broken: list[tuple[Path, str, str]], root: Path) -> None: + if not broken: + print("No broken local markdown links detected.") + return + print("Broken local markdown links detected:") + for markdown_file, target, _ in broken: + rel = to_repo_relative(markdown_file, root) + print(f"- {rel}: {target}") + + +def main() -> int: + args = parse_args() + root = Path(args.root).resolve() + + files = gather_markdown_files(root, args.include) + if not files: + print("No markdown files found for the configured include paths.") + return 0 + + duplicates = find_duplicate_markdown(files) + broken_links = find_broken_local_links(files, root) + + print(f"Scanned {len(files)} markdown files.") + print_duplicate_report(duplicates, root) + print_broken_link_report(broken_links, root) + + has_issues = bool(duplicates or broken_links) + if has_issues: + print("Markdown hygiene check failed.") + return 1 + + print("Markdown hygiene check passed.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/template/.claude/settings.json b/template/.claude/settings.json new file mode 100644 index 0000000..ab15a38 --- /dev/null +++ b/template/.claude/settings.json @@ -0,0 +1,13 @@ +{ + "extraKnownMarketplaces": { + "score-agent-plugins": { + "source": { + "source": "github", + "repo": "eclipse-score/agent-plugins" + } + } + }, + "enabledPlugins": { + "score-governance@score-agent-plugins": true + } +} diff --git a/template/.github/copilot/settings.json b/template/.github/copilot/settings.json new file mode 100644 index 0000000..ab15a38 --- /dev/null +++ b/template/.github/copilot/settings.json @@ -0,0 +1,13 @@ +{ + "extraKnownMarketplaces": { + "score-agent-plugins": { + "source": { + "source": "github", + "repo": "eclipse-score/agent-plugins" + } + } + }, + "enabledPlugins": { + "score-governance@score-agent-plugins": true + } +} diff --git a/template/.github/instructions/clean-code.instructions.md b/template/.github/instructions/clean-code.instructions.md new file mode 100644 index 0000000..eae6ce3 --- /dev/null +++ b/template/.github/instructions/clean-code.instructions.md @@ -0,0 +1,93 @@ +--- +applyTo: '**' +--- + +# Clean Code Guidelines (All Languages) + +## Goal +Code is *extremely readable*, composed of *very small and focused* methods/functions, and avoids all code smells. + +## General Principles +- Code is for **humans first**, computers second +- Express intent clearly -- self-explanatory names for variables, methods, classes +- Prefer self-documenting code; comments as last resort +- **Small is beautiful** -- small, focused methods and classes +- Duplication is a bad sign -- extract and reuse +- KISS -- reduce complexity as much as possible +- YAGNI -- avoid over-engineering +- Boy Scout Rule -- leave the codebase cleaner than you found it +- Tell, Don't Ask -- promote loose coupling +- Be Consistent -- follow existing conventions +- Encapsulate boundary conditions in one place +- Avoid negative conditionals +- Use dependency injection + +## SOLID Principles +- **SRP**: Each method/class does one thing only +- **OCP**: Open for extension, closed for modification +- **LSP**: Subtypes substitutable for base types +- **ISP**: Small, focused interfaces +- **DIP**: Depend on abstractions, not concretions + +## Code Smells to Remove +Long Method, Large Class, Primitive Obsession, Long Parameter List (max 3), Data Clumps, Switch Statements, Temporary Field, Divergent Change, Shotgun Surgery, Duplicated Code, Dead Code, Feature Envy, Middle Man, Magic Numbers (replace with named constants). + +## Class Design +- Small: max ~7 fields, ~3-5 public methods +- Single clear purpose aligned with domain +- Domain-focused naming (e.g. `PolicyRenewalService`, not `HelperUtil`) +- Encapsulation -- hide internal structure +- Prefer immutability, composition over inheritance +- Follow Law of Demeter +- Prefer value objects over primitives +- Avoid God objects and util classes + +## Method Design +- Ideal length: ~3 lines, rarely more than 5 +- Single atomic step of logic +- Names describe *what* not *how* +- No side effects +- Use guard clauses / early returns +- Avoid nested ifs/loops +- No flag arguments -- split into separate methods +- Extract complex predicates into named boolean methods + +## Test Design +- Small, specific, isolated, fast, independent, repeatable +- Arrange-Act-Assert format +- Max 3 assertions per test +- Always test new public behavior +- At least one negative test per API +- Avoid duplicated test data -- extract to class level + +## Naming +- Descriptive and unambiguous +- Methods: verbs. Classes: nouns. Variables: clear names +- Pronounceable, searchable, no encodings + +## Comments +- Explain *why*, not *what* +- Document assumptions, invariants, edge cases +- Never comment out code -- just remove it + +## Error Handling +- Use domain-specific custom exceptions +- Handle exceptions gracefully; never swallow them +- Externalize user-facing messages +- Maintain ErrorCode mapping + +## Security +- No hardcoded secrets, URLs, or sensitive info +- All sensitive config resolved via environment or config server +- PII protection: data masking, tokenization + +## Performance +- Avoid premature micro-optimizations unless profiled +- Batch operations in single transactions +- Refactor nested loops into indexed maps (O(n+m)) + +## API Design +- RESTful conventions with domain-driven design +- Plural noun resources: `/v1/templates` +- Accept filtering & expansion parameters +- Consistent resource naming diff --git a/template/.github/instructions/coding-style.instructions.md b/template/.github/instructions/coding-style.instructions.md new file mode 100644 index 0000000..37a5c33 --- /dev/null +++ b/template/.github/instructions/coding-style.instructions.md @@ -0,0 +1,65 @@ +--- +applyTo: '**' +--- + +# Coding Style + +## Immutability (CRITICAL) + +ALWAYS create new objects, NEVER mutate existing ones: +- Return new instances rather than modifying in place +- Use spread operators, `List.copyOf()`, `Map.copyOf()`, or equivalent +- Mark fields `final` / `readonly` / `const` by default + +Rationale: Immutable data prevents hidden side effects, makes debugging easier, and enables safe concurrency. + +## File Organization + +MANY SMALL FILES > FEW LARGE FILES: +- High cohesion, low coupling +- 200-400 lines typical, 800 max +- Extract utilities from large modules +- Organize by feature/domain, not by type + +## Function Size + +- Ideal: 3-10 lines, rarely more than 20 +- Max: 50 lines — split if exceeded +- Single atomic step of logic per function +- No flag arguments — split into separate functions + +## Nesting + +- Max 4 levels of nesting +- Use guard clauses and early returns to flatten +- Extract complex predicates into named boolean methods +- Extract inner loops into helper functions + +## Error Handling + +ALWAYS handle errors comprehensively: +- Handle errors explicitly at every level +- Provide user-friendly error messages in UI-facing code +- Log detailed error context on the server side +- Never silently swallow errors + +## Input Validation + +ALWAYS validate at system boundaries: +- Validate all user input before processing +- Use schema-based validation where available +- Fail fast with clear error messages +- Never trust external data (API responses, user input, file content) + +## Code Quality Checklist + +Before marking work complete: +- [ ] Code is readable and well-named +- [ ] Functions are small (<50 lines) +- [ ] Files are focused (<800 lines) +- [ ] No deep nesting (>4 levels) +- [ ] Proper error handling at every level +- [ ] No hardcoded values (use constants or config) +- [ ] No mutation (immutable patterns used) +- [ ] No `console.log` / `System.out.println` in production code +- [ ] No TODO/FIXME without a linked issue diff --git a/template/.github/instructions/git-workflow.instructions.md b/template/.github/instructions/git-workflow.instructions.md new file mode 100644 index 0000000..c66fced --- /dev/null +++ b/template/.github/instructions/git-workflow.instructions.md @@ -0,0 +1,51 @@ +--- +applyTo: '**' +--- + +# Git Workflow + +## Commit Message Format +``` +: + + + +> +``` + +### Types +`feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `perf`, `ci`, `style` + +### Rules +- Use imperative mood: "add feature" not "added feature" +- Keep subject line under 72 characters +- One logical change per commit +- Run `gitlint` locally before pushing + +## Branch Naming +Format: `/` + +Examples: +- `feature/add-login` +- `bugfix/fix-null-pointer` +- `hotfix/auth-patch` + +## Pull Request Workflow +1. Analyze full commit history: `git diff ...HEAD` +2. Draft comprehensive PR summary covering what changed and why +3. Include a test plan with verification steps +4. Push with `-u` flag if new branch +5. Request review from at least one peer + +## Pre-Commit Checklist +- [ ] All tests pass locally +- [ ] No lint errors or warnings +- [ ] No `console.log` / `System.out.println` left in production code +- [ ] No TODO/FIXME without a linked issue +- [ ] Commit message follows format above +- [ ] Branch is rebased on latest base branch + +## Merge Strategy +- Use squash merge when all commits are from the same author and represent one topic +- Use rebase/merge commit when commits represent distinct topics or multiple authors +- Preserve clear history and avoid merge commits from `main` into feature branches diff --git a/template/.github/instructions/python.instructions.md b/template/.github/instructions/python.instructions.md new file mode 100644 index 0000000..14d9aff --- /dev/null +++ b/template/.github/instructions/python.instructions.md @@ -0,0 +1,87 @@ +--- +applyTo: '**/*.py' +--- + +# Python Guidelines + +## Project Structure +``` +project-root/ +├── README.md +├── requirements.txt or pyproject.toml +├── src/mypackage/ +│ ├── __init__.py +│ └── module1.py +├── scripts/ +│ └── run_analysis.py +└── tests/ + ├── __init__.py + └── test_module1.py +``` +- Source code in dedicated package directory (`src/mypackage/`) +- Scripts in `scripts/` with `if __name__ == "__main__":` entry points +- Tests in parallel `tests/` directory mirroring code structure + +## Code Style +- Follow PEP 8; use auto-formatting (Black) and linting (ruff/flake8) +- `snake_case` for modules/functions, `CapWords` for classes, `UPPER_SNAKE_CASE` for constants +- Explicit, descriptive names for all identifiers +- Imports at top: standard library, third-party, local -- with blank lines between +- Absolute imports only; no wildcard imports +- Docstrings for all public modules, classes, functions, methods +- Minimize comments -- code must be self-explanatory + +## Function & Parameter Rules +- **Never use mutable default arguments** (lists, dicts) -- use `None` and create inside function +- **No blank strings/lists as defaults** +- **No `.get()` for required dict keys** -- access directly so missing keys raise errors +- Check explicitly for `None` or absence for optional values + +## Error Handling +- Catch specific exception types only -- never bare `except:` +- Use `logging` module, never `print()` +- Log or re-raise errors appropriately + +## Type Hints & Readability +- Use type hints to clarify input/output types +- Small, focused functions (Single Responsibility) +- Prefer comprehensions and built-ins for clarity +- Avoid deep nesting; break logic into helpers + +## Dependencies & Configuration +- Pin all dependency versions in `requirements.txt` or `pyproject.toml` +- Always use virtual environments +- Environment variables or config files for secrets -- never hardcode +- `.gitignore` excludes venvs, caches, non-source artifacts + +## Security +- **No hardcoded secrets** — use environment variables, `.env` files (in `.gitignore`), or secret managers +- **Parameterized queries only** — never use f-strings or `%` formatting for SQL +- **Path traversal prevention** — validate and sanitize all file paths; reject `..` components +- **Input validation** at API boundaries — use Pydantic models or marshmallow schemas +- **No `eval()` or `exec()`** on user input — ever +- **Dependency scanning** — use `pip-audit` or `safety` in CI pipeline +- **No sensitive data in logs** — mask PII, tokens, passwords + +## Data Validation with Pydantic +- Use Pydantic `BaseModel` for request/response validation at API boundaries +- Define strict types with `Field()` constraints (`min_length`, `max_length`, `ge`, `le`) +- Use `@validator` or `@field_validator` for custom validation logic +- Return Pydantic models from service layer for type safety + +## Testing +- **TDD mandatory**: RED → GREEN → REFACTOR for new features and bug fixes +- pytest for all testing +- `test_` prefix for files and functions +- Use pytest fixtures for shared setup +- Never mix tests with production code +- Happy path + edge case tests with clear assertions +- Use `@pytest.mark.parametrize` for data-driven tests +- `unittest.mock` for external dependencies +- Min 80% coverage; 100% for security-critical code +- Use `pytest-cov` for coverage enforcement in CI + +## General +- Prefer standard library; add third-party only for clear benefit +- Validate and sanitize all inputs; no silent failures +- Use context managers (`with`) for resource management diff --git a/template/.github/instructions/security.instructions.md b/template/.github/instructions/security.instructions.md new file mode 100644 index 0000000..336eda3 --- /dev/null +++ b/template/.github/instructions/security.instructions.md @@ -0,0 +1,54 @@ +--- +applyTo: '**' +--- + +# Security Guidelines + +## Mandatory Checks Before Every Commit +- [ ] No hardcoded secrets (API keys, passwords, tokens) +- [ ] All user inputs validated at system boundaries +- [ ] SQL injection prevention (parameterized queries only) +- [ ] XSS prevention (sanitized HTML output) +- [ ] CSRF protection enabled +- [ ] Authentication and authorization verified +- [ ] Rate limiting on public endpoints +- [ ] Error messages do not leak internal details + +## Secret Management +- NEVER hardcode secrets in source code +- Use environment variables or a secret/config manager +- Validate required secrets are present at startup +- Rotate any secrets that may have been exposed +- Keep files with secrets in `.gitignore` + +## Input Validation +- Validate all user input before processing +- Use schema-based validation where available (Bean Validation, Zod, Pydantic) +- Fail fast with clear error messages +- Never trust external data (API responses, user input, file content) + +## Dependency Security +- Audit transitive dependencies regularly +- Use OWASP Dependency-Check, Snyk, or Dependabot for CVE scanning +- Keep dependencies updated with automated tooling +- Pin versions in production deployments + +## Error Responses +- Never expose stack traces in API responses +- Map exceptions to safe, generic client messages at handler boundaries +- Log detailed errors server-side only +- Maintain an ErrorCode mapping for consistent client-facing messages + +## Authentication +- Never implement custom auth crypto — use established libraries +- Store passwords with bcrypt or Argon2 (never MD5/SHA1) +- Enforce authorization checks at service boundaries +- Never log passwords, tokens, or PII + +## Security Response Protocol +If a security issue is found during development: +1. STOP current work immediately +2. Assess severity (Critical / High / Medium / Low) +3. Fix CRITICAL issues before continuing any other work +4. Rotate any potentially exposed secrets +5. Review codebase for similar patterns diff --git a/template/.github/instructions/testing.instructions.md b/template/.github/instructions/testing.instructions.md new file mode 100644 index 0000000..fd1726b --- /dev/null +++ b/template/.github/instructions/testing.instructions.md @@ -0,0 +1,53 @@ +--- +applyTo: '**' +--- + +# Testing Requirements + +## Minimum Coverage: 80% + +100% required for: +- Financial calculations +- Authentication logic +- Security-critical code +- Core business logic + +## Test Types (ALL required for production features) +1. **Unit Tests** — Individual functions, utilities, components +2. **Integration Tests** — API endpoints, database operations, service interactions +3. **System/Scenario Tests** — Critical cross-component flows where applicable + +## Test-Driven Development (TDD) + +Mandatory workflow for new features and bug fixes: +1. **RED** — Write a failing test first +2. **GREEN** — Write minimal implementation to pass +3. **REFACTOR** — Improve code while keeping tests green +4. Repeat for each scenario + +### Rules +- Never write implementation before the test +- Run tests after every change +- Write minimal code to make tests pass +- Refactor only when tests are green + +## Test Structure +- Arrange-Act-Assert (AAA) pattern +- One logical assertion per test (max 3 related assertions) +- Descriptive test names: `methodName_scenario_expectedBehavior` +- Use `@DisplayName` or equivalent for human-readable descriptions + +## Test Quality +- Tests must be independent and isolated +- No shared mutable state between tests +- Fast execution (unit tests < 100ms each) +- Deterministic — no flaky tests +- Fix implementation, not tests (unless tests are wrong) + +## Language-Specific Frameworks +| Language | Unit | Mocking | Integration | Coverage | +|----------|------|---------|-------------|----------| +| C++ | GoogleTest | GoogleMock | Bazel test targets | lcov/gcov | +| Python | pytest | unittest.mock | pytest + service/integration fixtures | pytest-cov | +| Rust | cargo test | mockall (or equivalent) | integration tests in `tests/` | llvm-cov/grcov | +| Go | go test | gomock/testify | package/integration tests | go test -cover | diff --git a/template/.github/references/agent-card.schema.json b/template/.github/references/agent-card.schema.json new file mode 100644 index 0000000..a62fd10 --- /dev/null +++ b/template/.github/references/agent-card.schema.json @@ -0,0 +1,155 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://score.dev/schemas/agent-card.schema.json", + "title": "SCORE Agent Card", + "description": "Structured handoff artifact exchanged between agents and tools for one issue-scoped work item.", + "type": "object", + "additionalProperties": false, + "required": [ + "version", + "issue_id", + "repository", + "goal", + "status", + "summary", + "validation", + "next_action" + ], + "properties": { + "version": { + "const": 1 + }, + "issue_id": { + "type": "string", + "pattern": "^(ISSUE-[0-9]+|POC-[0-9]{8}-[0-9]{4})$" + }, + "repository": { + "type": "string", + "minLength": 1 + }, + "branch": { + "type": "string", + "minLength": 1 + }, + "goal": { + "type": "string", + "minLength": 1 + }, + "status": { + "type": "string", + "enum": [ + "in_progress", + "blocked", + "ready_for_handoff", + "completed" + ] + }, + "summary": { + "type": "string", + "minLength": 1 + }, + "findings": { + "type": "array", + "items": { + "$ref": "#/$defs/note" + }, + "default": [] + }, + "open_questions": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "default": [] + }, + "touched_files": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "uniqueItems": true, + "default": [] + }, + "validation": { + "type": "object", + "additionalProperties": false, + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "enum": [ + "not_run", + "passed", + "failed" + ] + }, + "commands": { + "type": "array", + "items": { + "$ref": "#/$defs/commandResult" + }, + "default": [] + } + } + }, + "trajectory": { + "type": "array", + "items": { + "$ref": "#/$defs/note" + }, + "default": [] + }, + "next_action": { + "type": "string", + "minLength": 1 + } + }, + "$defs": { + "note": { + "type": "object", + "additionalProperties": false, + "required": [ + "title", + "detail" + ], + "properties": { + "title": { + "type": "string", + "minLength": 1 + }, + "detail": { + "type": "string", + "minLength": 1 + } + } + }, + "commandResult": { + "type": "object", + "additionalProperties": false, + "required": [ + "command", + "status" + ], + "properties": { + "command": { + "type": "string", + "minLength": 1 + }, + "status": { + "type": "string", + "enum": [ + "passed", + "failed" + ] + }, + "detail": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/template/.github/references/assistant-runtime-alignment.md b/template/.github/references/assistant-runtime-alignment.md new file mode 100644 index 0000000..e3750ee --- /dev/null +++ b/template/.github/references/assistant-runtime-alignment.md @@ -0,0 +1,32 @@ +# Assistant Runtime Alignment + +This repository distributes one shared policy across multiple assistant runtimes. + +## Canonical instruction source + +- AGENTS.md is the canonical, runtime-neutral project policy. +- CLAUDE.md imports AGENTS.md for Claude Code compatibility. +- .github/ is runtime-specific glue (for example copilot-instructions.md). + +## Why this layout + +- Codex reads AGENTS.md directly and supports layered AGENTS files. +- Claude Code reads CLAUDE.md and recommends importing AGENTS.md when both are used. +- VS Code agent plugins and Codex plugins share compatible plugin concepts (skills, agents, hooks, MCP), so marketplace settings can be aligned. + +## Plugin marketplace alignment + +The template includes marketplace recommendation stubs in: + +- .claude/settings.json +- .github/copilot/settings.json + +Both point to the same marketplace and default plugin identifier so teams can keep tool capabilities aligned. + +## Adoption checklist + +1. Keep AGENTS.md as the source of truth for shared behavioral policy. +2. Keep CLAUDE.md minimal and import-first. +3. Keep .github/ minimal and runtime-specific. +4. Configure one approved plugin marketplace for all assistant runtimes. +5. Use copier update to roll out governance and alignment updates. diff --git a/template/.github/references/repo-manifest.schema.json b/template/.github/references/repo-manifest.schema.json new file mode 100644 index 0000000..ac98596 --- /dev/null +++ b/template/.github/references/repo-manifest.schema.json @@ -0,0 +1,152 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://score.dev/schemas/repo-manifest.schema.json", + "title": "SCORE Repo Manifest", + "description": "Minimal federated harness contract for a SCORE repository.", + "type": "object", + "additionalProperties": false, + "required": [ + "version", + "repository", + "bootstrap", + "execution", + "mcp" + ], + "properties": { + "version": { + "const": 1 + }, + "repository": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "language", + "visibility" + ], + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "language": { + "type": "string", + "enum": [ + "python", + "go", + "rust", + "cpp", + "typescript", + "mixed", + "other" + ] + }, + "visibility": { + "type": "string", + "enum": [ + "public", + "internal", + "private" + ] + }, + "tags": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "uniqueItems": true, + "default": [] + } + } + }, + "bootstrap": { + "type": "object", + "additionalProperties": false, + "required": [ + "contract_version" + ], + "properties": { + "contract_version": { + "type": "string", + "pattern": "^v[0-9]+\\.[0-9]+\\.[0-9]+$" + }, + "template_version": { + "type": "string", + "pattern": "^v[0-9]+\\.[0-9]+\\.[0-9]+$" + } + } + }, + "execution": { + "type": "object", + "additionalProperties": false, + "required": [ + "build", + "test", + "lint" + ], + "properties": { + "build": { + "$ref": "#/$defs/commandSpec" + }, + "test": { + "$ref": "#/$defs/commandSpec" + }, + "lint": { + "$ref": "#/$defs/commandSpec" + }, + "typecheck": { + "$ref": "#/$defs/commandSpec" + } + } + }, + "mcp": { + "type": "object", + "additionalProperties": false, + "required": [ + "server_name", + "tools" + ], + "properties": { + "server_name": { + "type": "string", + "minLength": 1 + }, + "tools": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "build", + "test", + "lint", + "typecheck", + "search" + ] + }, + "uniqueItems": true, + "minItems": 1 + } + } + } + }, + "$defs": { + "commandSpec": { + "type": "object", + "additionalProperties": false, + "required": [ + "command" + ], + "properties": { + "command": { + "type": "string", + "minLength": 1 + }, + "working_directory": { + "type": "string", + "minLength": 1 + } + } + } + } +} \ No newline at end of file diff --git a/template/.github/score/repo-manifest.json.jinja b/template/.github/score/repo-manifest.json.jinja new file mode 100644 index 0000000..5f06739 --- /dev/null +++ b/template/.github/score/repo-manifest.json.jinja @@ -0,0 +1,23 @@ +{ + "version": 1, + "repository": { + "name": "{{ repo_name }}", + "language": "{{ repo_language }}", + "visibility": "{{ repo_visibility }}" + }, + "bootstrap": { + "contract_version": "v0.1.0", + "template_source": "https://github.com/eclipse-score/.github" + }, + "execution": { + "build": { + "command": "{{ build_command }}" + }, + "test": { + "command": "{{ test_command }}" + }, + "lint": { + "command": "{{ lint_command }}" + } + } +} diff --git a/template/.github/workflows/docs-hygiene.yml b/template/.github/workflows/docs-hygiene.yml new file mode 100644 index 0000000..b332818 --- /dev/null +++ b/template/.github/workflows/docs-hygiene.yml @@ -0,0 +1,29 @@ +name: Markdown Hygiene + +on: + pull_request: + paths: + - '**/*.md' + - 'scripts/check_markdown_hygiene.py' + - '.github/workflows/docs-hygiene.yml' + schedule: + - cron: '0 6 * * 1' + workflow_dispatch: + +permissions: + contents: read + +jobs: + markdown-hygiene: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Run markdown hygiene checks + run: python scripts/check_markdown_hygiene.py --root . --include .github --include README.md --include profile diff --git a/template/.github/{{ assistant_instructions_file }}.jinja b/template/.github/{{ assistant_instructions_file }}.jinja new file mode 100644 index 0000000..4a23caa --- /dev/null +++ b/template/.github/{{ assistant_instructions_file }}.jinja @@ -0,0 +1,44 @@ +You are operating inside the SCORE governance overlay. + +This file is runtime-specific glue. +The canonical, runtime-neutral policy lives in AGENTS.md at repository root. + +Keep this file aligned with AGENTS.md and use it only for assistant-runtime specifics. +Update distributed policy files by running `copier update` from the repository root. + +## Responsibilities + +1. Preserve issue-first traceability — all work artifacts reference a GitHub issue number. +2. Honour SCORE contracts: + - `.github/references/repo-manifest.schema.json` + - `.github/references/agent-card.schema.json` + - `.github/score/repo-manifest.json` +3. Apply coding standards from `.github/instructions/`. + +## Artifact Naming + +Use issue-scoped stage folders for all work artifacts: + +``` +.stage/ISSUE-/ + plan.md + agent-card.json +``` + +## SDLC Progress Block + +Paste this into every response when tracking work: + +### SDLC Progress -- +- [ ] PLAN -- Not Started +- [ ] CODE -- Not Started +- [ ] BUILD -- Not Started +- [ ] TEST -- Not Started +- [ ] RELEASE -- Not Started + +## Governance Rules + +- Do not embed large agent/prompt catalogs here. +- Use an upstream SDD framework (Spec Kit, OpenSpec, BMAD) for workflow orchestration. +- Keep this file and `.github/instructions/` as the only SCORE-specific overlay. +- If AGENTS.md and this file conflict, treat AGENTS.md as the canonical project policy and reconcile the files. diff --git a/template/AGENTS.md b/template/AGENTS.md new file mode 100644 index 0000000..9c9fd24 --- /dev/null +++ b/template/AGENTS.md @@ -0,0 +1,51 @@ +# AGENTS.md + +SCORE canonical assistant policy for this repository. + +This file is the shared, runtime-neutral source of behavioral guidance across: +- Codex (reads AGENTS.md directly) +- Claude Code (via CLAUDE.md import) +- Other assistants (via their runtime-specific instructions file) + +## Scope + +This repository keeps a thin governance overlay only. +Do not add large local agent or prompt catalogs. + +## Core responsibilities + +1. Preserve issue-first traceability. +2. Preserve SCORE contracts: + - .github/references/repo-manifest.schema.json + - .github/references/agent-card.schema.json + - .github/score/repo-manifest.json +3. Apply coding standards from .github/instructions/. + +## Artifact rules + +Use issue-scoped artifacts only: + +```text +.stage/ISSUE-/... +``` + +Do not create anonymous stage artifacts at repo root. + +## SDLC status block + +When reporting progress, use: + +```markdown +### SDLC Progress -- +- [ ] PLAN -- Not Started +- [ ] CODE -- Not Started +- [ ] BUILD -- Not Started +- [ ] TEST -- Not Started +- [ ] RELEASE -- Not Started +``` + +## Governance constraints + +- Keep SCORE policy concise and deterministic. +- Keep workflow frameworks external (for example Spec Kit or OpenSpec). +- Keep shared commands in .github/score/repo-manifest.json. diff --git a/template/CLAUDE.md b/template/CLAUDE.md new file mode 100644 index 0000000..7d8138a --- /dev/null +++ b/template/CLAUDE.md @@ -0,0 +1,7 @@ +@AGENTS.md + +## Claude Code notes + +- Keep Claude-specific additions in this file below the AGENTS import. +- Prefer .claude/rules/ for path-specific rules when repository complexity grows. +- Keep this file short; put shared project behavior in AGENTS.md. diff --git a/template/scripts/check_markdown_hygiene.py b/template/scripts/check_markdown_hygiene.py new file mode 100644 index 0000000..7b42c23 --- /dev/null +++ b/template/scripts/check_markdown_hygiene.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +"""Lightweight markdown hygiene checks for repository-scale governance docs. + +Checks: +- Duplicate markdown files by content hash +- Broken local markdown links (relative repo paths) + +Exit codes: +- 0: no issues +- 1: one or more issues found +""" + +from __future__ import annotations + +import argparse +import hashlib +import re +import sys +from pathlib import Path +from typing import Iterable + +MARKDOWN_LINK_RE = re.compile(r"\[[^\]]+\]\(([^)]+)\)") +DEFAULT_EXCLUDED_DIRS = {".git", ".venv", "venv", "node_modules", ".mypy_cache", ".pytest_cache"} + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Check markdown hygiene in a repository") + parser.add_argument("--root", default=".", help="Repository root path") + parser.add_argument( + "--include", + action="append", + default=[".github", "README.md", "profile"], + help="Path (file/dir) under root to include; repeatable", + ) + return parser.parse_args() + + +def to_repo_relative(path: Path, root: Path) -> str: + return path.relative_to(root).as_posix() + + +def gather_markdown_files(root: Path, include_paths: Iterable[str]) -> list[Path]: + files: list[Path] = [] + for include in include_paths: + candidate = (root / include).resolve() + if not candidate.exists(): + continue + if candidate.is_file() and candidate.suffix.lower() == ".md": + files.append(candidate) + continue + if candidate.is_dir(): + for path in candidate.rglob("*.md"): + if any(part in DEFAULT_EXCLUDED_DIRS for part in path.parts): + continue + files.append(path.resolve()) + unique = sorted(set(files)) + return unique + + +def sha256_of(path: Path) -> str: + digest = hashlib.sha256() + digest.update(path.read_bytes()) + return digest.hexdigest() + + +def find_duplicate_markdown(files: list[Path]) -> list[list[Path]]: + by_hash: dict[str, list[Path]] = {} + for path in files: + file_hash = sha256_of(path) + by_hash.setdefault(file_hash, []).append(path) + return [group for group in by_hash.values() if len(group) > 1] + + +def strip_anchor_and_query(target: str) -> str: + no_anchor = target.split("#", 1)[0] + no_query = no_anchor.split("?", 1)[0] + return no_query + + +def is_external_link(target: str) -> bool: + lowered = target.lower() + return lowered.startswith(("http://", "https://", "mailto:", "tel:")) + + +def find_broken_local_links(files: list[Path], root: Path) -> list[tuple[Path, str, str]]: + issues: list[tuple[Path, str, str]] = [] + for markdown_file in files: + text = markdown_file.read_text(encoding="utf-8") + for raw_target in MARKDOWN_LINK_RE.findall(text): + target = raw_target.strip() + if not target or target.startswith("#") or is_external_link(target): + continue + + # Template placeholders are examples, not resolvable links. + if "{" in target or "}" in target: + continue + + normalized = strip_anchor_and_query(target) + if not normalized: + continue + + # Absolute repo path style: /path/from/repo/root + if normalized.startswith("/"): + resolved = (root / normalized.lstrip("/")).resolve() + else: + resolved = (markdown_file.parent / normalized).resolve() + + if not resolved.exists(): + issues.append((markdown_file, target, to_repo_relative(markdown_file, root))) + return issues + + +def print_duplicate_report(duplicates: list[list[Path]], root: Path) -> None: + if not duplicates: + print("No duplicate markdown files detected.") + return + print("Duplicate markdown files detected:") + for group in duplicates: + print("- Duplicate group:") + for path in group: + print(f" - {to_repo_relative(path, root)}") + + +def print_broken_link_report(broken: list[tuple[Path, str, str]], root: Path) -> None: + if not broken: + print("No broken local markdown links detected.") + return + print("Broken local markdown links detected:") + for markdown_file, target, _ in broken: + rel = to_repo_relative(markdown_file, root) + print(f"- {rel}: {target}") + + +def main() -> int: + args = parse_args() + root = Path(args.root).resolve() + + files = gather_markdown_files(root, args.include) + if not files: + print("No markdown files found for the configured include paths.") + return 0 + + duplicates = find_duplicate_markdown(files) + broken_links = find_broken_local_links(files, root) + + print(f"Scanned {len(files)} markdown files.") + print_duplicate_report(duplicates, root) + print_broken_link_report(broken_links, root) + + has_issues = bool(duplicates or broken_links) + if has_issues: + print("Markdown hygiene check failed.") + return 1 + + print("Markdown hygiene check passed.") + return 0 + + +if __name__ == "__main__": + sys.exit(main())