tractatus/scripts/relicense-apache-to-eupl.js
TheFlow d600f6ed83
Some checks are pending
CI / Run Tests (push) Waiting to run
CI / Lint Code (push) Waiting to run
CI / CSP Compliance Check (push) Waiting to run
chore(license): Phase B — relicense source files from Apache 2.0 to EUPL-1.2
Phase B of PLAN_LICENSE_STANDARDISATION_EUPL12_20260419. Follows Phase A
(c85f310f, 4ddc54a0) which flipped the LICENSE file + README; this commit
propagates EUPL-1.2 through source-file headers.

21 files touched across 4 distinct Apache-reference variants:

- V1 (14 files) — full Apache header block (JS /* ... */): 2 routes + 1
  controller + 7 services + 2 models + 3 utils. Replaced with equivalent
  EUPL-1.2 block pointing at EC canonical URL.
- V2 (2 files) — inline JSDoc license line (Copyright Tractatus Project):
  src/routes/calendar.routes.js + src/models/ScheduledTask.model.js.
  Replaced with EUPL-1.2 v. 1.2 equivalent.
- V3 (4 files) — Python docstring 'License: Apache 2.0': all 4 al-integration
  Python files. Replaced with 'License: EUPL-1.2'.
- V4 (1 file) — al-integration/README.md bare 'Apache 2.0' under '## License'
  heading. Replaced with 'EUPL-1.2'.

Verification:
- grep -r "Apache License|Apache 2.0|apache.org/licenses" src/ al-integration/
  returns zero matches (modulo venv).
- Unit tests: 524/524 pass (npm run test:unit).
- Integration test failures (177) are DB-connection infrastructure, pre-existing,
  unrelated to this header-only change.

Sole author basis: TheFlow, 930+ commits, unilateral relicensing (same as Phase A).

Replacement infrastructure also committed: scripts/relicense-apache-to-eupl.js
(auto-detecting variant replacement, idempotent, --dry-run mode). Reusable for
Phase C (community-repo sweep) if pattern structure aligns.

Out-of-scope Apache mentions still in the repo (next pass, NOT Phase B):
- SESSION_HANDOFF_ENFORCEMENT_COMPLETE.md (root doc)
- CLAUDE_Tractatus_Maintenance_Guide.md (root doc)
- For Claude Web/tractatus-claude-web-complete/** (docs snapshot subdirectory)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 20:32:09 +12:00

157 lines
6 KiB
JavaScript

#!/usr/bin/env node
/*
* Copyright 2025 John G Stroh
*
* Licensed under the European Union Public Licence, Version 1.2 (EUPL-1.2);
* you may not use this file except in compliance with the Licence.
*
* You may obtain a copy of the Licence at:
* https://interoperable-europe.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*/
// Relicense Apache 2.0 → EUPL-1.2 across Tractatus source files.
// Phase B of PLAN_LICENSE_STANDARDISATION_EUPL12_20260419.
//
// Usage:
// node scripts/relicense-apache-to-eupl.js # Apply changes
// node scripts/relicense-apache-to-eupl.js --dry-run # Show changes without writing
const fs = require('fs');
const path = require('path');
const DRY_RUN = process.argv.includes('--dry-run');
const REPO_ROOT = path.resolve(__dirname, '..');
// --- Variant 1: Full Apache header block (JS /* ... */) ---
const V1_APACHE = `/*
* Copyright 2025 John G Stroh
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/`;
const V1_EUPL = `/*
* Copyright 2025 John G Stroh
*
* Licensed under the European Union Public Licence, Version 1.2 (EUPL-1.2);
* you may not use this file except in compliance with the Licence.
*
* You may obtain a copy of the Licence at:
* https://interoperable-europe.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*/`;
// --- Variant 2: Inline JSDoc license lines (inside descriptive block) ---
const V2_APACHE = ` * Copyright 2025 Tractatus Project\n * Licensed under Apache License 2.0`;
const V2_EUPL = ` * Copyright 2025 Tractatus Project\n * Licensed under the European Union Public Licence, v. 1.2 (EUPL-1.2)`;
// --- Variant 3: Python docstring License line ---
const V3_APACHE = `License: Apache 2.0`;
const V3_EUPL = `License: EUPL-1.2`;
// --- Variant 4: al-integration/README.md — bare "Apache 2.0" under "## License" heading ---
const V4_APACHE = `## License\n\nApache 2.0`;
const V4_EUPL = `## License\n\nEUPL-1.2`;
const FILES = [
// Variant 1 expected — full Apache block (12 JS files)
'src/routes/audit.routes.js',
'src/controllers/audit.controller.js',
'src/services/AdaptiveCommunicationOrchestrator.service.js',
'src/services/InstructionPersistenceClassifier.service.js',
'src/services/MetacognitiveVerifier.service.js',
'src/services/ContextPressureMonitor.service.js',
'src/services/BoundaryEnforcer.service.js',
'src/services/CrossReferenceValidator.service.js',
'src/services/PluralisticDeliberationOrchestrator.service.js',
'src/utils/attack-surface-validator.util.js',
'src/utils/activity-classifier.util.js',
'src/utils/audit-sanitizer.util.js',
// Variant 2 expected — inline JSDoc license lines (4 JS files)
'src/routes/calendar.routes.js',
'src/models/ScheduledTask.model.js',
'src/models/SyncMetadata.model.js',
'src/models/MissedBreach.model.js',
// Variant 3 expected — Python docstring (4 files)
'al-integration/testing/stress_test.py',
'al-integration/testing/stress_test_vllm.py',
'al-integration/training/train_analyzer.py',
'al-integration/agents/feedback_analyzer.py',
// Variant 4 expected — al-integration README (1 file)
'al-integration/README.md',
];
function processFile(relPath) {
const fullPath = path.join(REPO_ROOT, relPath);
if (!fs.existsSync(fullPath)) {
return { status: 'NOT_FOUND', variants: [] };
}
let content = fs.readFileSync(fullPath, 'utf8');
const original = content;
const variants = [];
if (content.includes(V1_APACHE)) {
content = content.replace(V1_APACHE, V1_EUPL);
variants.push('V1');
}
if (content.includes(V2_APACHE)) {
content = content.replace(V2_APACHE, V2_EUPL);
variants.push('V2');
}
if (content.includes(V3_APACHE)) {
content = content.replace(V3_APACHE, V3_EUPL);
variants.push('V3');
}
if (content.includes(V4_APACHE)) {
content = content.replace(V4_APACHE, V4_EUPL);
variants.push('V4');
}
if (content === original) {
return { status: 'NO_MATCH', variants: [] };
}
if (!DRY_RUN) {
fs.writeFileSync(fullPath, content, 'utf8');
}
return { status: 'CHANGED', variants };
}
const results = FILES.map((f) => ({ path: f, ...processFile(f) }));
console.log(`${DRY_RUN ? '[DRY RUN] ' : ''}Relicense Apache 2.0 → EUPL-1.2 — ${FILES.length} files in scope\n`);
const changed = results.filter((r) => r.status === 'CHANGED');
const noMatch = results.filter((r) => r.status === 'NO_MATCH');
const notFound = results.filter((r) => r.status === 'NOT_FOUND');
changed.forEach((r) => console.log(` CHANGED [${r.variants.join(',')}] ${r.path}`));
noMatch.forEach((r) => console.log(` NO_MATCH (check patterns) ${r.path}`));
notFound.forEach((r) => console.log(` NOT_FOUND ${r.path}`));
console.log(`\nSummary: ${changed.length} changed / ${noMatch.length} no-match / ${notFound.length} not-found (total ${FILES.length}).`);
if (DRY_RUN) {
console.log('\nDRY-RUN: no files written. Re-run without --dry-run to apply.');
}
process.exit(noMatch.length > 0 || notFound.length > 0 ? 1 : 0);