Drop FreeBSD 14 support (Sam & Claude)

Require the tracked FreeBSD 15.x line during install and environment checks, and align docs and skill compatibility metadata with 15.x only.

---
Build: pass
Tests: pass — 37 passed (2 files)

---
Build: pass | Tests: pass — 2363 passed (701 files)
This commit is contained in:
Operator & Codex 2026-05-10 16:31:40 +02:00
parent 0340674432
commit 771e19e1c7
29 changed files with 167 additions and 46 deletions

View file

@ -1,7 +1,7 @@
---
name: backup-db
description: Create a full PostgreSQL database backup (pg_dump) and store in project-relative backup directory
compatibility: FreeBSD 15.0+
compatibility: FreeBSD 15.x
invoke_patterns:
- "Back up the database"
- "Back up * database"

View file

@ -1,7 +1,7 @@
---
name: db-analyze
description: Run ANALYZE on PostgreSQL to update table statistics used by the query planner
compatibility: FreeBSD 15.0+
compatibility: FreeBSD 15.x
invoke_patterns:
- "Run analyze"
- "Analyze the database"

View file

@ -1,7 +1,7 @@
---
name: db-migrate
description: Apply pending PostgreSQL schema migrations — always backs up first, reports what changed
compatibility: FreeBSD 15.0+
compatibility: FreeBSD 15.x
invoke_patterns:
- "Apply migrations"
- "Run migrations"

View file

@ -1,7 +1,7 @@
---
name: db-sync-check
description: Verify replication lag between local PostgreSQL and Supabase (persistent memory sync)
compatibility: FreeBSD 15.0+
compatibility: FreeBSD 15.x
invoke_patterns:
- "Check replication lag"
- "Is Supabase in sync"

View file

@ -1,7 +1,7 @@
---
name: db-vacuum
description: Run VACUUM (and optionally ANALYZE) on PostgreSQL tables to reclaim dead row space
compatibility: FreeBSD 15.0+
compatibility: FreeBSD 15.x
invoke_patterns:
- "Run vacuum"
- "Vacuum the database"

View file

@ -1,7 +1,7 @@
---
name: disk-usage
description: Check disk space on host and inside jails — ZFS pool usage, dataset quotas, free space
compatibility: FreeBSD 15.0+
compatibility: FreeBSD 15.x
invoke_patterns:
- "How much free disk"
- "Disk space"

View file

@ -1,7 +1,7 @@
---
name: git-branch-protect
description: Check or apply branch protection rules — verify main is protected, create hotfix branches
compatibility: FreeBSD 15.0+
compatibility: FreeBSD 15.x
invoke_patterns:
- 'Protect branch'
- 'Branch protection'

View file

@ -1,7 +1,7 @@
---
name: git-merge
description: Merge a branch into a target branch — detects conflicts and escalates rather than auto-resolving
compatibility: FreeBSD 15.0+
compatibility: FreeBSD 15.x
invoke_patterns:
- 'Merge PR *'
- 'Merge * into *'

View file

@ -1,7 +1,7 @@
---
name: git-pull
description: Pull latest changes from local git jail with rebase strategy — safe, no merge commits
compatibility: FreeBSD 15.0+
compatibility: FreeBSD 15.x
invoke_patterns:
- 'Pull latest'
- 'Pull from origin'

View file

@ -1,7 +1,7 @@
---
name: git-push-mirror
description: Push to upstream Codeberg mirror from local git jail and verify sync
compatibility: FreeBSD 15.0+
compatibility: FreeBSD 15.x
invoke_patterns:
- 'Push to mirror'
- 'Push to Codeberg'

View file

@ -1,7 +1,7 @@
---
name: git-push-upstream
description: Push current branch to Codeberg upstream — explicit one-way sync from local to public mirror
compatibility: FreeBSD 15.0+
compatibility: FreeBSD 15.x
invoke_patterns:
- 'Push to Codeberg'
- 'Push to upstream'

View file

@ -1,7 +1,7 @@
---
name: git-release-tag
description: Create an annotated git tag for a release and push to local + upstream
compatibility: FreeBSD 15.0+
compatibility: FreeBSD 15.x
invoke_patterns:
- 'Tag version *'
- 'Tag release *'

View file

@ -1,7 +1,7 @@
---
name: jail-status
description: Check status of one or all Bastille jails — running, stopped, uptime, CPU, RAM
compatibility: FreeBSD 15.0+
compatibility: FreeBSD 15.x
invoke_patterns:
- "Check if * jail is running"
- "Is * jail up"

View file

@ -1,7 +1,7 @@
---
name: service-restart
description: Start, stop, or restart a service on host or inside a jail via hostd
compatibility: FreeBSD 15.0+
compatibility: FreeBSD 15.x
invoke_patterns:
- "Restart * service"
- "Start * service"

View file

@ -1,7 +1,7 @@
---
name: system-stats
description: Check host CPU, RAM, load average, and top processes — quick health snapshot
compatibility: FreeBSD 15.0+
compatibility: FreeBSD 15.x
invoke_patterns:
- "System health"
- "CPU usage"

View file

@ -1,7 +1,7 @@
---
name: zfs-scrub
description: Check and run ZFS pool scrubs to verify data integrity. Detect silent corruption, bit rot, and checksum errors on FreeBSD.
compatibility: FreeBSD 15.0+
compatibility: FreeBSD 15.x
invoke_patterns:
- "Scrub"
- "ZFS scrub"

View file

@ -1,7 +1,7 @@
---
name: zfs-snapshot
description: Create a ZFS snapshot of a dataset for point-in-time recovery
compatibility: FreeBSD 15.0+
compatibility: FreeBSD 15.x
invoke_patterns:
- "Create ZFS snapshot"
- "Take a snapshot"

View file

@ -96,13 +96,13 @@ Major architectural addition: Paperclip as multi-agent orchestration layer for C
- `setup/paperclip.ts` — Jail provisioning for Paperclip at 10.0.0.2, company auto-provisioning, skills mounting
- Identity files: `SYSADMIN_AGENT.md`, `DB_ADMIN_AGENT.md`, `GIT_ADMIN_AGENT.md` — Agent role instructions with skill-aware decision logic (alongside existing `SOUL.md`)
- Operational skills: `jail-status`, `backup-db`, `disk-usage`, `service-restart`, `system-stats`, `db-migrate`, `db-vacuum`, `db-analyze`, `git-pull`, `git-merge`, `git-release-tag`, `git-branch-protect`, `git-push-mirror`
- All 33 skills in `.agent/skills/` updated with `compatibility: FreeBSD 15.0+` marker in SKILL.md frontmatter
- All 33 skills in `.agent/skills/` updated with `compatibility: FreeBSD 15.x` marker in SKILL.md frontmatter
- PostgreSQL integration — Paperclip connects to existing PostgreSQL jail at 10.0.0.3, shares database instance with Clawdie
- Skills mounting — `.agent/skills/` mounted read-only into Paperclip jail via nullfs for agent discovery
### Changed
- `.agent/skills/*/SKILL.md` — All skill files now include platform compatibility marker (e.g., `compatibility: FreeBSD 15.0+`)
- `.agent/skills/*/SKILL.md` — All skill files now include platform compatibility marker (e.g., `compatibility: FreeBSD 15.x`)
### Notes

View file

@ -149,7 +149,7 @@ just setup-cms # npm run setup -- --step cms
### Prerequisites
- FreeBSD 15.0-RELEASE
- FreeBSD 15.x
- ZFS root installation
- Root access or sudo
- Internet access for `pkg` and `npm`
@ -665,7 +665,7 @@ Users then run `/add-slack` on their fork and get clean code that does exactly w
## Requirements
- FreeBSD 15.0-RELEASE
- FreeBSD 15.x
- Node.js 24+
- Python 3.11+ (for voice transcription, screenshots, and other features)
- ZFS (recommended)

View file

@ -2,7 +2,7 @@
**Date:** 10.maj.2026 (revised from 01.maj.2026 feasibility analysis)
**Status:** Active adoption plan
**Target:** FreeBSD 15.0+ (mac_do full group control is 15.0+ only)
**Target:** FreeBSD 15.x (mac_do full group control is available on the tracked 15.x line)
---
@ -91,7 +91,7 @@ Move every runtime sudo call to a hostd op:
### 3.2 mac_do Addition (Per-Jail Privilege Isolation)
mac_do is a FreeBSD Mandatory Access Control (MAC) policy module (base system, FreeBSD 15.0+) that allows unprivileged users to change process credentials according to kernel-enforced rules. The companion userland tool is `mdo(1)`.
mac_do is a FreeBSD Mandatory Access Control (MAC) policy module (base system, FreeBSD 15.x) that allows unprivileged users to change process credentials according to kernel-enforced rules. The companion userland tool is `mdo(1)`.
#### What mac_do adds:

View file

@ -11,6 +11,8 @@ description: Single-command install flow for Clawdie.
## Quick start
Clawdie tracks the FreeBSD 15.x line. The installer rejects FreeBSD 14.x and any unvalidated future major version.
```bash
just install
```

View file

@ -10,7 +10,7 @@ understanding what the installer expects.
## Host
- **OS:** FreeBSD 15.0+ (jails are the only supported runtime).
- **OS:** FreeBSD 15.x (jails are the only supported runtime).
- **Privileges:** root or a user with `sudo` and ZFS access.
- **ZFS:** recommended. Snapshots are taken at install milestones
if `zroot/bastille` exists; skipped silently otherwise.

View file

@ -9,7 +9,7 @@ Uporabite ta seznam za preverjanje, ali je vaša nova namestitev Clawdie praviln
## Pred namestitvijo
- [ ] Ima FreeBSD 15.0+ nameščen
- [ ] Ima FreeBSD 15.x nameščen
- [ ] Ima Node.js >=20 nameščen
- [ ] Ima dostop do PostgreSQL baze (lokalno ali oddaljeno)
- [ ] Ima Telegram Bot Token (od @BotFather)

View file

@ -95,7 +95,7 @@
<span class="info-label">Tested on</span>
<p>
<strong>FreeBSD 15.0-RELEASE-p4</strong> on DigitalOcean VPS. Requires
FreeBSD 15.0+.
FreeBSD 15.x.
</p>
</div>

View file

@ -9,7 +9,13 @@ import pg from 'pg';
import { OPS_DB_URL, PI_TUI_BIN, STORE_DIR, TENANT_ID } from '../src/config.js';
import { logger } from '../src/logger.js';
import { commandExists, getPlatform, isRoot } from './platform.js';
import {
commandExists,
getFreeBSDVersionSupport,
getPlatform,
isRoot,
SUPPORTED_FREEBSD_LINE,
} from './platform.js';
import { loadAllPackageLists, loadPackageList } from './packages.js';
import { emitStatus } from './status.js';
@ -126,6 +132,33 @@ export async function run(_args: string[]): Promise<void> {
jailed = false;
}
const freebsdSupport =
platform === 'freebsd'
? getFreeBSDVersionSupport()
: {
ok: false,
detected: 'not_freebsd',
major: null,
required: SUPPORTED_FREEBSD_LINE,
};
if (platform === 'freebsd' && !freebsdSupport.ok) {
emitStatus('CHECK_ENVIRONMENT', {
PLATFORM: platform,
IS_JAILED: jailed,
FREEBSD_VERSION: freebsdSupport.detected,
FREEBSD_MAJOR: freebsdSupport.major ?? 'unknown',
FREEBSD_REQUIRED: freebsdSupport.required,
STATUS: 'failed',
ERROR: 'unsupported_freebsd_version',
LOG: 'logs/setup.log',
});
console.error(
`Unsupported FreeBSD version: ${freebsdSupport.detected}. Required: ${freebsdSupport.required}.`,
);
process.exit(1);
}
const hostPrerequisites = loadPackageList('host-baseline.txt').map((pkg) => {
const check = HOST_PREREQUISITE_CHECKS[pkg];
if (!check) {
@ -305,6 +338,8 @@ export async function run(_args: string[]): Promise<void> {
hasEnv,
hasAuth,
hasRegisteredGroups,
freebsdVersion: freebsdSupport.detected,
freebsdRequired: freebsdSupport.required,
},
'Environment check complete',
);
@ -322,6 +357,9 @@ export async function run(_args: string[]): Promise<void> {
PLATFORM: platform,
IS_JAILED: jailed,
TENANT_ID,
FREEBSD_VERSION: freebsdSupport.detected,
FREEBSD_MAJOR: freebsdSupport.major ?? 'unknown',
FREEBSD_REQUIRED: freebsdSupport.required,
JEXEC: jexec,
JLS: jls,
SERVICE: service,

View file

@ -19,7 +19,12 @@ import { fileURLToPath } from 'url';
import { logger } from '../src/logger.js';
import { extractEnvValue } from './profile.js';
import { commandExists, getPlatform, isRoot } from './platform.js';
import {
commandExists,
getFreeBSDVersionSupport,
getPlatform,
isRoot,
} from './platform.js';
import { auditEnvFile } from './env-audit.js';
import { SERVICE_NAME } from '../src/platform-identity.js';
import { SOCKET_PATH as HOSTD_SOCKET_PATH } from '../src/hostd/types.js';
@ -541,6 +546,14 @@ export async function run(argv: string[]): Promise<void> {
process.exit(1);
}
const freebsdSupport = getFreeBSDVersionSupport();
if (!freebsdSupport.ok) {
console.error(
`Unsupported FreeBSD version: ${freebsdSupport.detected}. Required: ${freebsdSupport.required}.`,
);
process.exit(1);
}
// Detect ZFS dataset once up front
const bastilleDataset = opts.noSnapshots ? null : detectBastilleDataset();
if (bastilleDataset) {

View file

@ -8,6 +8,8 @@ import {
commandExists,
getNodeVersion,
getNodeMajorVersion,
parseFreeBSDMajorVersion,
checkFreeBSDVersionSupport,
} from './platform.js';
// --- getPlatform ---
@ -27,6 +29,27 @@ describe('isRoot', () => {
});
});
describe('FreeBSD version support', () => {
it('parses FreeBSD major versions', () => {
expect(parseFreeBSDMajorVersion('15.0-RELEASE')).toBe(15);
expect(parseFreeBSDMajorVersion('15.0-RELEASE-p8')).toBe(15);
expect(parseFreeBSDMajorVersion('15.1-RELEASE')).toBe(15);
expect(parseFreeBSDMajorVersion('14.3-RELEASE')).toBe(14);
expect(parseFreeBSDMajorVersion('not a version')).toBeNull();
});
it('accepts only the tracked FreeBSD 15 line', () => {
expect(checkFreeBSDVersionSupport('15.0-RELEASE').ok).toBe(true);
expect(checkFreeBSDVersionSupport('15.0-RELEASE-p8').ok).toBe(true);
expect(checkFreeBSDVersionSupport('15.1-RELEASE').ok).toBe(true);
expect(checkFreeBSDVersionSupport('14.3-RELEASE').ok).toBe(false);
expect(checkFreeBSDVersionSupport('16.0-RELEASE').ok).toBe(false);
expect(checkFreeBSDVersionSupport('not a version').ok).toBe(false);
});
});
// --- getServiceManager ---
describe('getServiceManager', () => {
it('returns a valid service manager', () => {
const result = getServiceManager();

View file

@ -8,6 +8,16 @@ import path from 'path';
export type Platform = 'freebsd' | 'unknown';
export type ServiceManager = 'freebsd-rc' | 'none';
export const SUPPORTED_FREEBSD_MAJOR = 15;
export const SUPPORTED_FREEBSD_LINE = 'FreeBSD 15.x';
export interface FreeBSDVersionSupport {
ok: boolean;
detected: string;
major: number | null;
required: string;
}
export function getPlatform(): Platform {
return os.platform() === 'freebsd' ? 'freebsd' : 'unknown';
}
@ -34,6 +44,41 @@ export function getServiceManager(): ServiceManager {
return getPlatform() === 'freebsd' ? 'freebsd-rc' : 'none';
}
export function parseFreeBSDMajorVersion(raw: string): number | null {
const match = raw.trim().match(/^(\d+)(?:[.-]|$)/u);
if (!match) return null;
const major = parseInt(match[1], 10);
return Number.isNaN(major) ? null : major;
}
export function checkFreeBSDVersionSupport(raw: string): FreeBSDVersionSupport {
const detected = raw.trim();
const major = parseFreeBSDMajorVersion(detected);
return {
ok: major === SUPPORTED_FREEBSD_MAJOR,
detected: detected || 'unknown',
major,
required: SUPPORTED_FREEBSD_LINE,
};
}
export function readFreeBSDUserlandVersion(): string {
return execSync('freebsd-version -u', { encoding: 'utf-8' }).trim();
}
export function getFreeBSDVersionSupport(): FreeBSDVersionSupport {
try {
return checkFreeBSDVersionSupport(readFreeBSDUserlandVersion());
} catch {
return {
ok: false,
detected: 'unknown',
major: null,
required: SUPPORTED_FREEBSD_LINE,
};
}
}
export function getNodePath(): string {
try {
return execSync('command -v node', { encoding: 'utf-8' }).trim();

View file

@ -16,7 +16,7 @@ function makeSkills(names: string[]): Skill[] {
return names.map((name) => ({
name,
description: `${name} skill`,
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: [],
}));
}
@ -49,7 +49,7 @@ describe('Skills Discovery', () => {
{
name: 'jail-status',
description: '',
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: [
'Check if * jail is running',
'Is * up',
@ -59,31 +59,31 @@ describe('Skills Discovery', () => {
{
name: 'disk-usage',
description: '',
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: ['How much free disk*', 'Disk space*'],
},
{
name: 'service-restart',
description: '',
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: ['Restart * service', 'Start * service'],
},
{
name: 'system-stats',
description: '',
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: ['CPU and memory*', 'System load*'],
},
{
name: 'zfs-snapshot',
description: '',
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: ['Take a ZFS snapshot*', 'ZFS snapshot*'],
},
{
name: 'backup-db',
description: '',
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: ['Back up the database*', 'Backup database*'],
},
];
@ -146,25 +146,25 @@ describe('Skills Discovery', () => {
{
name: 'db-vacuum',
description: '',
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: ['Run vacuum*', 'Vacuum database*'],
},
{
name: 'db-analyze',
description: '',
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: ['Analyze the database*', 'Analyze*'],
},
{
name: 'db-migrate',
description: '',
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: ['Apply pending migrations*', 'Run migrations*'],
},
{
name: 'db-sync-check',
description: '',
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: ['Check replication*', 'Sync check*'],
},
];
@ -200,25 +200,25 @@ describe('Skills Discovery', () => {
{
name: 'git-merge',
description: '',
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: ['Merge PR*', 'Merge * into main'],
},
{
name: 'git-release-tag',
description: '',
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: ['Tag version*', 'Create tag*'],
},
{
name: 'git-push-mirror',
description: '',
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: ['Push to*mirror*', 'Push to Codeberg*'],
},
{
name: 'git-pull',
description: '',
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: ['Pull latest*', 'Git pull*'],
},
];
@ -253,7 +253,7 @@ describe('Skills Discovery', () => {
{
name: 'jail-status',
description: '',
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: ['Check jail*'],
},
];
@ -291,13 +291,13 @@ describe('Skills Discovery', () => {
{
name: 'jail-status',
description: '',
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: ['Check * status', 'Jail status'],
},
{
name: 'service-restart',
description: '',
compatibility: 'FreeBSD 15.0+',
compatibility: 'FreeBSD 15.x',
invoke_patterns: ['Restart *'],
},
];