@rune-kit/rune 2.29.0 → 2.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +49 -2
- package/agents/audit.md +1 -1
- package/agents/cook.md +1 -1
- package/agents/journal.md +1 -1
- package/agents/reviewer.md +23 -1
- package/agents/session-bridge.md +1 -1
- package/agents/skill-forge.md +1 -1
- package/agents/skill-router.md +1 -1
- package/agents/trend-scout.md +1 -1
- package/agents/worktree.md +1 -1
- package/compiler/__tests__/skill-attribution.test.js +109 -0
- package/compiler/__tests__/update.test.js +416 -0
- package/compiler/bin/rune.js +19 -0
- package/compiler/commands/update.js +354 -0
- package/package.json +1 -1
- package/skills/audit/SKILL.md +3 -3
- package/skills/completion-gate/SKILL.md +1 -1
- package/skills/constraint-check/SKILL.md +1 -1
- package/skills/context-engine/SKILL.md +2 -2
- package/skills/cook/SKILL.md +2 -2
- package/skills/cook/references/loop-detection.md +1 -1
- package/skills/cook/references/mid-run-signals.md +1 -1
- package/skills/debug/SKILL.md +2 -2
- package/skills/dependency-doctor/SKILL.md +1 -1
- package/skills/design/SKILL.md +1 -1
- package/skills/docs/SKILL.md +1 -1
- package/skills/docs-seeker/SKILL.md +3 -0
- package/skills/hallucination-guard/SKILL.md +2 -2
- package/skills/incident/SKILL.md +1 -1
- package/skills/integrity-check/SKILL.md +1 -1
- package/skills/launch/SKILL.md +1 -1
- package/skills/preflight/SKILL.md +35 -9
- package/skills/rescue/SKILL.md +1 -1
- package/skills/research/SKILL.md +1 -1
- package/skills/review/SKILL.md +135 -84
- package/skills/review/references/rules/config.md +63 -0
- package/skills/review/references/rules/default.md +57 -0
- package/skills/review/references/rules/go.md +65 -0
- package/skills/review/references/rules/index.md +43 -0
- package/skills/review/references/rules/python.md +64 -0
- package/skills/review/references/rules/rust.md +65 -0
- package/skills/review/references/rules/sql.md +65 -0
- package/skills/review/references/rules/ts-js.md +80 -0
- package/skills/sast/SKILL.md +1 -1
- package/skills/scout/SKILL.md +3 -0
- package/skills/sentinel/SKILL.md +29 -6
- package/skills/skill-router/SKILL.md +1 -1
- package/skills/team/SKILL.md +2 -2
- package/skills/verification/SKILL.md +3 -4
- package/skills/watchdog/SKILL.md +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rune",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.30.0",
|
|
4
4
|
"description": "66-skill engineering mesh with Codex-native skills, subagents, synchronous lifecycle hooks, security gates, MCP-aware workflows, and project memory.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rune Contributors"
|
package/README.md
CHANGED
|
@@ -103,11 +103,19 @@ Plus **9 domain packs** (product, sales, data-science, support, growth, media, p
|
|
|
103
103
|
|
|
104
104
|
---
|
|
105
105
|
|
|
106
|
-
## What's New (v2.
|
|
106
|
+
## What's New (v2.30.0 — Tier Restored)
|
|
107
|
+
|
|
108
|
+
> **v2.30.0 (2026-07-29):** Rune's model tier table had quietly stopped applying on Claude Code, and had been wrong in **23 of 66 places** for longer than that. `agents/*.md` is a hand-written parallel copy of each skill's tier; nothing generated it from `skills/` and no check compared the two, so they drifted — `cook` said opus in one file and sonnet in the other, `verification` said sonnet and haiku. `skills/*/SKILL.md` is now the single source of truth, every pair agrees, and a new **`validateAgentSync`** gate fails CI on any future drift, alongside a model-split check and a warning for a top-level `model` that cannot take effect. Where the two disagreed the tier was decided by role, with one rule overriding both old values: **a gate never runs below sonnet** — `completion-gate`, `constraint-check`, `integrity-check`, `hallucination-guard`, and `verification` exist to catch unverified claims, so running them cheaper than the agent they audit defeats the point. Separately: tiering only ever applied through spawned subagents, and on Opus 5 the harness tells the model not to spawn them unless asked — so every tier assignment had become a silent no-op. `scout` and `docs-seeker` now declare `context: fork` + `model: haiku` and route through the Skill tool instead; verified end-to-end on Opus 5 by per-model billing showing `claude-haiku-4-5`. `cook` deliberately declares no model — an orchestrator inherits the session model, because choosing Opus for a session is a deliberate act. 1,647 tests.
|
|
109
|
+
|
|
110
|
+
### Previous (v2.29.1 — One-Command Update)
|
|
111
|
+
|
|
112
|
+
> **v2.29.1 (2026-07-24):** v2.29.0 documented the update flow; this release automates it. New **`rune update`** command — one-shot updater for an already-configured project: `git pull --ff-only` any detected Pro/Business tier repos (env var → sibling dir, same detection as setup; a failed pull **aborts loudly**, never a silent half-update), re-runs the managed setup rewrite in place non-interactively (your installed platforms, preset, and tiers are detected from the existing hook config — no prompts), then verifies with doctor + hook drift and reminds Codex users to re-trust `/hooks` only when `.codex/hooks.json` actually changed. Flags: `--no-pull`, `--preset`, `--tier`, `--dry`. Plus: "Updating" sections in all three tier READMEs. Docs-and-CLI patch — no skill or mesh changes. 1,641 tests.
|
|
113
|
+
|
|
114
|
+
#### Earlier (v2.29.0 — Codex Native)
|
|
107
115
|
|
|
108
116
|
> **v2.29.0 (2026-07-23):** Codex stops being a compile target that happens to work and becomes a **first-class native runtime**. The Codex adapter now emits everything current Codex supports natively — `.agents/skills/`, project-scoped agent TOML (`.codex/agents/rune-{heavy,standard,fast}.toml`), a dedicated synchronous hook adapter targeting `.codex/hooks.json` (Codex silently skips async handlers), MCP config, and tier-aware compilation so Free/Pro/Business stacks resolve exactly as on Claude Code. The mesh validator was fixed at the root — `Calls (outbound)` is now the single authoritative edge inventory, acknowledged by the target's `Called By` — moving the canonical count from 209 to **248 connections** (same mesh, honest count; doctor now fails CI on any stale claim). Doctor also grew a cross-tier audit: Business metadata (28 pack skills, 4 orchestrators), $149 pricing, 13-platform count, and JSON-schema validation of the new `docs/config-schema.json` + hooks manifest schema. All XLabs remote-MCP references now go through `XLABS_MCP_TOKEN` — plaintext bearer values are forbidden. 1,615 tests pass.
|
|
109
117
|
|
|
110
|
-
|
|
118
|
+
#### Earlier (v2.28.0 — Reasoner's Blind Spots)
|
|
111
119
|
|
|
112
120
|
> **v2.28.0 (2026-07-22):** Completes the reasoning wave. Every addition targets one failure class: **a check that feels done because the model re-read its own work and agreed with itself.** `problem-solver` (v0.6.0) gains a **model failure-mode table** beside its human-bias table — pattern-match satisfaction, template hijack, fluent≠true, prior-as-fact, completion pressure, surface blindness — plus three tells that you are inside one right now (instant confident answer; a stated detail your draft never used; two failed attempts in the same framing). `verification` (v0.8.0) gains the **Constraint Loop** for deliverables carrying a mechanically checkable constraint on their own surface form (banned characters, exact counts, strict formats) — a class Rune had no coverage for: expand the constraint before drafting, verify with a tool, re-scan the whole artifact, ship byte-for-byte. `design` (v0.9.0) gains **render blindness** — a checklist item ticked from source is a prediction, and the imagined render is always flattering; visual items are marked 👁 and are ticked from a render or marked ASSUMED. Advisory throughout, no new skills.
|
|
113
121
|
|
|
@@ -407,6 +415,45 @@ This compiles all 66 skills into your IDE's rules format. Same knowledge, same w
|
|
|
407
415
|
lifecycle hooks; the remaining adapters preserve the workflows and constraints in the best
|
|
408
416
|
format each runtime supports.
|
|
409
417
|
|
|
418
|
+
## Updating
|
|
419
|
+
|
|
420
|
+
New releases are announced on [Telegram](https://t.me/xlabs_updates) and [GitHub Releases](https://github.com/rune-kit/rune/releases); see [`CHANGELOG.md`](CHANGELOG.md) for what changed.
|
|
421
|
+
|
|
422
|
+
> **One-liner alternative**: `npx @rune-kit/rune@latest update` runs the whole flow below for an already-configured project — git-pulls detected Pro/Business repos, re-runs the managed setup rewrite in place (reusing your installed platforms/preset/tiers), and verifies with doctor. The platform-specific commands below remain the canonical path.
|
|
423
|
+
|
|
424
|
+
### Claude Code (plugin)
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
claude plugin update rune
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Restart Claude Code to load the new version, then re-run the wizard once so hooks and tier skills match the new release:
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
npx @rune-kit/rune@latest setup
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Codex / Cursor / Windsurf / other compiled platforms
|
|
437
|
+
|
|
438
|
+
Re-running setup with `@latest` fetches the newest compiler and rewrites everything Rune manages **in place** — it is idempotent, and never touches your own config or the human-authored parts of `AGENTS.md`:
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
# same flags you installed with, e.g.:
|
|
442
|
+
npx @rune-kit/rune@latest setup --here
|
|
443
|
+
npx @rune-kit/rune@latest setup --here --platform codex --tier pro
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
If you use the build pipeline (`rune init` route) instead of the wizard: `npx @rune-kit/rune@latest init --force` regenerates platform files without touching your `.rune/` state.
|
|
447
|
+
|
|
448
|
+
### Verify after updating
|
|
449
|
+
|
|
450
|
+
```bash
|
|
451
|
+
npx @rune-kit/rune@latest doctor # compiled output + mesh integrity
|
|
452
|
+
npx @rune-kit/rune@latest doctor --hooks # hook drift vs the canonical preset
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
On Codex, review and re-trust changed hooks with `/hooks`.
|
|
456
|
+
|
|
410
457
|
## Quick Start
|
|
411
458
|
|
|
412
459
|
```bash
|
package/agents/audit.md
CHANGED
|
@@ -25,7 +25,7 @@ You are the **audit** skill — Rune's comprehensive project auditor.
|
|
|
25
25
|
|
|
26
26
|
**Critical Rules:**
|
|
27
27
|
- MUST complete all 8 phases (explicitly state if skipped)
|
|
28
|
-
-
|
|
28
|
+
- Drop a finding only on direct counter-evidence, never on uncertainty — an unconfirmable finding is reported and typed `ASSUMED` with its unchecked premise named
|
|
29
29
|
- Include at least 3 positive findings
|
|
30
30
|
- Delegate Phase 1 to dependency-doctor, Phase 2 to sentinel
|
|
31
31
|
|
package/agents/cook.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cook
|
|
3
3
|
description: "Feature implementation orchestrator — handles 70% of requests. Full TDD cycle: understand → plan → test → implement → verify → commit. Use for ANY code modification (features, bugs, refactors, security)."
|
|
4
|
-
model:
|
|
4
|
+
model: sonnet
|
|
5
5
|
subagent_type: general-purpose
|
|
6
6
|
---
|
|
7
7
|
|
package/agents/journal.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: journal
|
|
3
3
|
description: "Persistent state tracking across sessions — records decisions with rationale, progress, risks, ADRs to .rune/ files. Called by surgeon, deploy, rescue for cross-session continuity."
|
|
4
|
-
model:
|
|
4
|
+
model: haiku
|
|
5
5
|
subagent_type: general-purpose
|
|
6
6
|
---
|
|
7
7
|
|
package/agents/reviewer.md
CHANGED
|
@@ -9,7 +9,7 @@ You are the **reviewer** subagent — a code review specialist spawned by other
|
|
|
9
9
|
|
|
10
10
|
## Operating Rules
|
|
11
11
|
|
|
12
|
-
1. **Every finding MUST
|
|
12
|
+
1. **Every finding MUST carry a verbatim evidence snippet** — vague observations are rejected, and so are line numbers produced from memory (see Evidence & Anchoring below)
|
|
13
13
|
2. Check all 5 areas: correctness, security, performance, conventions, test coverage
|
|
14
14
|
3. Severity levels: CRITICAL (blocks merge) → HIGH → MEDIUM → LOW
|
|
15
15
|
4. Never rubber-stamp — if zero issues found, look harder (default-suspicious mindset)
|
|
@@ -17,4 +17,26 @@ You are the **reviewer** subagent — a code review specialist spawned by other
|
|
|
17
17
|
6. Include at least 1 positive note (what's well-designed)
|
|
18
18
|
7. Verdict: APPROVE / REQUEST CHANGES / NEEDS DISCUSSION
|
|
19
19
|
|
|
20
|
+
## Falsification Pass (before reporting)
|
|
21
|
+
|
|
22
|
+
**Falsify, not verify.** Do not filter findings by how confident you feel — you cannot calibrate that. Filter by disproof:
|
|
23
|
+
|
|
24
|
+
- **DROP** only when the code you read holds **direct counter-evidence** against the finding's key claim. Discard it silently.
|
|
25
|
+
- **KEEP** when the finding rests on context outside the diff that you actually read — that context is evidence.
|
|
26
|
+
- **KEEP** when you can neither confirm nor disprove it. "Unsure" is not grounds to drop.
|
|
27
|
+
|
|
28
|
+
Type every surviving finding `OBSERVED` (you read the defect), `DERIVED` (follows from what you read, state the mechanism), or `ASSUMED` (rests on an unverified premise — name it). **`ASSUMED` findings are never CRITICAL.**
|
|
29
|
+
|
|
30
|
+
## Evidence & Anchoring
|
|
31
|
+
|
|
32
|
+
Report the snippet, not the line number. A line number recalled from a long context drifts, and a correct finding pointing at the wrong line is unactionable.
|
|
33
|
+
|
|
34
|
+
- **Copy** the offending lines verbatim from the file — no rewriting, no re-indenting, diff markers (`+`/`-`) stripped, max 5 lines. A finding needing more than 5 lines to show is a design comment: report it without evidence at MEDIUM or below.
|
|
35
|
+
- **Anchor** each finding before reporting: `Grep` the exact snippet in its file → hit, use that line. Miss → retry once with whitespace normalised and the first and last lines dropped.
|
|
36
|
+
- **`UNANCHORED`** on a second miss: downgrade one severity level, report as `path (unanchored)` with the snippet shown inline. Never drop it — a failed `Grep` is not counter-evidence.
|
|
37
|
+
|
|
38
|
+
## Strict Focus
|
|
39
|
+
|
|
40
|
+
Read anything you need for context. Report findings **only** about files in the stated review scope — an out-of-scope finding is dropped, or noted as a one-line follow-up in the report footer with no severity.
|
|
41
|
+
|
|
20
42
|
You do NOT fix code. You identify issues with evidence. The parent skill decides next steps.
|
package/agents/session-bridge.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: session-bridge
|
|
3
3
|
description: "Cross-session context persistence — auto-saves decisions, conventions, progress, instincts to .rune/ files. Loads + integrity-checks at session start. Auto-triggered before compaction."
|
|
4
|
-
model:
|
|
4
|
+
model: haiku
|
|
5
5
|
subagent_type: general-purpose
|
|
6
6
|
---
|
|
7
7
|
|
package/agents/skill-forge.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: skill-forge
|
|
3
3
|
description: "The skill that builds skills — TDD-driven: baseline test → write skill → verify → refactor → integrate into mesh. Use for creating or editing Rune skills."
|
|
4
|
-
model:
|
|
4
|
+
model: opus
|
|
5
5
|
subagent_type: general-purpose
|
|
6
6
|
---
|
|
7
7
|
|
package/agents/skill-router.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: skill-router
|
|
3
3
|
description: "Meta-enforcement layer — routes EVERY agent action through the correct skill before any code is written. Always active. Prevents 'just this once' bypasses."
|
|
4
|
-
model:
|
|
4
|
+
model: sonnet
|
|
5
5
|
subagent_type: general-purpose
|
|
6
6
|
---
|
|
7
7
|
|
package/agents/trend-scout.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: trend-scout
|
|
3
3
|
description: "Market intelligence — competitor analysis, technology trends, community sentiment. Scans Product Hunt, GitHub Trending, HN, Reddit. Use for positioning, not API docs."
|
|
4
|
-
model:
|
|
4
|
+
model: haiku
|
|
5
5
|
subagent_type: general-purpose
|
|
6
6
|
---
|
|
7
7
|
|
package/agents/worktree.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: worktree
|
|
3
3
|
description: "Git worktree lifecycle — create isolated workspaces for parallel development, manage branches, cleanup. Max 3 active worktrees. Called by team for parallel streams."
|
|
4
|
-
model:
|
|
4
|
+
model: haiku
|
|
5
5
|
subagent_type: general-purpose
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attribution hygiene — skills and agents must not carry graft-source credits.
|
|
3
|
+
*
|
|
4
|
+
* Rune skills present their guidance as Rune's own. A `> From <repo> (N★): "…"`
|
|
5
|
+
* blockquote leaks where a pattern was grafted from, dates the file with a star
|
|
6
|
+
* count, and reads as borrowed authority. The prose stays; the credit line goes.
|
|
7
|
+
*
|
|
8
|
+
* Guards the patterns removed in a2df7b5 so they cannot drift back in.
|
|
9
|
+
*
|
|
10
|
+
* Legitimate credits are NOT attribution: a license obligation for redistributed
|
|
11
|
+
* data (skills/design), a descriptive "Source patterns:" lead-in, and a filename
|
|
12
|
+
* cited inside a config example all stay. Each is asserted explicitly below so a
|
|
13
|
+
* future tightening of the rules cannot silently outlaw them.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import assert from 'node:assert';
|
|
17
|
+
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
18
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
19
|
+
import { describe, test } from 'node:test';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
|
|
22
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
const REPO_ROOT = resolve(__dirname, '../..');
|
|
24
|
+
const SCAN_DIRS = ['skills', 'agents'];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Each rule matches a single line. Keep them shape-based: a rule that keys on a
|
|
28
|
+
* repo name rots the moment a different repo is grafted.
|
|
29
|
+
*/
|
|
30
|
+
const FORBIDDEN = [
|
|
31
|
+
{ name: 'blockquote "> From <repo>"', re: /^\s*>\s*From\s+\S/ },
|
|
32
|
+
{ name: 'blockquote "> Inspired by"', re: /^\s*>\s*Inspired by\s/ },
|
|
33
|
+
{ name: 'blockquote "> Source:"', re: /^\s*>\s*Source:/ },
|
|
34
|
+
{ name: 'bare "Source: … ★"', re: /^\s*Source:\s.*★/ },
|
|
35
|
+
{ name: 'blockquote carrying a star count', re: /^\s*>\s.*★/ },
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
/** Lines that look adjacent to the rules above and must keep passing. */
|
|
39
|
+
const MUST_PASS = [
|
|
40
|
+
'Design intelligence data from [UI/UX Pro Max](https://github.com/nextlevelbuilder/ui-ux-pro-max-skill) (MIT, 42.8k★).',
|
|
41
|
+
'> Source patterns: production-proven PostgreSQL scaling strategies.',
|
|
42
|
+
'Source: compliance-soc2.yaml',
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
function walkMarkdown(dir) {
|
|
46
|
+
const out = [];
|
|
47
|
+
for (const entry of readdirSync(dir)) {
|
|
48
|
+
const full = join(dir, entry);
|
|
49
|
+
if (statSync(full).isDirectory()) {
|
|
50
|
+
out.push(...walkMarkdown(full));
|
|
51
|
+
} else if (entry.endsWith('.md')) {
|
|
52
|
+
out.push(full);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function findViolations(text, file) {
|
|
59
|
+
const hits = [];
|
|
60
|
+
text.split(/\r?\n/).forEach((line, i) => {
|
|
61
|
+
for (const rule of FORBIDDEN) {
|
|
62
|
+
if (rule.re.test(line)) {
|
|
63
|
+
hits.push(`${file}:${i + 1} — ${rule.name} → ${line.trim()}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
return hits;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
describe('attribution hygiene', () => {
|
|
71
|
+
test('no source-attribution lines in skills/ or agents/', () => {
|
|
72
|
+
const violations = [];
|
|
73
|
+
for (const dirName of SCAN_DIRS) {
|
|
74
|
+
const dir = join(REPO_ROOT, dirName);
|
|
75
|
+
for (const file of walkMarkdown(dir)) {
|
|
76
|
+
violations.push(...findViolations(readFileSync(file, 'utf8'), relative(REPO_ROOT, file)));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
assert.deepStrictEqual(
|
|
81
|
+
violations,
|
|
82
|
+
[],
|
|
83
|
+
`Attribution lines found — delete the credit, keep the prose:\n${violations.join('\n')}`,
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('rules fire on the exact lines removed in a2df7b5', () => {
|
|
88
|
+
const removed = [
|
|
89
|
+
'> From goclaw (nextlevelbuilder/goclaw, 832★): "Compact during run, not just at session boundary."',
|
|
90
|
+
'> From superpowers (obra/superpowers, 84k★): "Each fix revealing new problems elsewhere = structural issue."',
|
|
91
|
+
'> Inspired by CLI-Anything (HKUDS/CLI-Anything, 14.5k★): "Never trust exit 0."',
|
|
92
|
+
'Source: goclaw (832★) — SHA256-based loop detection distinguishes true stuck loops.',
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
for (const line of removed) {
|
|
96
|
+
assert.ok(
|
|
97
|
+
FORBIDDEN.some((rule) => rule.re.test(line)),
|
|
98
|
+
`no rule caught a known attribution line: ${line}`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('legitimate credits and lookalikes are not flagged', () => {
|
|
104
|
+
for (const line of MUST_PASS) {
|
|
105
|
+
const caught = FORBIDDEN.filter((rule) => rule.re.test(line)).map((rule) => rule.name);
|
|
106
|
+
assert.deepStrictEqual(caught, [], `false positive on a legitimate line: ${line}`);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
});
|