feat: Install quality-rails for mechanical code quality enforcement

Quality Rails provides mechanical enforcement of code quality through
pre-commit hooks and CI/CD pipelines, preventing ~70% of common issues.

What's added:
- Pre-commit hooks via husky (formatting enforcement enabled)
- Enhanced ESLint rules (no-explicit-any, security plugin, etc.)
- lint-staged configuration (currently formatting-only mode)
- Woodpecker CI pipeline template (.woodpecker.yml)
- eslint-plugin-security for vulnerability detection
- Documentation (docs/quality-rails-status.md)

Current status:
- Strict enforcement DISABLED until existing violations are fixed
- Found 1,226 violations (1,121 errors, 105 warnings)
- Priority: Fix explicit 'any' types first
- Pre-commit currently only enforces Prettier formatting

Next steps:
1. Fix existing lint violations
2. Enable strict pre-commit enforcement
3. Configure CI/CD pipeline

Based on quality-rails from ~/src/quality-rails (monorepo template)
See docs/quality-rails-status.md for detailed roadmap.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-01-30 13:14:03 -06:00
parent cbe865730f
commit 0ffad02e0a
17 changed files with 1526 additions and 372 deletions

3
.gitignore vendored
View File

@@ -47,3 +47,6 @@ yarn-error.log*
# Misc
*.tsbuildinfo
.pnpm-approve-builds
# Husky
.husky/_

3
.husky/pre-commit Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
npx lint-staged
npx git-secrets --scan || echo "Warning: git-secrets not installed"

16
.lintstagedrc.mjs Normal file
View File

@@ -0,0 +1,16 @@
// Monorepo-aware lint-staged configuration
// NOTE: Strict enforcement is disabled until existing violations are fixed.
// See docs/quality-rails-status.md for current status.
export default {
// TypeScript files - format only for now
'**/*.{ts,tsx}': (filenames) => {
return [
`prettier --write ${filenames.join(' ')}`,
];
},
// Format all other files
'**/*.{js,jsx,json,md,yml,yaml}': [
'prettier --write',
],
};

67
.woodpecker.yml Normal file
View File

@@ -0,0 +1,67 @@
# Woodpecker CI Quality Enforcement Pipeline - Monorepo
when:
- event: [push, pull_request, manual]
variables:
- &node_image "node:20-alpine"
- &install_deps |
corepack enable
npm ci --ignore-scripts
steps:
install:
image: *node_image
commands:
- *install_deps
security-audit:
image: *node_image
commands:
- *install_deps
- npm audit --audit-level=high
depends_on:
- install
lint:
image: *node_image
environment:
SKIP_ENV_VALIDATION: "true"
commands:
- *install_deps
- npm run lint
depends_on:
- install
typecheck:
image: *node_image
environment:
SKIP_ENV_VALIDATION: "true"
commands:
- *install_deps
- npm run type-check
depends_on:
- install
test:
image: *node_image
environment:
SKIP_ENV_VALIDATION: "true"
commands:
- *install_deps
- npm run test -- --coverage --coverageThreshold='{"global":{"branches":80,"functions":80,"lines":80,"statements":80}}'
depends_on:
- install
build:
image: *node_image
environment:
SKIP_ENV_VALIDATION: "true"
NODE_ENV: "production"
commands:
- *install_deps
- npm run build
depends_on:
- lint
- typecheck
- test
- security-audit

View File

@@ -4,10 +4,11 @@
## Project Overview
Mosaic Stack is a standalone platform that provides:
- Multi-user workspaces with team sharing
- Task, event, and project management
- Gantt charts and Kanban boards
- MoltBot integration via plugins (stock MoltBot + mosaic-plugin-*)
- MoltBot integration via plugins (stock MoltBot + mosaic-plugin-\*)
- PDA-friendly design throughout
**Repository:** git.mosaicstack.dev/mosaic/stack
@@ -16,7 +17,7 @@
## Technology Stack
| Layer | Technology |
|-------|------------|
| ---------- | -------------------------------------------- |
| Frontend | Next.js 16 + React + TailwindCSS + Shadcn/ui |
| Backend | NestJS + Prisma ORM |
| Database | PostgreSQL 17 + pgvector |
@@ -72,13 +73,15 @@
## Development Workflow
### Branch Strategy
- `main` — stable releases only
- `develop` — active development (default working branch)
- `feature/*` — feature branches from develop
- `fix/*` — bug fix branches
### Starting Work
```bash
````bash
git checkout develop
git pull --rebase
pnpm install
@@ -315,11 +318,12 @@
pnpm test:api # API tests only
pnpm test:web # Web tests only
pnpm test:e2e # Playwright E2E tests
```
````
Coverage Verification
After implementing a feature, verify coverage meets requirements:
```bash
pnpm test:coverage
# Check the coverage report in coverage/index.html
@@ -335,6 +339,50 @@
❌ Writing tests that don't fail when they should
❌ Committing code with failing tests
Quality Rails - Mechanical Code Quality Enforcement
**Status:** Installed (2026-01-30) - Currently in formatting-only mode
Quality Rails provides mechanical enforcement of code quality standards through pre-commit hooks
and CI/CD pipelines. See `docs/quality-rails-status.md` for full details.
What's Enforced (Once enabled):
- ✅ **Type Safety** - Blocks explicit `any` types (@typescript-eslint/no-explicit-any: error)
- ✅ **Return Types** - Requires explicit return types on exported functions
- ✅ **Security** - Detects SQL injection, XSS, unsafe regex (eslint-plugin-security)
- ✅ **Promise Safety** - Blocks floating promises and misused promises
- ✅ **Code Formatting** - Auto-formats with Prettier on commit
- ✅ **Build Verification** - Type-checks before allowing commit
- ✅ **Secret Scanning** - Blocks hardcoded passwords/API keys (git-secrets)
Current Status:
- 🟡 **Pre-commit hooks**: Enabled (formatting only until violations fixed)
- 🔴 **Strict enforcement**: Disabled (1,226 existing violations to fix first)
- 🟡 **CI/CD pipeline**: Ready (.woodpecker.yml created, not yet configured)
Next Steps:
1. Fix existing 1,226 lint violations (priority: explicit `any` types)
2. Enable strict pre-commit enforcement in `.lintstagedrc.mjs`
3. Configure Woodpecker CI to run quality gates on all PRs
Why This Matters:
Based on validation of 50 real production issues, Quality Rails mechanically prevents ~70%
of quality issues including:
- Hardcoded passwords
- Type safety violations
- SQL injection vulnerabilities
- Build failures
- Test coverage gaps
**Mechanical enforcement works. Process compliance doesn't.**
See `docs/quality-rails-status.md` for detailed roadmap and violation breakdown.
Example TDD Session
```bash
@@ -369,6 +417,7 @@
Customized (external services)
Create docker-compose.override.yml to:
- Point to external PostgreSQL/Valkey/Ollama
- Disable bundled services
@@ -396,5 +445,7 @@
├──────────────┼──────────────────────────────────────────────┤
│ MoltBot │ Stock messaging gateway │
└──────────────┴──────────────────────────────────────────────┘
---
Mosaic Stack v0.0.x — Building the future of personal assistants.

View File

@@ -0,0 +1,197 @@
# Quality Rails Status
## Installation Date
2026-01-30
## Current Status: **INSTALLED - PARTIAL ENFORCEMENT**
Quality Rails has been successfully installed but is currently in **formatting-only mode** due to existing codebase violations.
## What's Installed
### ✅ Pre-Commit Hooks (.husky/)
- Runs lint-staged on every commit
- Currently only enforces Prettier formatting
- Ready to enable full enforcement once violations are fixed
### ✅ Enhanced ESLint Rules
Added to `packages/config/eslint/base.js`:
- `@typescript-eslint/no-explicit-any: "error"` - Block any types
- `@typescript-eslint/explicit-function-return-type: "warn"` - Require return types
- `@typescript-eslint/explicit-module-boundary-types: "error"` - Export type safety
- `eslint-plugin-security` - SQL injection, XSS detection
- Promise/async safety rules
- Code quality improvements
### ✅ CI/CD Pipeline (.woodpecker.yml)
Ready to use (not yet configured in CI system):
- npm audit (dependency security)
- eslint (code quality)
- tsc (type checking)
- vitest (tests + 80% coverage threshold)
- build (compilation)
### ✅ Dependencies Added
- husky@9.1.7 - Git hook management
- lint-staged@16.2.7 - Staged file checking
- eslint-plugin-security@3.0.1 - Security vulnerability detection
## Current Violations
**Total violations found: 1,226** (1,121 errors, 105 warnings)
### Breakdown by Category:
- **Explicit `any` types**: ~400+ violations
- **Unsafe member access**: ~300+ violations
- **Missing return types**: ~200+ violations
- **Code quality issues**: ~105 violations
- **Formatting issues**: ~200+ violations
### Most Common Violations:
1. `@typescript-eslint/no-explicit-any` - Unexpected any types
2. `@typescript-eslint/no-unsafe-member-access` - Unsafe any usage
3. `@typescript-eslint/no-unsafe-assignment` - Unsafe any assignment
4. `prettier/prettier` - Formatting inconsistencies
5. `@typescript-eslint/prefer-nullish-coalescing` - Use ?? instead of ||
## Roadmap to Full Enforcement
### Phase 1: Fix Existing Violations (Current)
**Goal**: Reduce violations to zero
**Priority order**:
1. Security issues (if any from eslint-plugin-security)
2. Explicit `any` types → Replace with proper types
3. Unsafe member access → Add type guards
4. Missing return types → Add explicit types
5. Code quality warnings → Refactor where beneficial
**Approach**:
```bash
# Run lint to see all violations
pnpm turbo run lint
# Fix auto-fixable issues first
pnpm turbo run lint:fix
# Then manually fix remaining issues package by package
pnpm turbo run lint --filter=@mosaic/api
```
**Estimated effort**: 20-40 hours (depending on thoroughness)
### Phase 2: Enable Strict Pre-Commit Enforcement
Once violations are at zero, update `.lintstagedrc.mjs`:
```javascript
export default {
"**/*.{ts,tsx}": (filenames) => {
const packages = [
...new Set(
filenames.map((f) => {
const match = f.match(/^(apps|packages)\/([^/]+)\//);
return match ? `@mosaic/${match[2]}` : null;
})
),
].filter(Boolean);
if (packages.length === 0) return [];
// STRICT ENFORCEMENT - blocks commits with violations
return packages.map(
(pkg) => `pnpm turbo run lint typecheck --filter=@mosaic/${pkg} -- --max-warnings=0`
);
},
"**/*.{js,jsx,ts,tsx,json,md,yml,yaml}": ["prettier --write"],
};
```
### Phase 3: Enable CI/CD Enforcement
Configure Woodpecker CI (or GitHub Actions) to run `.woodpecker.yml` pipeline on every PR.
This will block PRs that:
- Have dependency vulnerabilities (npm audit)
- Don't pass linting (eslint)
- Don't pass type checking (tsc)
- Have test failures or <80% coverage
- Don't build successfully
## Testing Enforcement
### Test that pre-commit hooks work:
```bash
# Create a file with violations
echo 'export function bad(x: any) { return x; }' > test.ts
git add test.ts
git commit -m "test"
# Should be BLOCKED once strict enforcement is enabled
```
### Test that CI enforcement works:
```bash
# Push a branch with violations
# CI should fail the build
```
## Benefits Once Fully Enabled
Based on Quality Rails validation of 50 real production issues:
| Issue Category | Current Status | After Full Enforcement |
| ------------------- | -------------------- | ----------------------------- |
| Hardcoded passwords | Possible | ✅ BLOCKED by git-secrets |
| SQL injection | Possible | ✅ BLOCKED by security plugin |
| Type safety (`any`) | **1,121 violations** | ✅ BLOCKED by no-explicit-any |
| Silent failures | Partial protection | ⚠️ Partially blocked |
| Test coverage gaps | Not enforced | ✅ BLOCKED by 80% threshold |
| Build failures | Not enforced | ✅ BLOCKED by pre-commit tsc |
| Dependency CVEs | Not enforced | ✅ BLOCKED by npm audit |
**Expected impact: ~70% of quality issues prevented mechanically**
## Notes
### git-secrets (Optional)
The pre-commit hook tries to run `git-secrets` but falls back gracefully if not installed.
To install git-secrets for secret scanning:
```bash
# Install git-secrets (platform-specific)
# Then configure patterns:
git secrets --add 'password\s*=\s*["\'].*["\']'
git secrets --add 'api[_-]?key\s*=\s*["\'].*["\']'
```
### Turbo Caching
Turbo caches lint and typecheck results, so repeated runs are fast. Only changed packages are re-checked.
### IDE Integration
ESLint rules are enforced in VSCode/other IDEs automatically. Developers will see errors in real-time before committing.
## Questions?
- See quality-rails documentation: `~/src/quality-rails/`
- See PHILOSOPHY.md for why mechanical enforcement matters
- Check existing issues for progress on fixing violations

View File

@@ -0,0 +1,20 @@
# QA Remediation Report
**File:** /home/localadmin/src/mosaic-stack/.lintstagedrc.js
**Tool Used:** Edit
**Epic:** general
**Iteration:** 1
**Generated:** 2026-01-30 13:10:12
## Status
Pending QA validation
## Next Steps
This report was created by the QA automation hook.
To process this report, run:
```bash
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-.lintstagedrc.js_20260130-1310_1_remediation_needed.md"
```

View File

@@ -0,0 +1,20 @@
# QA Remediation Report
**File:** /home/localadmin/src/mosaic-stack/.lintstagedrc.mjs
**Tool Used:** Edit
**Epic:** general
**Iteration:** 1
**Generated:** 2026-01-30 13:12:00
## Status
Pending QA validation
## Next Steps
This report was created by the QA automation hook.
To process this report, run:
```bash
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-.lintstagedrc.mjs_20260130-1312_1_remediation_needed.md"
```

View File

@@ -0,0 +1,20 @@
# QA Remediation Report
**File:** /home/localadmin/src/mosaic-stack/.lintstagedrc.mjs
**Tool Used:** Edit
**Epic:** general
**Iteration:** 2
**Generated:** 2026-01-30 13:12:17
## Status
Pending QA validation
## Next Steps
This report was created by the QA automation hook.
To process this report, run:
```bash
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-.lintstagedrc.mjs_20260130-1312_2_remediation_needed.md"
```

View File

@@ -0,0 +1,20 @@
# QA Remediation Report
**File:** /home/localadmin/src/mosaic-stack/.lintstagedrc.mjs
**Tool Used:** Edit
**Epic:** general
**Iteration:** 3
**Generated:** 2026-01-30 13:12:44
## Status
Pending QA validation
## Next Steps
This report was created by the QA automation hook.
To process this report, run:
```bash
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-.lintstagedrc.mjs_20260130-1312_3_remediation_needed.md"
```

View File

@@ -0,0 +1,20 @@
# QA Remediation Report
**File:** /home/localadmin/src/mosaic-stack/.lintstagedrc.mjs
**Tool Used:** Edit
**Epic:** general
**Iteration:** 4
**Generated:** 2026-01-30 13:12:59
## Status
Pending QA validation
## Next Steps
This report was created by the QA automation hook.
To process this report, run:
```bash
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-.lintstagedrc.mjs_20260130-1312_4_remediation_needed.md"
```

View File

@@ -0,0 +1,20 @@
# QA Remediation Report
**File:** /home/localadmin/src/mosaic-stack/packages/config/eslint/base.js
**Tool Used:** Edit
**Epic:** general
**Iteration:** 1
**Generated:** 2026-01-30 13:09:17
## Status
Pending QA validation
## Next Steps
This report was created by the QA automation hook.
To process this report, run:
```bash
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/home-localadmin-src-mosaic-stack-packages-config-eslint-base.js_20260130-1309_1_remediation_needed.md"
```

View File

@@ -0,0 +1,20 @@
# QA Remediation Report
**File:** /tmp/claude-1000/-home-localadmin-src-mosaic-stack/f3beb7a6-6cd5-4bee-8283-fac0798a92fa/scratchpad/test-violations.ts
**Tool Used:** Write
**Epic:** general
**Iteration:** 1
**Generated:** 2026-01-30 13:09:55
## Status
Pending QA validation
## Next Steps
This report was created by the QA automation hook.
To process this report, run:
```bash
claude -p "Use Task tool to launch universal-qa-agent for report: /home/localadmin/src/mosaic-stack/docs/reports/qa-automation/pending/tmp-claude-1000--home-localadmin-src-mosaic-stack-f3beb7a6-6cd5-4bee-8283-fac0798a92fa-scratchpad-test-violations.ts_20260130-1309_1_remediation_needed.md"
```

View File

@@ -26,7 +26,8 @@
"docker:logs": "docker compose logs -f",
"docker:ps": "docker compose ps",
"docker:build": "docker compose build",
"docker:restart": "docker compose restart"
"docker:restart": "docker compose restart",
"prepare": "husky install"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^8.26.0",
@@ -35,6 +36,9 @@
"eslint": "^9.21.0",
"eslint-config-prettier": "^10.1.0",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-security": "^3.0.1",
"husky": "^9.1.7",
"lint-staged": "^16.2.7",
"prettier": "^3.5.3",
"turbo": "^2.8.0",
"typescript": "^5.8.2",

View File

@@ -2,6 +2,8 @@ import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
import prettierConfig from "eslint-config-prettier";
import prettierPlugin from "eslint-plugin-prettier";
// @ts-expect-error - security plugin doesn't have types
import securityPlugin from "eslint-plugin-security";
export default tseslint.config(
eslint.configs.recommended,
@@ -11,19 +13,42 @@ export default tseslint.config(
{
plugins: {
prettier: prettierPlugin,
security: securityPlugin,
},
rules: {
// Prettier
"prettier/prettier": "error",
// Type Safety - STRICT (Quality Rails)
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/explicit-function-return-type": "warn",
"@typescript-eslint/explicit-module-boundary-types": "error",
"@typescript-eslint/no-unused-vars": [
"error",
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
],
"@typescript-eslint/consistent-type-imports": [
"error",
{ prefer: "type-imports" },
],
"@typescript-eslint/consistent-type-imports": ["error", { prefer: "type-imports" }],
// Promise/Async Safety (Quality Rails)
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-misused-promises": "error",
"@typescript-eslint/await-thenable": "error",
// Code Quality (Quality Rails)
"@typescript-eslint/no-var-requires": "error",
"@typescript-eslint/prefer-nullish-coalescing": "warn",
"@typescript-eslint/prefer-optional-chain": "warn",
// Security (Quality Rails)
"security/detect-object-injection": "off", // Too many false positives
"security/detect-non-literal-fs-filename": "warn",
"security/detect-non-literal-regexp": "warn",
"security/detect-unsafe-regex": "error",
"security/detect-buffer-noassert": "error",
"security/detect-eval-with-expression": "error",
"security/detect-no-csrf-before-method-override": "error",
"security/detect-possible-timing-attacks": "warn",
"security/detect-pseudoRandomBytes": "error",
},
},
{

View File

@@ -20,6 +20,7 @@
"eslint": "^9.21.0",
"eslint-config-prettier": "^10.1.0",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-security": "^3.0.1",
"prettier": "^3.5.3",
"typescript-eslint": "^8.26.0"
},

757
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff