# How the Hallmark Audit Verb Scores Code Against Anti-Patterns

> Learn how the Hallmark audit verb scores code against anti-patterns. It parses code, matches anti-patterns, and calculates a normalized score to improve code quality.

- Repository: [Hassan El Mghari/hallmark](https://github.com/Nutlope/hallmark)
- Tags: how-to-guide
- Published: 2026-08-02

---

**The Hallmark `audit` verb evaluates your codebase by parsing source files into abstract syntax trees, matching them against a curated markdown catalogue of anti-patterns, and calculating a normalized 0-100 score based on cumulative severity weights.**

Hallmark is a CLI tool that implements functionality through *skills*—modular definitions that register specific verbs. The `audit` verb, defined in the Hallmark skill, performs static analysis by comparing your code against known undesirable patterns stored in [`skills/hallmark/references/anti-patterns.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/anti-patterns.md).

## The Three-Stage Scoring Pipeline

The audit process follows a deterministic pipeline that transforms raw source code into a quantified quality metric.

### Loading the Anti-Pattern Catalogue

At runtime, the audit verb loads [`skills/hallmark/references/anti-patterns.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/anti-patterns.md), which contains a structured list of anti-pattern definitions. Each entry specifies a **signature** (AST pattern to match), **description**, and **weight** (severity). The parser converts this markdown into an in-memory array of pattern objects used for subsequent matching.

The catalogue format supports easy extension, allowing teams to customize scoring criteria without modifying source code.

### AST Generation and Pattern Matching

When you invoke `hallmark audit <path>`, the verb recursively walks the target directory and parses each supported source file into an Abstract Syntax Tree (AST). For JavaScript and TypeScript files, Hallmark utilizes parsers like `@babel/parser` to generate these trees.

The matching engine traverses each AST, testing nodes against every signature in the loaded catalogue. Upon detection, it records a finding containing:

- File path and line number
- Weight of the matched anti-pattern
- Descriptive text from the catalogue

This collection of findings represents the raw audit data before normalization.

### Calculating the Final Score

After traversal completes, Hallmark computes the score using weighted aggregation. The algorithm sums the weights of all detected findings (`totalWeight`) and divides by the theoretical maximum possible weight (`maxPossible`), which represents every anti-pattern in the catalogue occurring simultaneously.

The formula produces a value normalized to 0-100:

- **0** indicates a perfect codebase with zero matches
- **100** represents a codebase containing every defined anti-pattern

This normalized score provides an immediate, comparable metric for code health across projects.

## Implementation in SKILL.md

The audit functionality originates in [`skills/hallmark/SKILL.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/SKILL.md), where the skill registers the verb and defines its handler logic.

```javascript
// Conceptual excerpt from SKILL.md verb registration
skill.registerVerb('audit', async (args) => {
  const catalogue = await loadAntiPatternCatalogue();
  const files = await collectSourceFiles(args.path);
  const findings = [];

  for (const file of files) {
    const ast = parseToAST(file.content);
    for (const pattern of catalogue) {
      if (matchesPattern(ast, pattern.signature)) {
        findings.push({
          file: file.path,
          line: locateLine(ast, pattern.signature),
          weight: pattern.weight,
          description: pattern.description,
        });
      }
    }
  }

  const totalWeight = findings.reduce((s, f) => s + f.weight, 0);
  const maxPossible = catalogue.reduce((s, p) => s + p.weight, 0);
  const score = Math.round((totalWeight / maxPossible) * 100);

  reportAuditResult(score, findings);
});

```

The `loadAntiPatternCatalogue()` function specifically targets [`skills/hallmark/references/anti-patterns.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/anti-patterns.md), while `matchesPattern()` implements the AST traversal logic that identifies structural signatures.

## Running an Audit from the Command Line

Invoke the audit verb against any directory containing source code:

```bash
hallmark audit ./src

```

The CLI outputs a normalized score followed by a detailed breakdown:

```

Audit Score: 27 / 100

Detected anti-patterns:
  • src/utils/helpers.js:12 – "Deeply nested callbacks" (weight 5)
  • src/components/Widget.tsx:45 – "Excessive prop drilling" (weight 8)
  • src/api/client.ts:78 – "Hard-coded URLs" (weight 4)

```

Each line item maps directly to an entry in [`anti-patterns.md`](https://github.com/Nutlope/hallmark/blob/main/anti-patterns.md), providing actionable feedback for refactoring.

## Customizing the Anti-Pattern Catalogue

To modify scoring behavior, edit [`skills/hallmark/references/anti-patterns.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/anti-patterns.md) and add new entries using the standard format:

```markdown

### Unused imports

- **Description:** Files contain imports that are never referenced.
- **Signature:** `ImportDeclaration` nodes with no Identifier usages.
- **Weight:** 3

```

Changes take effect immediately upon the next `hallmark audit` execution, requiring no recompilation or configuration reload. The dynamic loading mechanism ensures the catalogue remains the single source of truth for quality rules.

## Summary

- The **Hallmark audit verb** operates as a registered skill that performs static code analysis through AST matching.
- It loads **anti-pattern definitions** from [`skills/hallmark/references/anti-patterns.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/anti-patterns.md), where each pattern carries a configurable severity weight.
- The scoring algorithm normalizes detected pattern weights against the theoretical maximum to produce a **0-100 score**, where lower values indicate healthier code.
- Implementation resides primarily in [`SKILL.md`](https://github.com/Nutlope/hallmark/blob/main/SKILL.md), utilizing language-specific parsers to generate ASTs for pattern detection.
- Users can extend or customize rules by editing the markdown catalogue, with changes reflecting immediately in subsequent audit runs.

## Frequently Asked Questions

### How does Hallmark calculate the audit score?

Hallmark sums the weights of all detected anti-patterns found during the AST traversal, then divides this total by the sum of all possible weights defined in the catalogue. It multiplies the result by 100 and rounds to the nearest integer, producing a normalized score where 0 represents no anti-patterns and 100 represents the worst-case scenario of every defined pattern occurring simultaneously.

### Where are the anti-pattern rules stored?

The rules live in [`skills/hallmark/references/anti-patterns.md`](https://github.com/Nutlope/hallmark/blob/main/skills/hallmark/references/anti-patterns.md) within the Hallmark repository. This markdown file contains structured entries with signatures, descriptions, and severity weights. The audit verb parses this file at runtime to build its comparison catalogue, making the rule set easily editable without changing code.

### Can I run Hallmark audit programmatically from Node.js?

Yes, you can invoke the CLI programmatically using Node.js child processes. While Hallmark exposes the `skill.registerVerb()` interface internally, external consumers typically execute the command via `child_process.exec()` or `spawn()` and parse the stdout to integrate audit results into build pipelines or custom reporting tools.

### What languages does the Hallmark audit verb support?

The audit verb supports any language for which Hallmark bundles a parser, typically JavaScript and TypeScript via `@babel/parser` or similar AST generators. The architecture allows extending support to additional languages by implementing the `parseToAST()` function for that language's grammar and adding corresponding signatures to the anti-pattern catalogue.