@event4u/agent-config 14.19.0 → 14.21.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +351 -156
- package/README.md +1 -1
- package/agents/templates/.ai-council.yml.example +10 -0
- package/dist/agent-src/commands/agent-handoff.md +42 -41
- package/dist/agent-src/commands/analyze/inbox.md +1 -1
- package/dist/agent-src/commands/analyze/{reference-repo.md → repo.md} +247 -32
- package/dist/agent-src/commands/analyze/roadmap-repos.md +129 -0
- package/dist/agent-src/commands/analyze.md +12 -7
- package/dist/agent-src/commands/feature/plan.md +55 -16
- package/dist/agent-src/commands/feature/refactor.md +8 -3
- package/dist/agent-src/commands/humanize.md +48 -4
- package/dist/agent-src/contexts/execution/contract-decision-sheet.md +22 -1
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +11 -2
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +35 -2
- package/dist/agent-src/contexts/execution/terminal-states.md +4 -1
- package/dist/agent-src/rules/evaluator-independence.md +4 -4
- package/dist/agent-src/rules/external-reference-deep-dive.md +2 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +0 -2
- package/dist/agent-src/skills/competitive-positioning/SKILL.md +2 -2
- package/dist/agent-src/skills/evaluate-llm-feature/SKILL.md +1 -1
- package/dist/agent-src/skills/humanizer/references/anti-aiisms.md +27 -11
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +1 -1
- package/dist/agent-src/skills/markitdown/SKILL.md +2 -2
- package/dist/agent-src/skills/repomix-packer/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +28 -6
- package/dist/agent-src/skills/technical-specification/SKILL.md +4 -0
- package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -3
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/features.md +5 -0
- package/dist/agent-src/templates/roadmaps.md +29 -1
- package/dist/agent-src/templates/scripts/telemetry/mcp_lite.ts +213 -0
- package/dist/agent-src/templates/scripts/telemetry/report_renderer.ts +16 -0
- package/dist/agent-src/templates/scripts/telemetry_report.ts +22 -2
- package/dist/cli/mcp/dispatch.js +10 -0
- package/dist/cli/mcp/stdio.js +33 -2
- package/dist/cli/mcp/telemetry.js +227 -0
- package/dist/cli-delegate/{chunk-KPBWFOPZ.js → chunk-2FTMENJL.js} +194 -37
- package/dist/cli-delegate/{chunk-PMEKGDED.js → chunk-2TAQ2K4V.js} +166 -1
- package/dist/cli-delegate/{chunk-GZTMRYCN.js → chunk-643Z7FIC.js} +79 -12
- package/dist/cli-delegate/{chunk-CYH5SP6M.js → chunk-I6RHFQEG.js} +151 -16
- package/dist/cli-delegate/{chunk-IDHUQIXP.js → chunk-J3RFNKG4.js} +41 -5
- package/dist/cli-delegate/{chunk-7PRE6ZV2.js → chunk-YS6XTLEK.js} +199 -4
- package/dist/cli-delegate/chunk-ZVBOHSVF.js +1109 -0
- package/dist/cli-delegate/cmd_conformance.js +88 -110
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_export.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +42 -12
- package/dist/cli-delegate/cmd_memory_get.js +1 -1
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_route_audit.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +59 -902
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- package/dist/cli-delegate/cmd_sync.js +2 -2
- package/dist/cli-delegate/cmd_uninstall.js +2 -2
- package/dist/cli-delegate/cmd_validate.js +2 -2
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +62 -31
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +7 -6
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +6 -5
- package/dist/hooks/dispatch.js +3801 -2803
- package/dist/install/install.mjs +396 -168
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +809 -180
- package/dist/scripts/ai_council/content_ceiling.js +63 -0
- package/dist/scripts/ai_council/seat_policy.js +106 -0
- package/dist/scripts/ai_council/transport_resolver.js +21 -0
- package/dist/scripts/mcp_server/telemetry.js +148 -0
- package/docs/guidelines/agent-infra/naming.md +1 -1
- package/docs/guidelines/agent-infra/security-lint-containment.md +46 -6
- package/docs/guidelines/agent-infra/self-improvement-pipeline.md +4 -4
- package/docs/guidelines/agent-infra/untrusted-input-spotlighting.md +1 -1
- package/docs/guidelines/redundancy-taxonomy.md +28 -0
- package/llms.txt +14 -14
- package/package.json +1 -1
- package/src/agent-src/templates/scripts/telemetry/mcp_lite.ts +213 -0
- package/src/agent-src/templates/scripts/telemetry/report_renderer.ts +16 -0
- package/src/agent-src/templates/scripts/telemetry_report.ts +22 -2
- package/src/config/assurance-capability-registry.json +9 -0
- package/src/config/gate-coverage.yml +55 -0
- package/src/config/gate-violation-baselines.json +18 -6
- package/src/config/release-gate-locality.yml +4 -3
- package/src/scripts/_cli/cmd_session_recycle.ts +51 -3
- package/src/scripts/_lib/audit_field_provenance.ts +44 -3
- package/src/scripts/_lib/blocked_by_marker.ts +189 -0
- package/src/scripts/_lib/host_capability.ts +76 -1
- package/src/scripts/_lib/install_reach_checks.ts +33 -1
- package/src/scripts/_lib/mcp_bridge.ts +222 -10
- package/src/scripts/_lib/mcp_consent_residual.ts +173 -0
- package/src/scripts/_lib/orchestration_record.ts +23 -1
- package/src/scripts/_lib/recycle_envelope_paths.ts +190 -0
- package/src/scripts/_lib/release_highlights.ts +19 -211
- package/src/scripts/_lib/release_material.ts +27 -77
- package/src/scripts/_lib/release_position.ts +96 -0
- package/src/scripts/_lib/review_skipped_record.ts +17 -1
- package/src/scripts/_lib/security_lint.ts +179 -7
- package/src/scripts/_lib/self_repair.ts +141 -6
- package/src/scripts/_lib/self_repair_store.ts +273 -7
- package/src/scripts/_lib/structured_ask.ts +117 -0
- package/src/scripts/_lib/subagent_capsule.ts +155 -3
- package/src/scripts/_lib/tty_prompt.ts +76 -0
- package/src/scripts/ai_council/api_transport.ts +163 -0
- package/src/scripts/ai_council/clients.ts +58 -63
- package/src/scripts/ai_council/config.ts +13 -0
- package/src/scripts/ai_council/content_ceiling.ts +79 -0
- package/src/scripts/ai_council/jury_aggregate.ts +178 -0
- package/src/scripts/ai_council/seat_policy.ts +137 -0
- package/src/scripts/ai_council/status_surface.ts +79 -0
- package/src/scripts/ai_council/transport_resolver.ts +59 -1
- package/src/scripts/ai_tells_rules.ts +446 -5
- package/src/scripts/ask_block_census.ts +499 -0
- package/src/scripts/bench_humanizer_eval.ts +264 -40
- package/src/scripts/bench_prose_tells_fp.ts +198 -0
- package/src/scripts/build_rule_trigger_matrix.ts +5 -0
- package/src/scripts/check_branch_work_committed.ts +234 -0
- package/src/scripts/check_mcp_doc_drift.ts +217 -0
- package/src/scripts/check_pack_size.ts +321 -3
- package/src/scripts/check_release_highlights.ts +0 -157
- package/src/scripts/council_cli.ts +18 -27
- package/src/scripts/detect_ai_tells.ts +142 -20
- package/src/scripts/handoff_context_hook.ts +54 -7
- package/src/scripts/harvest_reference_tokens.ts +698 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +31 -1
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +45 -1
- package/src/scripts/hooks/one_question_per_ask_hook.ts +191 -0
- package/src/scripts/hooks/session_eol_hook.ts +18 -7
- package/src/scripts/install-hooks.sh +52 -0
- package/src/scripts/install.ts +4 -4
- package/src/scripts/lint_agent_security.ts +316 -25
- package/src/scripts/lint_confusables.ts +42 -6
- package/src/scripts/lint_handoffs.ts +0 -132
- package/src/scripts/lint_hidden_unicode.ts +49 -7
- package/src/scripts/lint_instruction_smuggling.ts +42 -6
- package/src/scripts/lint_mcp_config_security.ts +42 -6
- package/src/scripts/lint_roadmap_blockers.ts +51 -1
- package/src/scripts/lint_roadmap_later_disposition.ts +217 -27
- package/src/scripts/lint_skill_frontmatter_safety.ts +43 -7
- package/src/scripts/mcp_server/resources.ts +41 -0
- package/src/scripts/mcp_server/telemetry.ts +19 -4
- package/src/scripts/migrate_command_suggestions.ts +1 -1
- package/src/scripts/orchestration_record.ts +12 -1
- package/src/scripts/probe_unblocked_ask.ts +135 -2
- package/src/scripts/release.ts +50 -95
- package/src/scripts/release_drill.ts +6 -10
- package/src/scripts/release_env.ts +1 -0
- package/src/scripts/release_publication.ts +40 -31
- package/src/scripts/self_repair_cli.ts +31 -1
- package/src/scripts/self_review_gate.ts +409 -15
- package/src/scripts/skill_scout.ts +69 -0
- package/dist/cli-delegate/chunk-2GNWGMAL.js +0 -172
- package/dist/cli-delegate/chunk-LPPWO5CE.js +0 -161
|
@@ -34,6 +34,35 @@ export type TellGroup =
|
|
|
34
34
|
/** "any" = language-agnostic (typography / structure tells). */
|
|
35
35
|
export type TellLanguage = "en" | "de" | "any";
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* A rule whose decision is not expressible as "this regex matched". Receives
|
|
39
|
+
* the already-exempt-stripped scan text plus the resolved language, and returns
|
|
40
|
+
* the same `{count, samples}` shape `countMatches` produces, so the caller does
|
|
41
|
+
* not branch on rule kind beyond choosing the source.
|
|
42
|
+
*/
|
|
43
|
+
export interface RawOccurrence {
|
|
44
|
+
/** Character offset into the scanned text. */
|
|
45
|
+
index: number;
|
|
46
|
+
text: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface MatchResult {
|
|
50
|
+
count: number;
|
|
51
|
+
samples: string[];
|
|
52
|
+
occurrences: RawOccurrence[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type TellMatcher = (text: string, language: "en" | "de") => MatchResult;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A pattern is USED CONSISTENTLY, not repeated by accident, when it occurs at
|
|
59
|
+
* least this many times and spans at least `MIN_CONSISTENT_SPREAD` of the
|
|
60
|
+
* document. Two occurrences in adjacent sentences is a repetition; four spread
|
|
61
|
+
* from the opening to the close is a habit, and a habit is evidence of style.
|
|
62
|
+
*/
|
|
63
|
+
export const MIN_CONSISTENT_OCCURRENCES = 3;
|
|
64
|
+
export const MIN_CONSISTENT_SPREAD = 0.6;
|
|
65
|
+
|
|
37
66
|
export interface TellRule {
|
|
38
67
|
id: string;
|
|
39
68
|
group: TellGroup;
|
|
@@ -43,6 +72,8 @@ export interface TellRule {
|
|
|
43
72
|
weight: number;
|
|
44
73
|
description: string;
|
|
45
74
|
patterns: RegExp[];
|
|
75
|
+
/** When present, REPLACES `patterns` — a rule too context-dependent for one. */
|
|
76
|
+
match?: TellMatcher;
|
|
46
77
|
}
|
|
47
78
|
|
|
48
79
|
/** Em/en-dash density cap per 500 words (CP1 parity). */
|
|
@@ -60,7 +91,201 @@ const w = (
|
|
|
60
91
|
weight: number,
|
|
61
92
|
description: string,
|
|
62
93
|
patterns: RegExp[],
|
|
63
|
-
|
|
94
|
+
match?: TellMatcher,
|
|
95
|
+
): TellRule => ({
|
|
96
|
+
id, group, severity, language, weight, description, patterns,
|
|
97
|
+
...(match ? { match } : {}),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Minimum word count below which a per-500-words density is not computed.
|
|
102
|
+
*
|
|
103
|
+
* `cluster_score_per_500` and `dash_density_per_500` are ratios extrapolated to
|
|
104
|
+
* 500 words. Under a small denominator the extrapolation is the artifact: six
|
|
105
|
+
* words carrying one em dash score 83.33 per 500 and fail a cap of 2, which
|
|
106
|
+
* says nothing about the prose. Below this floor both densities are reported as
|
|
107
|
+
* `null` and neither threshold is applied; hard rules are unaffected, because a
|
|
108
|
+
* hard hit is a count and not a rate.
|
|
109
|
+
*
|
|
110
|
+
* 50 is the largest floor compatible with the bound the repair was specified
|
|
111
|
+
* against — a 60-word text carrying three dashes must still be evaluated and
|
|
112
|
+
* must still fail. It does NOT make the metric meaningful everywhere above it:
|
|
113
|
+
* at a cap of 2 per 500 words no document under ~250 words can carry a single
|
|
114
|
+
* dash and pass. That residual is measured and published in
|
|
115
|
+
* `internal/bench/reports/prose-tells-fp-v1.md` rather than repaired here — the
|
|
116
|
+
* cap itself is a council decision of 2026-07-11 and is not this floor's to move.
|
|
117
|
+
*/
|
|
118
|
+
export const MIN_DENSITY_WORDS = 50;
|
|
119
|
+
|
|
120
|
+
/** Morphology that marks a common noun as abstract, per language. */
|
|
121
|
+
const ABSTRACT_SUFFIX_EN =
|
|
122
|
+
/(?:tions?|sions?|ities|ity|ness(?:es)?|ments?|ances?|ences?|isms?|ships?|hoods?|ancy|ency|ics|ures?|ologies|ology|th|ings?|acy)$/;
|
|
123
|
+
const ABSTRACT_SUFFIX_DE =
|
|
124
|
+
/(?:ungen|ung|heiten|heit|keiten|keit|schaften|schaft|tionen|tion|itäten|ität|ismus|tum|nis|barkeit)$/;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Common abstract nouns this repo authored by hand because they carry no
|
|
128
|
+
* abstract morphology. Deliberately short: every entry is a word whose
|
|
129
|
+
* abstractness a suffix test cannot see, never a convenience list of tells.
|
|
130
|
+
* Authored here — no word list is imported from any external source.
|
|
131
|
+
*/
|
|
132
|
+
const ABSTRACT_STEMS_EN = new Set([
|
|
133
|
+
"speed", "trust", "scale", "impact", "focus", "value", "risk", "craft",
|
|
134
|
+
"reach", "vision", "care", "rigor", "rigour", "worth", "pace", "flair",
|
|
135
|
+
]);
|
|
136
|
+
|
|
137
|
+
/** A member is abstract when it is a common noun with abstract morphology. */
|
|
138
|
+
export function isAbstractNoun(word: string, language: "en" | "de"): boolean {
|
|
139
|
+
if (/\d/.test(word)) return false;
|
|
140
|
+
const lower = word.toLowerCase();
|
|
141
|
+
if (language === "de") {
|
|
142
|
+
// German commons are capitalised, so capitalisation cannot separate a name
|
|
143
|
+
// from a noun; the morphology has to carry the whole decision.
|
|
144
|
+
return ABSTRACT_SUFFIX_DE.test(lower);
|
|
145
|
+
}
|
|
146
|
+
// A capitalised member is a name (or sentence-initial), never counted.
|
|
147
|
+
if (/^[A-Z]/.test(word)) return false;
|
|
148
|
+
return ABSTRACT_SUFFIX_EN.test(lower) || ABSTRACT_STEMS_EN.has(lower);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** A declarative short enough to read as a staccato beat. */
|
|
152
|
+
const STACCATO_MAX_WORDS = 5;
|
|
153
|
+
/** A run of this many consecutive staccato beats is the tell. */
|
|
154
|
+
const STACCATO_RUN = 4;
|
|
155
|
+
/** A run of this many identically-shaped bold-header bullets is the tell. */
|
|
156
|
+
const UNIFORM_BULLET_RUN = 4;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* `tell-staccato-run` — the `anti-aiisms.md` bound "≤ 3 short declaratives in a
|
|
160
|
+
* row", implemented rather than merely claimed (step 1.4). Four or more
|
|
161
|
+
* consecutive declaratives of at most five words each is the manufactured
|
|
162
|
+
* punchline rhythm; three is ordinary emphasis and scores nothing.
|
|
163
|
+
*
|
|
164
|
+
* Questions and exclamations break a run: a short question is a rhetorical
|
|
165
|
+
* device the catalog does not treat as a beat.
|
|
166
|
+
*/
|
|
167
|
+
export function matchStaccatoRun(text: string): MatchResult {
|
|
168
|
+
const occurrences: RawOccurrence[] = [];
|
|
169
|
+
let offset = 0;
|
|
170
|
+
for (const para of text.split(/\n\s*\n/)) {
|
|
171
|
+
const flat = para.replace(/\n/g, " ");
|
|
172
|
+
let run: string[] = [];
|
|
173
|
+
let runStart = 0;
|
|
174
|
+
let cursor = 0;
|
|
175
|
+
const flush = (): void => {
|
|
176
|
+
if (run.length >= STACCATO_RUN) {
|
|
177
|
+
occurrences.push({ index: offset + runStart, text: run.join(" ") });
|
|
178
|
+
}
|
|
179
|
+
run = [];
|
|
180
|
+
};
|
|
181
|
+
for (const raw of flat.split(/(?<=[.!?])\s+/)) {
|
|
182
|
+
const at = flat.indexOf(raw, cursor);
|
|
183
|
+
cursor = at + raw.length;
|
|
184
|
+
const sentence = raw.trim();
|
|
185
|
+
if (sentence === "") continue;
|
|
186
|
+
const words = sentence.split(/\s+/).filter(Boolean).length;
|
|
187
|
+
const isBeat = /\.$/.test(sentence) && words > 0 && words <= STACCATO_MAX_WORDS;
|
|
188
|
+
if (isBeat) {
|
|
189
|
+
if (run.length === 0) runStart = at;
|
|
190
|
+
run.push(sentence);
|
|
191
|
+
} else {
|
|
192
|
+
flush();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
flush();
|
|
196
|
+
offset += para.length + 2;
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
count: occurrences.length,
|
|
200
|
+
samples: occurrences.slice(0, 3).map((o) => o.text.slice(0, 60).trim()),
|
|
201
|
+
occurrences,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* `tell-uniform-bullet-run` — the `anti-aiisms.md` bound "merge a run of ≥ 4
|
|
207
|
+
* identically-shaped `- **X:** …` bullets", implemented rather than claimed
|
|
208
|
+
* (step 1.4). Distinct from `tell-bold-header-list`, which fires on a SINGLE
|
|
209
|
+
* such bullet: the bound is about a run, and a three-item definition list is
|
|
210
|
+
* ordinary reference prose.
|
|
211
|
+
*/
|
|
212
|
+
export function matchUniformBulletRun(text: string): MatchResult {
|
|
213
|
+
const occurrences: RawOccurrence[] = [];
|
|
214
|
+
let run = 0;
|
|
215
|
+
let first = "";
|
|
216
|
+
let firstIndex = 0;
|
|
217
|
+
let offset = 0;
|
|
218
|
+
const flush = (): void => {
|
|
219
|
+
if (run >= UNIFORM_BULLET_RUN) occurrences.push({ index: firstIndex, text: first });
|
|
220
|
+
run = 0;
|
|
221
|
+
first = "";
|
|
222
|
+
};
|
|
223
|
+
for (const line of text.split("\n")) {
|
|
224
|
+
if (/^\s*[-*]\s+\*\*[^*\n]{2,60}:?\*\*:?/.test(line)) {
|
|
225
|
+
if (run === 0) {
|
|
226
|
+
first = line.trim();
|
|
227
|
+
firstIndex = offset;
|
|
228
|
+
}
|
|
229
|
+
run += 1;
|
|
230
|
+
} else if (line.trim() !== "") {
|
|
231
|
+
// A blank line inside a list does not end the run — the shape survives it.
|
|
232
|
+
flush();
|
|
233
|
+
}
|
|
234
|
+
offset += line.length + 1;
|
|
235
|
+
}
|
|
236
|
+
flush();
|
|
237
|
+
return {
|
|
238
|
+
count: occurrences.length,
|
|
239
|
+
samples: occurrences.slice(0, 3).map((o) => o.text.slice(0, 60).trim()),
|
|
240
|
+
occurrences,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const TRIPLET_RE = /\b([\p{L}][\p{L}'’-]*), ([\p{L}][\p{L}'’-]*), (?:and|und) ([\p{L}][\p{L}'’-]*)\b/gu;
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* `tell-rule-of-three`, disarmed (road-to-measured-prose-tells step 1.2).
|
|
248
|
+
*
|
|
249
|
+
* The shipped pattern matched every Oxford-comma triplet, so three ordinary
|
|
250
|
+
* sentences of English scored 39.47 per 500 words and failed a cap of 3. The
|
|
251
|
+
* rule stays and loses its solo effect over ordinary lists:
|
|
252
|
+
*
|
|
253
|
+
* - a triplet counts only when **all three members are abstract common nouns**
|
|
254
|
+
* — never names, never numbers, never a list of concrete things;
|
|
255
|
+
* - when two or more such abstract triplets share a paragraph, every one of
|
|
256
|
+
* them counts, because the repeated rhetorical move inside one paragraph is
|
|
257
|
+
* the tell the rule was written for.
|
|
258
|
+
*
|
|
259
|
+
* A single concrete triplet, and any number of concrete triplets in one
|
|
260
|
+
* paragraph, score zero. Recorded deviation: the step specified the
|
|
261
|
+
* paragraph-cluster arm over "three-item lists" without qualification; applied
|
|
262
|
+
* that way it would fire on the step's own worked example (three ordinary lists
|
|
263
|
+
* in one paragraph, which its acceptance criterion requires to pass), so the
|
|
264
|
+
* abstractness test qualifies both arms and the cluster arm escalates rather
|
|
265
|
+
* than admits.
|
|
266
|
+
*/
|
|
267
|
+
export function matchRuleOfThree(text: string, language: "en" | "de"): MatchResult {
|
|
268
|
+
const occurrences: RawOccurrence[] = [];
|
|
269
|
+
let offset = 0;
|
|
270
|
+
for (const para of text.split(/\n\s*\n/)) {
|
|
271
|
+
const abstract: RawOccurrence[] = [];
|
|
272
|
+
for (const m of para.matchAll(TRIPLET_RE)) {
|
|
273
|
+
const members = [m[1] ?? "", m[2] ?? "", m[3] ?? ""];
|
|
274
|
+
if (members.every((x) => isAbstractNoun(x, language))) {
|
|
275
|
+
abstract.push({ index: offset + (m.index ?? 0), text: m[0] });
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
occurrences.push(...abstract);
|
|
279
|
+
// `+2` restores the blank line the split consumed; an offset off by two
|
|
280
|
+
// characters would misreport a column, which is the whole point of these.
|
|
281
|
+
offset += para.length + 2;
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
count: occurrences.length,
|
|
285
|
+
samples: occurrences.slice(0, 3).map((o) => o.text.slice(0, 60).trim()),
|
|
286
|
+
occurrences,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
64
289
|
|
|
65
290
|
export const TELL_RULES: TellRule[] = [
|
|
66
291
|
// ── hard: communication artifacts pasted into a deliverable ──────────────
|
|
@@ -211,8 +436,10 @@ export const TELL_RULES: TellRule[] = [
|
|
|
211
436
|
"cluster",
|
|
212
437
|
"any",
|
|
213
438
|
0.5,
|
|
214
|
-
"Forced
|
|
215
|
-
|
|
439
|
+
"Forced abstract triplets (innovation, inspiration, and collaboration) — " +
|
|
440
|
+
"ordinary lists of names, numbers or concrete things do not count",
|
|
441
|
+
[],
|
|
442
|
+
matchRuleOfThree,
|
|
216
443
|
),
|
|
217
444
|
|
|
218
445
|
// ── cluster: filler, hedging, endings, openers ───────────────────────────
|
|
@@ -335,6 +562,100 @@ export const TELL_RULES: TellRule[] = [
|
|
|
335
562
|
),
|
|
336
563
|
|
|
337
564
|
// ── cluster: typography / structure (language-agnostic) ──────────────────
|
|
565
|
+
// Cluster rules for the six recall families, one epoch each.
|
|
566
|
+
//
|
|
567
|
+
// Every pattern below is AUTHORED HERE. The external analysis that exposed
|
|
568
|
+
// these gaps is a corpus, never a rule list: importing its word lists would
|
|
569
|
+
// carry a licence obligation and a plaintext source pin into a tracked file.
|
|
570
|
+
// Each family was promoted only at a zero clean-corpus false-positive count —
|
|
571
|
+
// ledger: internal/bench/corpora/prose-tells-epochs.md.
|
|
572
|
+
w(
|
|
573
|
+
"tell-throat-clearing",
|
|
574
|
+
"filler",
|
|
575
|
+
"cluster",
|
|
576
|
+
"en",
|
|
577
|
+
1,
|
|
578
|
+
"Openers that announce before they say anything (in today's fast-paced …, here's the thing)",
|
|
579
|
+
[
|
|
580
|
+
/\bin (?:today'?s|this) (?:fast-paced|fast paced|digital|modern|ever-changing|rapidly changing) [\w-]+\b/i,
|
|
581
|
+
/\bin an era (?:of|where|when)\b/i,
|
|
582
|
+
/\blet'?s be (?:honest|clear|real)\b/i,
|
|
583
|
+
/^\s*(?:Honestly[,?]|Look,|Here'?s the thing[,.:])/im,
|
|
584
|
+
/\bbefore we (?:dive|get|jump) into\b/i,
|
|
585
|
+
],
|
|
586
|
+
),
|
|
587
|
+
w(
|
|
588
|
+
"tell-emphasis-crutch",
|
|
589
|
+
"style",
|
|
590
|
+
"cluster",
|
|
591
|
+
"en",
|
|
592
|
+
1,
|
|
593
|
+
"Intensifier stacks standing in for a reason (cannot be overstated, truly remarkable)",
|
|
594
|
+
[
|
|
595
|
+
/\b(?:truly|really|absolutely|incredibly|extremely) (?:remarkable|powerful|essential|critical|important|unique|transformative)\b/i,
|
|
596
|
+
/\bcannot be overstated\b/i,
|
|
597
|
+
/\bgame[- ]chang(?:er|ing)\b/i,
|
|
598
|
+
/\bnothing short of\b/i,
|
|
599
|
+
/\bmore important than ever\b/i,
|
|
600
|
+
],
|
|
601
|
+
),
|
|
602
|
+
w(
|
|
603
|
+
"tell-false-agency",
|
|
604
|
+
"content",
|
|
605
|
+
"cluster",
|
|
606
|
+
"en",
|
|
607
|
+
1,
|
|
608
|
+
"Abstractions given volition (the data tells a story, technology is reshaping how we …)",
|
|
609
|
+
[
|
|
610
|
+
/\bthe data (?:tells|tell) (?:a|the) story\b/i,
|
|
611
|
+
/\b(?:the )?numbers (?:speak for themselves|don'?t lie)\b/i,
|
|
612
|
+
/\b(?:technology|innovation|AI|the market|this shift) (?:is )?(?:reshaping|redefining|transforming) (?:how|the way)\b/i,
|
|
613
|
+
/\bthis (?:approach|framework|mindset|moment) (?:invites|asks|challenges) us to\b/i,
|
|
614
|
+
],
|
|
615
|
+
),
|
|
616
|
+
w(
|
|
617
|
+
"tell-narrator-distance",
|
|
618
|
+
"language",
|
|
619
|
+
"cluster",
|
|
620
|
+
"en",
|
|
621
|
+
1,
|
|
622
|
+
"An impersonal narrator hedging at a distance (one might argue, for many teams …)",
|
|
623
|
+
[
|
|
624
|
+
/\bone might (?:argue|say|wonder|ask|note)\b/i,
|
|
625
|
+
/\bit(?:'s| is) worth (?:asking|considering|remembering) (?:whether|that|how|why)\b/i,
|
|
626
|
+
/\bfor many (?:teams|organizations|organisations|companies|people|leaders),/i,
|
|
627
|
+
/\bthere is something (?:to be said for|deeply)\b/i,
|
|
628
|
+
],
|
|
629
|
+
),
|
|
630
|
+
w(
|
|
631
|
+
"tell-vague-declarative",
|
|
632
|
+
"content",
|
|
633
|
+
"cluster",
|
|
634
|
+
"en",
|
|
635
|
+
1,
|
|
636
|
+
"A declaration that names nothing (one thing is clear, it all comes down to)",
|
|
637
|
+
[
|
|
638
|
+
/\b(?:one|two|a few) things? (?:is|are) clear\b/i,
|
|
639
|
+
/\bthe answer (?:is|isn'?t) simple\b/i,
|
|
640
|
+
/\bit all comes down to\b/i,
|
|
641
|
+
/\bthat'?s the (?:whole|real) point\b/i,
|
|
642
|
+
],
|
|
643
|
+
),
|
|
644
|
+
w(
|
|
645
|
+
"tell-binary-contrast",
|
|
646
|
+
"language",
|
|
647
|
+
"cluster",
|
|
648
|
+
"en",
|
|
649
|
+
1,
|
|
650
|
+
"Binary inversions beyond 'not just' (less about X than about Y, it's not that X — it's that Y)",
|
|
651
|
+
[
|
|
652
|
+
/\bless about\b[^.!?\n]{1,60}\bthan about\b/i,
|
|
653
|
+
/\bthis (?:is|isn'?t) about\b[^.!?\n]{1,60}\bit'?s about\b/i,
|
|
654
|
+
/\bnot (?:a|an) [\w-]+ (?:problem|question|issue)[,.]?\s+(?:but|it'?s)\b/i,
|
|
655
|
+
/\b(?:it'?s )?not (?:that|so much)\b[^.!?\n]{1,60}\bit'?s that\b/i,
|
|
656
|
+
],
|
|
657
|
+
),
|
|
658
|
+
|
|
338
659
|
w(
|
|
339
660
|
"tell-curly-quotes",
|
|
340
661
|
"style",
|
|
@@ -353,6 +674,26 @@ export const TELL_RULES: TellRule[] = [
|
|
|
353
674
|
"Vertical list items opening with a bolded header + colon",
|
|
354
675
|
[/^\s*[-*]\s+\*\*[^*\n]{2,60}:?\*\*:?\s/m],
|
|
355
676
|
),
|
|
677
|
+
w(
|
|
678
|
+
"tell-staccato-run",
|
|
679
|
+
"style",
|
|
680
|
+
"cluster",
|
|
681
|
+
"any",
|
|
682
|
+
1,
|
|
683
|
+
"Four or more consecutive declaratives of five words or fewer (manufactured punchline rhythm)",
|
|
684
|
+
[],
|
|
685
|
+
matchStaccatoRun,
|
|
686
|
+
),
|
|
687
|
+
w(
|
|
688
|
+
"tell-uniform-bullet-run",
|
|
689
|
+
"style",
|
|
690
|
+
"cluster",
|
|
691
|
+
"any",
|
|
692
|
+
1,
|
|
693
|
+
"A run of four or more identically-shaped bold-header bullets",
|
|
694
|
+
[],
|
|
695
|
+
matchUniformBulletRun,
|
|
696
|
+
),
|
|
356
697
|
w(
|
|
357
698
|
"tell-title-case-heading",
|
|
358
699
|
"style",
|
|
@@ -415,8 +756,108 @@ export const TELL_RULES_DE: TellRule[] = [
|
|
|
415
756
|
"cluster",
|
|
416
757
|
"de",
|
|
417
758
|
1,
|
|
418
|
-
"Nicht nur X, sondern Y",
|
|
419
|
-
[
|
|
759
|
+
"Nicht nur X, sondern Y — und die Form 'es geht nicht um X, sondern um Y'",
|
|
760
|
+
[
|
|
761
|
+
/\bnicht nur\b[^.!?\n]{3,80}\bsondern( auch)?\b/i,
|
|
762
|
+
/\bnicht um\b[^.!?\n]{3,80}\bsondern( um)?\b/i,
|
|
763
|
+
],
|
|
764
|
+
),
|
|
765
|
+
|
|
766
|
+
// The same six families, German half: authored in German, never translated.
|
|
767
|
+
w(
|
|
768
|
+
"tell-de-throat-clearing",
|
|
769
|
+
"filler",
|
|
770
|
+
"cluster",
|
|
771
|
+
"de",
|
|
772
|
+
1,
|
|
773
|
+
"Räuspern vor dem ersten Satz (ehrlich gesagt, mal ehrlich, eins vorweg)",
|
|
774
|
+
[
|
|
775
|
+
/\behrlich gesagt\b/i,
|
|
776
|
+
/\bmal ehrlich\b/i,
|
|
777
|
+
/\beins vorweg\b/i,
|
|
778
|
+
/\bbevor wir (?:eintauchen|loslegen|einsteigen)\b/i,
|
|
779
|
+
],
|
|
780
|
+
),
|
|
781
|
+
w(
|
|
782
|
+
"tell-de-signposting",
|
|
783
|
+
"filler",
|
|
784
|
+
"cluster",
|
|
785
|
+
"de",
|
|
786
|
+
1,
|
|
787
|
+
"Ankündigungen statt Aussagen (lass uns eintauchen, das musst du wissen)",
|
|
788
|
+
[
|
|
789
|
+
/\blass(?:t)? uns eintauchen\b/i,
|
|
790
|
+
/\btauchen wir ein\b/i,
|
|
791
|
+
/\bohne (?:lange )?Vorrede\b/i,
|
|
792
|
+
/\bdas musst du wissen\b/i,
|
|
793
|
+
],
|
|
794
|
+
),
|
|
795
|
+
w(
|
|
796
|
+
"tell-de-vague-declarative",
|
|
797
|
+
"content",
|
|
798
|
+
"cluster",
|
|
799
|
+
"de",
|
|
800
|
+
1,
|
|
801
|
+
"Feststellungen, die nichts benennen (eines ist klar, letztlich geht es darum)",
|
|
802
|
+
[
|
|
803
|
+
/\beines ist klar\b/i,
|
|
804
|
+
/\bdie Wahrheit ist\b/i,
|
|
805
|
+
/\bletztlich geht es (?:darum|um)\b/i,
|
|
806
|
+
/\bam Ende des Tages\b/i,
|
|
807
|
+
],
|
|
808
|
+
),
|
|
809
|
+
w(
|
|
810
|
+
"tell-de-generic-conclusion",
|
|
811
|
+
"filler",
|
|
812
|
+
"cluster",
|
|
813
|
+
"de",
|
|
814
|
+
1,
|
|
815
|
+
"Aufgesetzt positive Schlüsse (die Zukunft sieht rosig aus, spannende Zeiten)",
|
|
816
|
+
[
|
|
817
|
+
/\bdie Zukunft sieht rosig aus\b/i,
|
|
818
|
+
/\bspannende Zeiten\b/i,
|
|
819
|
+
/\bein Schritt in die richtige Richtung\b/i,
|
|
820
|
+
/\bdie Reise geht weiter\b/i,
|
|
821
|
+
],
|
|
822
|
+
),
|
|
823
|
+
w(
|
|
824
|
+
"tell-de-emphasis-crutch",
|
|
825
|
+
"style",
|
|
826
|
+
"cluster",
|
|
827
|
+
"de",
|
|
828
|
+
1,
|
|
829
|
+
"Verstärker statt Begründung (nicht hoch genug einschätzen, absolut entscheidend)",
|
|
830
|
+
[
|
|
831
|
+
/\bnicht hoch genug (?:einschätzen|einzuschätzen)\b/i,
|
|
832
|
+
/\b(?:absolut|wirklich|wahrhaft) (?:entscheidend|beeindruckend|einzigartig|unverzichtbar)\b/i,
|
|
833
|
+
/\bwichtiger denn je\b/i,
|
|
834
|
+
],
|
|
835
|
+
),
|
|
836
|
+
w(
|
|
837
|
+
"tell-de-false-agency",
|
|
838
|
+
"content",
|
|
839
|
+
"cluster",
|
|
840
|
+
"de",
|
|
841
|
+
1,
|
|
842
|
+
"Abstrakta mit Willen (die Zahlen sprechen für sich, die Daten erzählen)",
|
|
843
|
+
[
|
|
844
|
+
/\bdie Zahlen sprechen für sich\b/i,
|
|
845
|
+
/\bdie Daten erzählen\b/i,
|
|
846
|
+
/\b(?:Technologie|Innovation|KI|der Markt) (?:verändert|prägt|definiert) (?:die Art|neu)\b/i,
|
|
847
|
+
],
|
|
848
|
+
),
|
|
849
|
+
w(
|
|
850
|
+
"tell-de-narrator-distance",
|
|
851
|
+
"language",
|
|
852
|
+
"cluster",
|
|
853
|
+
"de",
|
|
854
|
+
1,
|
|
855
|
+
"Unpersönlicher Erzähler auf Distanz (man könnte argumentieren, für viele Unternehmen)",
|
|
856
|
+
[
|
|
857
|
+
/\bman (?:könnte|kann) (?:argumentieren|sagen|fragen)\b/i,
|
|
858
|
+
/\bes lohnt sich zu fragen\b/i,
|
|
859
|
+
/\bfür viele (?:Unternehmen|Teams|Menschen),/i,
|
|
860
|
+
],
|
|
420
861
|
),
|
|
421
862
|
];
|
|
422
863
|
|