@titan-design/cluster
v0.2.0
Published
Deterministic Drain template mining with pluggable line masking
Readme
@titan-design/cluster
Deterministic template mining for tool output and error blobs: a native TypeScript Drain (ICWS 2017) with the per-blob signature and frozen-mask recipe in front of it. Template ids are deterministic for a given input order and stable across restarts through snapshot and restore. Lines that Drain merges share the id of whichever line founded the cluster, so the same lines in a different order can get a different id.
Tier 0 of the titan-platform DAG. No dependencies. Extracted from active-work's AW-28 miner (TP-5).
Pipeline
import { Clusterer } from "@titan-design/cluster";
const clusterer = new Clusterer();
const result = clusterer.cluster({ partition: "Bash", text: toolResultText });
// result.templateId, result.maskedSignature, result.extractedParams, result.isNewTemplateextractSignaturereduces a multi-line blob to one anchor line plus a line-count bucket, because Drain is per-line and stack traces vary in length.hasErrorSignaltells you whether that anchor came from a recognized failure shape; successful output has unbounded cardinality and should be screened out before clustering. Anchor rules are frozen data (AnchorConfigs), one per partition, with a generic fallback; see Anchor rules.applyMasksreplaces UUIDs, hashes, paths, durations, line numbers, exit codes, and digit runs with typed placeholders, recording the first match of each as a parameter. Configs are frozen data (MaskConfigs), one per partition, with a generic fallback.DrainTreeclusters the masked tokens: fixed-depth prefix tree, token-position similarity, templates that only ever loosen. One tree per partition viaDrainTreeRegistry, so atscline and avitestline never merge.templateIdis a sha256 of(partition, maskedSignature), taken from the line that founds a Drain cluster and bound to that cluster for good. Later lines Drain merges into the cluster reuse the founder's id, even when their own masked signature differs.
Anchor rules
DEFAULT_ANCHOR_CONFIGS gives two partitions special rules. test tries test-runner
shapes first (pass/fail counts, error TS…, eslint rule ids), then the shell ones. git
anchors on its first line and never counts as an error signal. Every other partition
uses generic: shell diagnostics, …Error lines, stack frames, exit codes, then the last
non-blank line. Each AnchorConfig is a rule list plus a fallback of last-non-blank
or first-line.
Map your own partition names onto them through ClustererOptions.anchors, or the
optional third argument of extractSignature and hasErrorSignal:
import { Clusterer, DEFAULT_ANCHOR_CONFIGS, hasErrorSignal } from "@titan-design/cluster";
const anchors = { ...DEFAULT_ANCHOR_CONFIGS, Bash: DEFAULT_ANCHOR_CONFIGS.test! };
const clusterer = new Clusterer({ anchors });
hasErrorSignal("Bash", "src/a.ts(3,5): error TS2322: bad type", anchors); // trueRestarts
clusterer.snapshot() captures every tree with its learned wildcards and its
cluster-to-template bindings; Clusterer.fromSnapshot(snapshot) restores them. Storing
the snapshot and the occurrences is the caller's concern. A chunked sequence of runs
converges on the same templates and ids as one all-at-once run over the same input order.
Tuning
DrainTreeOptions: depth (4), simTh (0.55, higher than Drain3's 0.4 because dev
output is more heterogeneous than syslog), maxChildren (100), maxClusters (5000,
evicting the least-supported cluster on overflow). evicting reports when a partition
has hit the cap. An evicted cluster's template binding is dropped with it, so a template
whose cluster recurs after eviction comes back with the same id and isNewTemplate: true.
