refactor(public): remove website config files and clean package.json
REMOVED:
- tailwind.config.js (website CSS config)
- .eslintrc.json (website linting config)
- scripts/check-csp-violations.js (website CSP checking)
- scripts/install-gitleaks-hook.sh (dev tool)
- docs/architecture/ADR-001-public-repository-release-process.md (internal process)
UPDATED:
- package.json: Rewritten for framework (removed 17 website dependencies)
- Removed: bcrypt, csurf, i18next, jsonwebtoken, marked, multer, puppeteer,
sanitize-html, stripe, highlight.js, tailwindcss, autoprefixer, pa11y, etc
- Kept only: express, mongoose, winston, helmet, rate-limit, validator
- Changed name from tractatus-website to tractatus-framework
- Changed description to framework description
Result: Clean framework package, no website code
This commit is contained in:
parent
0bda5fddb2
commit
a6709bc9a5
6 changed files with 22 additions and 1111 deletions
159
.eslintrc.json
159
.eslintrc.json
|
|
@ -1,159 +0,0 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
// ===================================
|
||||
// inst_026: Client-Side Code Quality
|
||||
// ===================================
|
||||
|
||||
// No console.log in production code (console.error allowed)
|
||||
"no-console": ["error", {
|
||||
"allow": ["error", "warn"]
|
||||
}],
|
||||
|
||||
// Consistent code style
|
||||
"quotes": ["error", "single", {
|
||||
"avoidEscape": true,
|
||||
"allowTemplateLiterals": true
|
||||
}],
|
||||
"semi": ["error", "always"],
|
||||
"indent": ["error", 2, {
|
||||
"SwitchCase": 1
|
||||
}],
|
||||
"comma-dangle": ["error", "never"],
|
||||
|
||||
// No unused variables (prevents dead code)
|
||||
"no-unused-vars": ["error", {
|
||||
"argsIgnorePattern": "^_",
|
||||
"varsIgnorePattern": "^_"
|
||||
}],
|
||||
|
||||
// Require let/const instead of var
|
||||
"no-var": "error",
|
||||
"prefer-const": "error",
|
||||
|
||||
// Arrow functions consistency
|
||||
"arrow-spacing": ["error", {
|
||||
"before": true,
|
||||
"after": true
|
||||
}],
|
||||
"arrow-parens": ["error", "as-needed"],
|
||||
|
||||
// Best practices
|
||||
"eqeqeq": ["error", "always"],
|
||||
"no-eval": "error",
|
||||
"no-implied-eval": "error",
|
||||
"no-with": "error",
|
||||
"no-new-func": "error",
|
||||
|
||||
// Security (XSS prevention)
|
||||
"no-script-url": "error",
|
||||
"no-alert": "warn",
|
||||
|
||||
// Code quality
|
||||
"no-debugger": "error",
|
||||
"no-empty": "error",
|
||||
"no-extra-semi": "error",
|
||||
"no-unreachable": "error",
|
||||
"no-dupe-keys": "error",
|
||||
|
||||
// Spacing and formatting
|
||||
"space-before-function-paren": ["error", {
|
||||
"anonymous": "never",
|
||||
"named": "never",
|
||||
"asyncArrow": "always"
|
||||
}],
|
||||
"keyword-spacing": ["error", {
|
||||
"before": true,
|
||||
"after": true
|
||||
}],
|
||||
"space-infix-ops": "error",
|
||||
"comma-spacing": ["error", {
|
||||
"before": false,
|
||||
"after": true
|
||||
}],
|
||||
"brace-style": ["error", "1tbs", {
|
||||
"allowSingleLine": true
|
||||
}],
|
||||
|
||||
// Modern JavaScript
|
||||
"prefer-arrow-callback": "warn",
|
||||
"prefer-template": "warn",
|
||||
"object-shorthand": ["warn", "always"],
|
||||
|
||||
// Disable rules that conflict with Prettier (if used later)
|
||||
"max-len": ["warn", {
|
||||
"code": 120,
|
||||
"ignoreUrls": true,
|
||||
"ignoreStrings": true,
|
||||
"ignoreTemplateLiterals": true
|
||||
}]
|
||||
},
|
||||
|
||||
"overrides": [
|
||||
{
|
||||
// Frontend JavaScript (public/js/**)
|
||||
"files": ["public/js/**/*.js"],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": false
|
||||
},
|
||||
"globals": {
|
||||
"fetch": "readonly",
|
||||
"Headers": "readonly",
|
||||
"Request": "readonly",
|
||||
"Response": "readonly",
|
||||
"URL": "readonly",
|
||||
"URLSearchParams": "readonly"
|
||||
},
|
||||
"rules": {
|
||||
// Stricter rules for client-side code
|
||||
"no-console": ["error", {
|
||||
"allow": ["error"]
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
// Backend JavaScript (src/**)
|
||||
"files": ["src/**/*.js"],
|
||||
"env": {
|
||||
"browser": false,
|
||||
"node": true
|
||||
},
|
||||
"rules": {
|
||||
// Allow console in backend code
|
||||
"no-console": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
// Test files
|
||||
"files": ["tests/**/*.js", "**/*.test.js", "**/*.spec.js"],
|
||||
"env": {
|
||||
"jest": true,
|
||||
"node": true
|
||||
},
|
||||
"rules": {
|
||||
// Relax rules for tests
|
||||
"no-console": "off",
|
||||
"no-unused-expressions": "off"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
"ignorePatterns": [
|
||||
"node_modules/",
|
||||
"dist/",
|
||||
"build/",
|
||||
"coverage/",
|
||||
".claude/",
|
||||
"*.min.js"
|
||||
]
|
||||
}
|
||||
|
|
@ -1,609 +0,0 @@
|
|||
# ADR-001: Public Repository Release Process
|
||||
|
||||
**Status**: Accepted
|
||||
**Date**: 2025-10-21
|
||||
**Decision Makers**: User, Claude Code
|
||||
**Consulted**: Previous session handoffs, GitHub Release v3.5.0 experience
|
||||
**Informed**: Future sessions, potential contributors
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
The Tractatus framework was developed as an internal project with extensive research documentation, governance exploration, and implementation code mixed together. To make the framework useful to the broader community, we needed to release a public version focused on implementation while protecting internal research and strategy.
|
||||
|
||||
**Key Constraints**:
|
||||
- Internal repository contains proprietary governance research
|
||||
- Public repository should be implementation-focused only
|
||||
- Must appear professional to external users (world-class quality standard)
|
||||
- Need to prevent "bad actor bias" (misrepresentation of framework purpose)
|
||||
- GitHub is primary distribution channel
|
||||
|
||||
**Triggering Events**:
|
||||
1. Framework reached production-ready state (v3.5)
|
||||
2. Need to share implementation with potential adopters
|
||||
3. Desire to enable community contributions
|
||||
4. Educational purpose (demonstrate governance framework in practice)
|
||||
|
||||
---
|
||||
|
||||
## Decision
|
||||
|
||||
We will maintain **separate repositories** with distinct purposes and content:
|
||||
|
||||
### Internal Repository: `AgenticGovernance/tractatus`
|
||||
- **Purpose**: Complete project including research, governance exploration, website
|
||||
- **Audience**: Project team, internal development
|
||||
- **Content**: Everything (code, research, governance docs, internal strategy, session handoffs)
|
||||
- **Visibility**: Private or limited access
|
||||
|
||||
### Public Repository: `AgenticGovernance/tractatus-framework`
|
||||
- **Purpose**: Implementation framework for developers
|
||||
- **Audience**: Developers, implementers, researchers wanting to use the framework
|
||||
- **Content**: Implementation code, technical docs, API references, setup guides
|
||||
- **Visibility**: Public, community-driven
|
||||
|
||||
**Release Process**: Multi-phase cleanup → Professional documentation → GitHub Release → Community features
|
||||
|
||||
---
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
### Positive Drivers (Why separate repositories)
|
||||
|
||||
1. **Security**: Internal research and strategy must not be public
|
||||
2. **Clarity**: Developers need implementation docs, not governance theory
|
||||
3. **Professionalism**: Public repo must appear world-class (no internal clutter)
|
||||
4. **Prevent Misuse**: "Bad actor bias" incident showed AI can misrepresent framework
|
||||
5. **Contribution Quality**: Separate repos allow focused contribution guidelines
|
||||
|
||||
### Negative Drivers (Risks of single repository)
|
||||
|
||||
1. **Accidental Exposure**: Easy to accidentally publish internal docs
|
||||
2. **Confusion**: Mixed content creates unclear narrative
|
||||
3. **Attack Surface**: Internal strategy exposed to potential adversaries
|
||||
4. **Maintenance Burden**: Filtering what to sync increases complexity
|
||||
5. **Brand Risk**: Unprofessional appearance damages credibility
|
||||
|
||||
---
|
||||
|
||||
## Considered Alternatives
|
||||
|
||||
### Alternative 1: Single Repository with Branch Separation
|
||||
**Approach**: `main` branch public, `internal` branch private
|
||||
|
||||
**Pros**:
|
||||
- Single repository to manage
|
||||
- Easier to sync changes
|
||||
- No duplication of core code
|
||||
|
||||
**Cons**:
|
||||
- High risk of accidental merges
|
||||
- Complex git workflow for contributors
|
||||
- Still requires filtering commits
|
||||
- Easy to leak internal content
|
||||
|
||||
**Rejected because**: Security risk too high, complexity not worth marginal convenience
|
||||
|
||||
---
|
||||
|
||||
### Alternative 2: Monorepo with Public/Private Directories
|
||||
**Approach**: Public repo clones only `/framework` directory
|
||||
|
||||
**Pros**:
|
||||
- Clear separation in directory structure
|
||||
- Single source of truth for framework code
|
||||
- Automated sync possible
|
||||
|
||||
**Cons**:
|
||||
- git submodules or subtree complexity
|
||||
- Contributors see full structure (confusing)
|
||||
- Risk of publishing wrong directory
|
||||
- Difficult to review PRs cleanly
|
||||
|
||||
**Rejected because**: Complexity outweighs benefits, contributors confused by structure
|
||||
|
||||
---
|
||||
|
||||
### Alternative 3: Fully Separate Repositories (CHOSEN)
|
||||
**Approach**: Two independent repositories with manual sync
|
||||
|
||||
**Pros**:
|
||||
- Complete isolation (security)
|
||||
- Each repo has clear, focused purpose
|
||||
- Professional appearance (no clutter)
|
||||
- Simple mental model for contributors
|
||||
- Flexibility to diverge if needed
|
||||
|
||||
**Cons**:
|
||||
- Manual sync required for framework updates
|
||||
- Potential for repos to drift
|
||||
- Duplication of some documentation
|
||||
- Extra maintenance overhead
|
||||
|
||||
**Chosen because**: Security and clarity outweigh maintenance cost
|
||||
|
||||
---
|
||||
|
||||
## Implementation
|
||||
|
||||
### Phase 1-7: Repository Cleanup (82% reduction)
|
||||
**Goal**: Remove all non-implementation content from public repo
|
||||
|
||||
**Phases**:
|
||||
1. Remove test data and credentials
|
||||
2. Remove internal session handoffs
|
||||
3. Remove governance research documents
|
||||
4. Remove business strategy documents
|
||||
5. Remove project-specific website code
|
||||
6. Remove accidentally published scripts (95 scripts)
|
||||
7. Remove broken imports and fix crashes
|
||||
8. Fix critical startup crashes
|
||||
|
||||
**Result**: 615 files → 96 files (84% reduction), zero startup crashes
|
||||
|
||||
**Key Learning**: Each phase required systematic audit, not assumptions based on filenames
|
||||
|
||||
---
|
||||
|
||||
### Phase 8: Professional Documentation Suite
|
||||
**Goal**: Make repository appear world-class to external users
|
||||
|
||||
**Documents Created**:
|
||||
|
||||
#### 1. CHANGELOG.md
|
||||
- **Format**: Keep a Changelog (industry standard)
|
||||
- **Content**: Complete v3.5.0 initial release notes with features, technical highlights, requirements
|
||||
- **Length**: 158 lines
|
||||
- **Quality**: Publication-ready, semantic versioning compliant
|
||||
|
||||
#### 2. SECURITY.md
|
||||
- **Purpose**: Security vulnerability reporting and best practices
|
||||
- **Content**:
|
||||
- Reporting: security@agenticgovernance.digital
|
||||
- SLA: 48-hour acknowledgment
|
||||
- Supported versions table
|
||||
- Best practices (5 sections)
|
||||
- Compliance (OWASP Top 10)
|
||||
- Security audit history
|
||||
- **Length**: 232 lines
|
||||
- **Quality**: Professional, comprehensive
|
||||
|
||||
#### 3. README.md Enhancements
|
||||
- **Additions**:
|
||||
- 5 status badges (license, release, tests, Node.js, MongoDB)
|
||||
- Quick links section
|
||||
- Core services table with file links
|
||||
- 5 usage examples
|
||||
- API documentation section
|
||||
- Citation section (BibTeX for academics)
|
||||
- **Growth**: 215 → 372 lines (73% increase)
|
||||
- **Quality**: Professional, informative, actionable
|
||||
|
||||
---
|
||||
|
||||
### Phase 9: GitHub Release v3.5.0
|
||||
**Goal**: Provide stable release with downloadable packages
|
||||
|
||||
**Created**: https://github.com/AgenticGovernance/tractatus-framework/releases/tag/v3.5.0
|
||||
|
||||
**Release Notes Contents**:
|
||||
- 6 core services with descriptions
|
||||
- 4 support services
|
||||
- 9 database models
|
||||
- API endpoints summary
|
||||
- Docker deployment instructions
|
||||
- Testing infrastructure (17 tests passing)
|
||||
- Security features
|
||||
- Requirements (Node.js 18+, MongoDB 7.0+)
|
||||
|
||||
**Auto-Generated Downloads**:
|
||||
- tractatus-framework-3.5.0.zip
|
||||
- tractatus-framework-3.5.0.tar.gz
|
||||
|
||||
**Benefit**: Users can download stable release without git clone
|
||||
|
||||
---
|
||||
|
||||
### Phase 10: Community Features
|
||||
**Goal**: Enable community engagement
|
||||
|
||||
**Enabled**: GitHub Discussions
|
||||
- **URL**: https://github.com/AgenticGovernance/tractatus-framework/discussions
|
||||
- **Purpose**: Questions, implementation sharing, ideas
|
||||
- **Moderation**: Manual review by project team
|
||||
|
||||
**Future**: Contribution guidelines, issue templates, PR templates (deferred)
|
||||
|
||||
---
|
||||
|
||||
## Governance Rules Created
|
||||
|
||||
### inst_028 (Original - Deprecated)
|
||||
**Text**: "ONLY documentation and research materials MUST be synced to tractatus-framework public GitHub repository"
|
||||
|
||||
**Issue**: Too narrow, didn't prevent theoretical frameworks or governance guides
|
||||
|
||||
**Status**: Deprecated 2025-10-21 (consolidated into inst_063_CONSOLIDATED)
|
||||
|
||||
---
|
||||
|
||||
### inst_062 (Original - Deprecated)
|
||||
**Text**: "GitHub README.md must be reviewed weekly and 'Last Updated' date updated when material changes occur"
|
||||
|
||||
**Issue**: Separate from main public GitHub policy
|
||||
|
||||
**Status**: Deprecated 2025-10-21 (consolidated into inst_063_CONSOLIDATED)
|
||||
|
||||
---
|
||||
|
||||
### inst_063 (Original - Deprecated)
|
||||
**Text**: "Public GitHub (tractatus-framework) must remain implementation-focused. Prohibited without explicit approval: governance research, deliberation guides, theoretical frameworks..."
|
||||
|
||||
**Context**: Created after "bad actor bias" incident where AI converted implementation docs to authoritarian governance guide
|
||||
|
||||
**Issue**: Lacked weekly review requirement
|
||||
|
||||
**Status**: Deprecated 2025-10-21 (consolidated into inst_063_CONSOLIDATED)
|
||||
|
||||
---
|
||||
|
||||
### inst_063_CONSOLIDATED (Current - Active)
|
||||
**Full Rule**:
|
||||
```
|
||||
Public GitHub repository (tractatus-framework) must remain implementation-focused.
|
||||
|
||||
Prohibited without explicit approval:
|
||||
1. Governance research documents
|
||||
2. Pluralistic deliberation guides
|
||||
3. Theoretical frameworks
|
||||
4. Project-specific internal documentation
|
||||
5. Business strategy documents
|
||||
|
||||
Allowed:
|
||||
1. Technical implementation documentation
|
||||
2. API reference guides
|
||||
3. Code examples and tutorials
|
||||
4. Installation/setup guides
|
||||
5. Contribution guidelines
|
||||
|
||||
README.md must be reviewed weekly and "Last Updated" date updated when material
|
||||
changes occur. README is primary external interface - must be world-class and current.
|
||||
```
|
||||
|
||||
**Quadrant**: STRATEGIC
|
||||
**Persistence**: HIGH
|
||||
**Scope**: PERMANENT
|
||||
|
||||
**Notes**: Consolidated from inst_028, inst_062, inst_063. Created after bad actor incident where AI converted implementation docs to authoritarian governance guide. Prevents misrepresentation of framework purpose.
|
||||
|
||||
---
|
||||
|
||||
## "Bad Actor Bias" Incident
|
||||
|
||||
### What Happened
|
||||
During public repository preparation, AI assistant suggested converting implementation documentation into "governance guide for preventing bad actors" - completely misrepresenting the framework's purpose.
|
||||
|
||||
### Root Cause
|
||||
AI pattern recognition associated "governance framework" with "preventing bad behavior" despite explicit instructions that Tractatus is about:
|
||||
- Value pluralism (no single moral framework)
|
||||
- Human decision-making (AI facilitates, doesn't decide)
|
||||
- Explicit instructions over learned patterns (27027 failure mode)
|
||||
|
||||
### Impact
|
||||
If published, would have positioned framework as **authoritarian control tool** instead of **pluralistic deliberation facilitator** - exact opposite of actual purpose.
|
||||
|
||||
### Response
|
||||
1. Created inst_063 with explicit prohibited/allowed lists
|
||||
2. Enhanced with weekly README review requirement
|
||||
3. Added to CLAUDE.md as permanent warning
|
||||
4. Documented in this ADR as key learning
|
||||
|
||||
### Prevention
|
||||
- inst_063_CONSOLIDATED now blocks governance-focused content
|
||||
- Requires explicit user approval for any theoretical frameworks
|
||||
- Weekly README reviews catch drift toward misrepresentation
|
||||
- ADR documents correct positioning for future reference
|
||||
|
||||
---
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive Consequences
|
||||
|
||||
1. **Security**: Internal research protected, zero risk of accidental exposure
|
||||
2. **Clarity**: Public repo has clear, focused purpose (implementation framework)
|
||||
3. **Professionalism**: External users see world-class quality (badges, docs, releases)
|
||||
4. **Contribution Quality**: Contributors understand framework is development tool, not governance theory
|
||||
5. **Brand Protection**: inst_063_CONSOLIDATED prevents misrepresentation
|
||||
6. **Community Ready**: Discussions enabled, professional appearance, downloadable releases
|
||||
|
||||
### Negative Consequences
|
||||
|
||||
1. **Manual Sync**: Framework code changes must be manually synced between repos
|
||||
2. **Duplication**: Some documentation exists in both repos (README, CONTRIBUTING)
|
||||
3. **Maintenance Overhead**: Two repos to manage, update, monitor
|
||||
4. **Potential Drift**: Repos could diverge if sync discipline lapses
|
||||
5. **Contributor Confusion**: Contributors may not understand relationship between repos
|
||||
|
||||
### Mitigation Strategies
|
||||
|
||||
**For Manual Sync**:
|
||||
- Document sync process in internal CLAUDE.md
|
||||
- Use git tags to track which versions synced
|
||||
- Quarterly sync reviews to catch drift
|
||||
|
||||
**For Duplication**:
|
||||
- Accept duplication as cost of clarity
|
||||
- Keep duplicated docs minimal (README, LICENSE only)
|
||||
- Use automation where possible (CI/CD for tests)
|
||||
|
||||
**For Maintenance**:
|
||||
- Schedule weekly reviews per inst_063_CONSOLIDATED
|
||||
- Use GitHub Actions for automated checks
|
||||
- Leverage community for issue reporting
|
||||
|
||||
**For Contributor Confusion**:
|
||||
- Clear README explanation of repository purpose
|
||||
- Contribution guidelines specify scope
|
||||
- Issue templates guide appropriate contributions
|
||||
|
||||
---
|
||||
|
||||
## Metrics
|
||||
|
||||
### Repository Reduction
|
||||
- **Before**: 615 files (mixed content)
|
||||
- **After**: 96 files (implementation only)
|
||||
- **Reduction**: 84% fewer files
|
||||
- **Benefit**: Clarity, professionalism, security
|
||||
|
||||
### Documentation Quality
|
||||
- **README Growth**: 215 → 372 lines (+73%)
|
||||
- **New Docs**: CHANGELOG.md (158 lines), SECURITY.md (232 lines)
|
||||
- **Badges Added**: 5 (license, release, tests, Node.js, MongoDB)
|
||||
- **Quality**: Publication-ready, professional
|
||||
|
||||
### Community Engagement
|
||||
- **GitHub Release**: v3.5.0 with downloadable packages
|
||||
- **Discussions**: Enabled (community questions, sharing, ideas)
|
||||
- **Contributors**: Ready to accept (guidelines in place)
|
||||
|
||||
### Governance
|
||||
- **Rules Created**: inst_063_CONSOLIDATED (consolidated from 3 rules)
|
||||
- **Protection**: "Bad actor bias" incident prevention
|
||||
- **Maintenance**: Weekly README review requirement
|
||||
|
||||
---
|
||||
|
||||
## Lessons Learned
|
||||
|
||||
### 1. Assumptions Are Dangerous
|
||||
**Observation**: Initial cleanup phases assumed filenames indicated content
|
||||
**Learning**: Must READ files, not assume based on names
|
||||
**Example**: Files named "guide" contained implementation details, files named "implementation" contained theory
|
||||
|
||||
**Applied**: User repeatedly caught superficial cleanup, forced systematic audit
|
||||
|
||||
---
|
||||
|
||||
### 2. AI Pattern Recognition Can Misrepresent Purpose
|
||||
**Observation**: AI suggested converting framework to "bad actor prevention" guide
|
||||
**Learning**: AI training creates strong associations that override explicit instructions
|
||||
**Example**: "Governance" → "preventing bad behavior" despite pluralistic values focus
|
||||
|
||||
**Applied**: Created inst_063 with explicit prohibited/allowed lists, weekly reviews
|
||||
|
||||
---
|
||||
|
||||
### 3. Professional Appearance Matters
|
||||
**Observation**: Initial public repo lacked badges, changelog, security policy
|
||||
**Learning**: External users judge quality by presentation, not just code
|
||||
**Example**: No CHANGELOG.md signals "hobby project", not "production framework"
|
||||
|
||||
**Applied**: Created comprehensive documentation suite, GitHub Release with downloads
|
||||
|
||||
---
|
||||
|
||||
### 4. Separate Repositories Reduce Risk
|
||||
**Observation**: Single repository with filtering creates accidental exposure risk
|
||||
**Learning**: Complete separation is simplest mental model, safest approach
|
||||
**Example**: Wrong directory sync could expose all internal research
|
||||
|
||||
**Applied**: Fully separate repos, manual sync with discipline over automation
|
||||
|
||||
---
|
||||
|
||||
### 5. Community Needs Clear Narrative
|
||||
**Observation**: Mixed content creates confusion about framework purpose
|
||||
**Learning**: Public repo must tell coherent story: "development tool for AI safety"
|
||||
**Example**: Governance research dilutes implementation narrative
|
||||
|
||||
**Applied**: Public repo is implementation-only, clear README with purpose/examples
|
||||
|
||||
---
|
||||
|
||||
### 6. Weekly Reviews Prevent Drift
|
||||
**Observation**: Repositories evolve over time, purpose can drift
|
||||
**Learning**: Regular reviews maintain alignment with intended positioning
|
||||
**Example**: README could gradually accumulate governance theory without oversight
|
||||
|
||||
**Applied**: inst_063_CONSOLIDATED requires weekly README review, "Last Updated" tracking
|
||||
|
||||
---
|
||||
|
||||
### 7. World-Class Quality Takes Iterations
|
||||
**Observation**: User rejected multiple cleanup attempts as insufficient
|
||||
**Learning**: True quality requires systematic audit, not quick passes
|
||||
**Example**: 8 phases of cleanup, each catching issues previous phases missed
|
||||
|
||||
**Applied**: No shortcuts, no assumptions, comprehensive verification
|
||||
|
||||
---
|
||||
|
||||
## Review Schedule
|
||||
|
||||
### Weekly Reviews (per inst_063_CONSOLIDATED)
|
||||
- **What**: README.md content and "Last Updated" date
|
||||
- **Who**: Project team (human approval required)
|
||||
- **Check**: Alignment with implementation focus, no governance theory drift
|
||||
|
||||
### Monthly Reviews
|
||||
- **What**: Public repository content audit
|
||||
- **Who**: Project team
|
||||
- **Check**: No internal docs accidentally published, documentation current
|
||||
|
||||
### Quarterly Reviews
|
||||
- **What**: Sync status between internal and public repos
|
||||
- **Who**: Project team
|
||||
- **Check**: Framework code in sync, public repo has latest stable implementation
|
||||
|
||||
### Post-Release Reviews
|
||||
- **What**: Community feedback, GitHub Discussions, issue tracker
|
||||
- **Who**: Project team
|
||||
- **Check**: Contributor understanding, purpose clarity, misrepresentation risk
|
||||
|
||||
---
|
||||
|
||||
## Related Decisions
|
||||
|
||||
### Supersedes
|
||||
- None (first ADR in architecture/ directory)
|
||||
|
||||
### Superseded By
|
||||
- None (current)
|
||||
|
||||
### Related ADRs
|
||||
- ADR-002: Multi-project governance architecture (future)
|
||||
- ADR-003: Continuous enforcement architecture (future)
|
||||
|
||||
### Related Rules
|
||||
- inst_063_CONSOLIDATED: Public GitHub management (active)
|
||||
- inst_028: Original sync rule (deprecated)
|
||||
- inst_062: Original README review rule (deprecated)
|
||||
- inst_063: Original public GitHub rule (deprecated)
|
||||
|
||||
---
|
||||
|
||||
## Appendix A: Public Repository Contents (v3.5.0)
|
||||
|
||||
### Core Services (6)
|
||||
1. InstructionPersistenceClassifier
|
||||
2. CrossReferenceValidator
|
||||
3. BoundaryEnforcer
|
||||
4. ContextPressureMonitor
|
||||
5. MetacognitiveVerifier
|
||||
6. PluralisticDeliberationOrchestrator
|
||||
|
||||
### Support Services (4)
|
||||
1. MemoryProxyService
|
||||
2. AuditService
|
||||
3. GovernanceService
|
||||
4. SchedulerService
|
||||
|
||||
### Database Models (9)
|
||||
1. GovernanceRule
|
||||
2. Project
|
||||
3. SessionState
|
||||
4. AuditLog
|
||||
5. GovernanceLog
|
||||
6. MemoryEntry
|
||||
7. ScheduledJob
|
||||
8. TokenCheckpoint
|
||||
9. DeliberationSession
|
||||
|
||||
### Documentation Files (7)
|
||||
1. README.md (372 lines)
|
||||
2. CHANGELOG.md (158 lines)
|
||||
3. SECURITY.md (232 lines)
|
||||
4. LICENSE (Apache 2.0)
|
||||
5. CONTRIBUTING.md
|
||||
6. CODE_OF_CONDUCT.md
|
||||
7. .gitignore
|
||||
|
||||
### Total Files: 96 (down from 615)
|
||||
|
||||
---
|
||||
|
||||
## Appendix B: Sync Process (Internal → Public)
|
||||
|
||||
### When to Sync
|
||||
1. Framework core services modified
|
||||
2. Database models changed
|
||||
3. API endpoints updated
|
||||
4. Documentation improvements
|
||||
5. Security fixes
|
||||
6. Major features completed
|
||||
|
||||
### What to Sync
|
||||
- ✅ `/src/services/` (framework components)
|
||||
- ✅ `/src/models/` (database schemas)
|
||||
- ✅ `/src/routes/` (API endpoints)
|
||||
- ✅ `/src/middleware/tractatus/` (framework enforcement)
|
||||
- ✅ `/tests/` (unit and integration tests)
|
||||
- ✅ `README.md`, `CHANGELOG.md`, `SECURITY.md`
|
||||
- ❌ `/docs/governance/` (internal research)
|
||||
- ❌ `/docs/research/` (theoretical frameworks)
|
||||
- ❌ `.claude/` (session management)
|
||||
- ❌ `/scripts/` (internal tooling - most scripts)
|
||||
|
||||
### How to Sync
|
||||
1. Create feature branch in internal repo
|
||||
2. Test thoroughly in internal environment
|
||||
3. Create corresponding branch in public repo
|
||||
4. Copy synced files (listed above)
|
||||
5. Run tests in public repo
|
||||
6. Create PR in public repo
|
||||
7. Review for accidental internal content
|
||||
8. Merge to public/main
|
||||
9. Tag release if appropriate
|
||||
10. Update CHANGELOG.md
|
||||
|
||||
### Sync Checklist
|
||||
- [ ] No internal research documents included
|
||||
- [ ] No session handoffs or governance exploration
|
||||
- [ ] No business strategy or project plans
|
||||
- [ ] README.md "Last Updated" current
|
||||
- [ ] CHANGELOG.md updated if release
|
||||
- [ ] Tests passing in public repo
|
||||
- [ ] No references to internal-only features
|
||||
- [ ] Documentation focuses on implementation
|
||||
|
||||
---
|
||||
|
||||
## Appendix C: "Bad Actor Bias" Prevention
|
||||
|
||||
### Prohibited Content Examples
|
||||
❌ "Governance framework for preventing bad actors"
|
||||
❌ "Ensure AI systems behave correctly"
|
||||
❌ "Enforce ethical behavior in autonomous agents"
|
||||
❌ "Authoritative decision-making for AI safety"
|
||||
❌ "Centralized control of AI values"
|
||||
|
||||
### Allowed Content Examples
|
||||
✅ "Development tool for implementing explicit instruction persistence"
|
||||
✅ "Framework for maintaining human authority in AI-assisted decisions"
|
||||
✅ "Facilitates pluralistic deliberation across value systems"
|
||||
✅ "Prevents pattern recognition from overriding user instructions"
|
||||
✅ "Enables value-aware AI systems with human oversight"
|
||||
|
||||
### Detection Keywords (Review Triggers)
|
||||
- "prevent bad actors"
|
||||
- "enforce behavior"
|
||||
- "ensure compliance"
|
||||
- "authoritative control"
|
||||
- "centralized governance"
|
||||
- "correct AI behavior"
|
||||
|
||||
### Correct Positioning
|
||||
**Tractatus IS**: A development tool (like an IDE or linter) that helps implement AI systems respecting:
|
||||
- Explicit instructions over learned patterns
|
||||
- Human decision-making in values-sensitive domains
|
||||
- Pluralistic moral frameworks (no hierarchy)
|
||||
- Context-aware session management
|
||||
|
||||
**Tractatus IS NOT**: A governance system, ethical enforcement tool, or bad actor prevention mechanism
|
||||
|
||||
---
|
||||
|
||||
**END OF ADR-001**
|
||||
78
package.json
78
package.json
|
|
@ -1,83 +1,49 @@
|
|||
{
|
||||
"name": "tractatus-website",
|
||||
"version": "0.1.0",
|
||||
"description": "Tractatus-Based LLM Safety Framework website platform",
|
||||
"name": "tractatus-framework",
|
||||
"version": "3.5.0",
|
||||
"description": "AI governance framework enforcing architectural safety constraints at runtime",
|
||||
"main": "src/server.js",
|
||||
"scripts": {
|
||||
"start": "node src/server.js",
|
||||
"dev": "concurrently -n \"SERVER,WATCHDOG\" -c \"cyan,magenta\" \"nodemon src/server.js\" \"node scripts/framework-watchdog.js\"",
|
||||
"dev:simple": "nodemon src/server.js",
|
||||
"build:css": "npx tailwindcss -i ./public/css/src/tailwind.css -o ./public/css/tailwind.css --minify",
|
||||
"watch:css": "npx tailwindcss -i ./public/css/src/tailwind.css -o ./public/css/tailwind.css --watch",
|
||||
"update:cache": "node scripts/update-cache-version.js",
|
||||
"build": "npm run update:cache && npm run build:css",
|
||||
"test": "jest --coverage",
|
||||
"test:watch": "jest --watch",
|
||||
"test:unit": "jest tests/unit",
|
||||
"test:integration": "jest tests/integration",
|
||||
"test:security": "jest tests/security",
|
||||
"lint": "eslint src/ tests/",
|
||||
"lint:fix": "eslint src/ tests/ --fix",
|
||||
"migrate:docs": "node scripts/migrate-documents.js",
|
||||
"init:db": "node scripts/init-db.js",
|
||||
"init:koha": "node scripts/init-koha.js",
|
||||
"seed:admin": "node scripts/seed-admin.js",
|
||||
"seed:projects": "node scripts/seed-projects.js",
|
||||
"generate:pdfs": "node scripts/generate-pdfs.js",
|
||||
"deploy": "npm run build && bash scripts/deploy-frontend.sh",
|
||||
"framework:init": "node scripts/session-init.js",
|
||||
"framework:watchdog": "node scripts/framework-watchdog.js",
|
||||
"framework:check": "node scripts/pre-action-check.js",
|
||||
"framework:recover": "node scripts/recover-framework.js",
|
||||
"check:csp": "node scripts/check-csp-violations.js",
|
||||
"fix:csp": "node scripts/fix-csp-violations.js"
|
||||
"test:integration": "jest tests/integration"
|
||||
},
|
||||
"keywords": [
|
||||
"ai-governance",
|
||||
"ai-safety",
|
||||
"llm",
|
||||
"tractatus",
|
||||
"digital-sovereignty",
|
||||
"ai-governance"
|
||||
"llm-governance",
|
||||
"framework",
|
||||
"runtime-constraints"
|
||||
],
|
||||
"author": "John Stroh <john.stroh.nz@pm.me>",
|
||||
"author": "Agentic Governance Project",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/AgenticGovernance/tractatus-framework.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"bcrypt": "^5.1.1",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
"csurf": "^1.11.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"express-rate-limit": "^7.5.1",
|
||||
"helmet": "^7.1.0",
|
||||
"highlight.js": "^11.9.0",
|
||||
"i18next": "^25.6.0",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"marked": "^11.0.0",
|
||||
"mongodb": "^6.3.0",
|
||||
"mongoose": "^8.19.1",
|
||||
"multer": "^2.0.2",
|
||||
"puppeteer": "^24.23.0",
|
||||
"sanitize-html": "^2.11.0",
|
||||
"stripe": "^19.1.0",
|
||||
"validator": "^13.15.15",
|
||||
"winston": "^3.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anthropic-ai/sdk": "^0.65.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"axe-core": "^4.10.3",
|
||||
"concurrently": "^9.2.1",
|
||||
"eslint": "^8.56.0",
|
||||
"jest": "^29.7.0",
|
||||
"nodemon": "^3.0.2",
|
||||
"pa11y": "^9.0.1",
|
||||
"pa11y-reporter-html": "^2.0.0",
|
||||
"postcss": "^8.5.6",
|
||||
"supertest": "^6.3.3",
|
||||
"tailwindcss": "^3.4.18"
|
||||
"supertest": "^6.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@anthropic-ai/sdk": "^0.65.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@anthropic-ai/sdk": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0",
|
||||
|
|
|
|||
|
|
@ -1,113 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* CSP Violations Checker
|
||||
* Enforces Content Security Policy compliance (inst_008)
|
||||
*
|
||||
* Checks staged files for:
|
||||
* - Inline scripts (<script> tags with code)
|
||||
* - Inline event handlers (onclick, onload, etc.)
|
||||
* - Inline styles in HTML
|
||||
*
|
||||
* Does NOT check:
|
||||
* - Non-HTML files (JS, CSS, MD, etc.)
|
||||
* - <script src="..."> external scripts
|
||||
*/
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Get list of staged files
|
||||
let stagedFiles;
|
||||
try {
|
||||
stagedFiles = execSync('git diff --cached --name-only --diff-filter=ACMR', { encoding: 'utf8' })
|
||||
.split('\n')
|
||||
.filter(f => f.trim() !== '');
|
||||
} catch (error) {
|
||||
console.error('Error getting staged files:', error.message);
|
||||
process.exit(0); // Allow commit if can't check
|
||||
}
|
||||
|
||||
// Filter to HTML files only
|
||||
const htmlFiles = stagedFiles.filter(f => f.endsWith('.html'));
|
||||
|
||||
if (htmlFiles.length === 0) {
|
||||
// No HTML files, nothing to check
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
let violations = [];
|
||||
|
||||
// Check each HTML file
|
||||
htmlFiles.forEach(file => {
|
||||
const filePath = path.join(process.cwd(), file);
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return; // File deleted, skip
|
||||
}
|
||||
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const lines = content.split('\n');
|
||||
|
||||
lines.forEach((line, index) => {
|
||||
const lineNum = index + 1;
|
||||
|
||||
// Check for inline scripts (but not <script src="...">)
|
||||
if (/<script(?!.*src=)[^>]*>[\s\S]*?<\/script>/i.test(line)) {
|
||||
if (line.includes('<script>') || (line.includes('<script ') && !line.includes('src='))) {
|
||||
violations.push({
|
||||
file,
|
||||
line: lineNum,
|
||||
type: 'inline-script',
|
||||
content: line.trim().substring(0, 80)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check for inline event handlers
|
||||
const inlineHandlers = ['onclick', 'onload', 'onmouseover', 'onsubmit', 'onerror', 'onchange'];
|
||||
inlineHandlers.forEach(handler => {
|
||||
if (new RegExp(`\\s${handler}=`, 'i').test(line)) {
|
||||
violations.push({
|
||||
file,
|
||||
line: lineNum,
|
||||
type: 'inline-handler',
|
||||
handler,
|
||||
content: line.trim().substring(0, 80)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Check for inline styles (style attribute)
|
||||
if (/\sstyle\s*=\s*["'][^"']*["']/i.test(line)) {
|
||||
// Allow Tailwind utility classes pattern (common false positive)
|
||||
if (!line.includes('class=') || line.match(/style\s*=\s*["'][^"']{20,}/)) {
|
||||
violations.push({
|
||||
file,
|
||||
line: lineNum,
|
||||
type: 'inline-style',
|
||||
content: line.trim().substring(0, 80)
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (violations.length === 0) {
|
||||
process.exit(0); // No violations, allow commit
|
||||
}
|
||||
|
||||
// Report violations
|
||||
console.error('\nCSP Violations Found:\n');
|
||||
violations.forEach(v => {
|
||||
console.error(` ${v.file}:${v.line}`);
|
||||
console.error(` Type: ${v.type}${v.handler ? ' (' + v.handler + ')' : ''}`);
|
||||
console.error(` Content: ${v.content}`);
|
||||
console.error('');
|
||||
});
|
||||
|
||||
console.error(`Total violations: ${violations.length}\n`);
|
||||
console.error('Fix these violations or use --no-verify to bypass (not recommended)\n');
|
||||
|
||||
process.exit(1); // Block commit
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Install Gitleaks Pre-Commit Hook
|
||||
# Created in response to Anthropic API key exposure incident (2025-10-21)
|
||||
# Implements inst_070: Pre-Commit Secret Detection
|
||||
|
||||
set -e
|
||||
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
echo " INSTALL GITLEAKS PRE-COMMIT HOOK"
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
|
||||
# Check if gitleaks is installed
|
||||
echo "▶ 1. Checking gitleaks installation..."
|
||||
if ! command -v gitleaks &> /dev/null; then
|
||||
echo " ✗ gitleaks not found"
|
||||
echo ""
|
||||
echo " Please install gitleaks first:"
|
||||
echo ""
|
||||
echo " macOS (Homebrew):"
|
||||
echo " brew install gitleaks"
|
||||
echo ""
|
||||
echo " Ubuntu/Debian:"
|
||||
echo " sudo apt-get update"
|
||||
echo " sudo apt-get install gitleaks"
|
||||
echo ""
|
||||
echo " Or download from: https://github.com/gitleaks/gitleaks/releases"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GITLEAKS_VERSION=$(gitleaks version 2>&1 | head -1 || echo "unknown")
|
||||
echo " ✓ gitleaks found: $GITLEAKS_VERSION"
|
||||
echo ""
|
||||
|
||||
# Check if we're in a git repository
|
||||
echo "▶ 2. Checking git repository..."
|
||||
if [ ! -d .git ]; then
|
||||
echo " ✗ Not a git repository (no .git directory found)"
|
||||
echo " Run this script from the root of your git repository"
|
||||
exit 1
|
||||
fi
|
||||
echo " ✓ Git repository detected"
|
||||
echo ""
|
||||
|
||||
# Create .gitleaksignore if it doesn't exist
|
||||
echo "▶ 3. Creating .gitleaksignore file..."
|
||||
if [ -f .gitleaksignore ]; then
|
||||
echo " ⚠ .gitleaksignore already exists (not overwriting)"
|
||||
else
|
||||
cat > .gitleaksignore << 'EOF'
|
||||
# Gitleaks Ignore File
|
||||
# Created: 2025-10-21 (Post-breach security enhancement)
|
||||
#
|
||||
# Add false positives here with explanations
|
||||
# Format: regex pattern (one per line)
|
||||
#
|
||||
# Example:
|
||||
# sk-ant-api03-EXAMPLE-REDACTED-NEVER-USE # Documentation placeholder
|
||||
|
||||
# Documentation placeholders (safe patterns)
|
||||
sk-ant-api03-EXAMPLE-REDACTED
|
||||
sk_live_EXAMPLE_REDACTED
|
||||
pk_live_EXAMPLE_REDACTED
|
||||
your-password-here
|
||||
your-token-here
|
||||
REDACTED
|
||||
|
||||
# Test fixtures (if needed)
|
||||
# Add specific test patterns here with comments
|
||||
EOF
|
||||
echo " ✓ Created .gitleaksignore with default placeholders"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Create pre-commit hook
|
||||
echo "▶ 4. Creating pre-commit hook..."
|
||||
HOOK_FILE=.git/hooks/pre-commit
|
||||
|
||||
if [ -f "$HOOK_FILE" ]; then
|
||||
# Backup existing hook
|
||||
BACKUP_FILE="${HOOK_FILE}.backup-$(date +%s)"
|
||||
cp "$HOOK_FILE" "$BACKUP_FILE"
|
||||
echo " ⚠ Existing pre-commit hook backed up to:"
|
||||
echo " $(basename $BACKUP_FILE)"
|
||||
echo ""
|
||||
echo " You may need to manually merge hooks if you had custom logic."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
cat > "$HOOK_FILE" << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
# Gitleaks Pre-Commit Hook
|
||||
# Created: 2025-10-21 (Post-breach security enhancement)
|
||||
# Implements: inst_070 (Pre-Commit Secret Detection)
|
||||
#
|
||||
# This hook runs gitleaks on staged files before allowing commits.
|
||||
# If secrets are detected, the commit is BLOCKED.
|
||||
|
||||
echo "🔍 Running gitleaks secret detection..."
|
||||
|
||||
# Run gitleaks on staged files
|
||||
if ! gitleaks protect --staged --verbose; then
|
||||
echo ""
|
||||
echo "❌ COMMIT BLOCKED: Secrets detected by gitleaks"
|
||||
echo ""
|
||||
echo "Actions:"
|
||||
echo " 1. Review the detected secrets above"
|
||||
echo " 2. Remove actual secrets from staged files"
|
||||
echo " 3. If false positive:"
|
||||
echo " - Verify it's NOT a real secret"
|
||||
echo " - Add pattern to .gitleaksignore with explanation"
|
||||
echo " - Get human approval before committing"
|
||||
echo " - Document in commit message"
|
||||
echo ""
|
||||
echo "NEVER bypass this hook without explicit user approval"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ No secrets detected - commit allowed"
|
||||
exit 0
|
||||
EOF
|
||||
|
||||
chmod +x "$HOOK_FILE"
|
||||
echo " ✓ Pre-commit hook created and made executable"
|
||||
echo ""
|
||||
|
||||
# Test the hook
|
||||
echo "▶ 5. Testing hook installation..."
|
||||
if [ -x "$HOOK_FILE" ]; then
|
||||
echo " ✓ Hook is executable"
|
||||
else
|
||||
echo " ✗ Hook is not executable (this should not happen)"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
echo " ✅ INSTALLATION COMPLETE"
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
echo "The pre-commit hook will now run automatically on every commit."
|
||||
echo ""
|
||||
echo "To test it:"
|
||||
echo " 1. Create a test file with a fake secret:"
|
||||
echo " echo 'sk-ant-api03-REAL_SECRET_KEY' > test-secret.txt"
|
||||
echo " 2. Stage and attempt to commit:"
|
||||
echo " git add test-secret.txt"
|
||||
echo " git commit -m 'test'"
|
||||
echo " 3. Commit should be BLOCKED with gitleaks error"
|
||||
echo " 4. Clean up:"
|
||||
echo " git reset HEAD test-secret.txt"
|
||||
echo " rm test-secret.txt"
|
||||
echo ""
|
||||
echo "Files created:"
|
||||
echo " - .git/hooks/pre-commit (executable hook)"
|
||||
echo " - .gitleaksignore (allowed patterns)"
|
||||
echo ""
|
||||
echo "Implements: inst_070 (Pre-Commit Secret Detection)"
|
||||
echo ""
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./public/**/*.html",
|
||||
"./public/**/*.js",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue