BREAKING CHANGE: the marketplace slug, the agent namespace (linkedin-studio:<agent>), and the runtime state-file path (~/.claude/linkedin-studio.local.md) all change. Reinstall required; existing state migrated in place (post metrics, streak, history preserved). The /linkedin:* commands are unchanged — the command namespace is set per-command in frontmatter and was always independent of the plugin slug. Functionality is byte-identical to v2.4.0; this release is pure identity. - dir + manifests: plugins/linkedin-studio + plugin.json + root marketplace.json - agent namespace updated in commands/newsletter.md (only functional invoker) - state path updated in 4 hook scripts + topic-rotation prompt + state template - catch-all skill dir renamed skills/linkedin-studio (5 functional skills unchanged) - docs + version bump to 3.0.0 across README badge, CHANGELOG, root README/CLAUDE.md - historical records (CHANGELOG past entries, docs/ build artifacts, config-audit v5.0.0 snapshots) intentionally retain the old slug Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
69 lines
2.4 KiB
JavaScript
69 lines
2.4 KiB
JavaScript
import { describe, test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync } from 'node:fs';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
// Lint-test for the persona-reviewer fasit fixture.
|
|
// Mirrors the structure-only discipline of state-updater.test.mjs and
|
|
// fact-checker-fixture.test.mjs: this test asserts the SHAPE of the fixture —
|
|
// one reader persona carrying all five library fields, a non-empty sample
|
|
// draft, the six judging axes, and both review modes documented. Whether the
|
|
// agent's live flags actually match the fasit directions is [GATE]/[OPERATØR],
|
|
// never self-certified here.
|
|
|
|
const FIXTURE_PATH = fileURLToPath(
|
|
new URL('../fixtures/persona-reviewer-cases.md', import.meta.url)
|
|
);
|
|
|
|
const fixture = readFileSync(FIXTURE_PATH, 'utf8');
|
|
|
|
// The five persona field keys, lowercase to match config/personas.template.md.
|
|
const PERSONA_FIELDS = ['rolle', 'avkobler', 'overbeviser', 'ekspertise', 'sjargong'];
|
|
|
|
// The six judging axes (plan Step 6 / fasit §6.3).
|
|
const AXES = [
|
|
'Krok', // hook holds?
|
|
'Resonans', // does the point land?
|
|
'Tone', // tone fit for this reader
|
|
'Troverdighet', // credibility
|
|
'Leder-takeaway', // leader takeaway + concrete action
|
|
'Lengde', // length / drive
|
|
];
|
|
|
|
// Both review modes must be documented (resonance + conversion).
|
|
const MODES = ['resonans', 'konverter'];
|
|
|
|
describe('persona-reviewer fixture structure', () => {
|
|
test('documents one persona with all five library fields', () => {
|
|
for (const field of PERSONA_FIELDS) {
|
|
assert.ok(
|
|
new RegExp(`\\*\\*${field}\\*\\*`).test(fixture),
|
|
`fixture must document the persona field **${field}**`
|
|
);
|
|
}
|
|
});
|
|
|
|
test('contains a non-empty sample-text section', () => {
|
|
const m = fixture.match(/##\s+Sample-tekst\b([\s\S]*?)(?=\n##\s|$)/i);
|
|
assert.ok(m, 'fixture must have a "## Sample-tekst" section');
|
|
assert.ok(
|
|
m[1].trim().length > 80,
|
|
'the sample-text section must contain a real draft excerpt, not a stub'
|
|
);
|
|
});
|
|
|
|
test('documents all six judging axes', () => {
|
|
for (const axis of AXES) {
|
|
assert.ok(fixture.includes(axis), `fixture must name the axis "${axis}"`);
|
|
}
|
|
});
|
|
|
|
test('documents both review modes (resonance + conversion)', () => {
|
|
for (const mode of MODES) {
|
|
assert.ok(
|
|
new RegExp(mode, 'i').test(fixture),
|
|
`fixture must document the "${mode}" mode`
|
|
);
|
|
}
|
|
});
|
|
});
|