> For the complete documentation index, see [llms.txt](https://triton.pharos.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://triton.pharos.ai/triton-best-practices/standards-enforcement.md).

# Standards Enforcement

Keeping logging standards from rotting on a growing team — a short review checklist, CI guardrails for common regressions, and testing that logs get written.

Standards that live only in a wiki page decay the moment the team gets busy. The ones that stick are the ones a machine checks and a reviewer glances at. This page is the practical layer: a review checklist short enough that people actually use it, a few CI guardrails that catch the regressions that matter, and how to test your logging.

## The review checklist

Keep it short or it won't get used. These are the things worth a reviewer's eyes on any PR that logs:

* **Template set, `Triton.t` used.** Shared fields (category/area) come from a template, not repeated by hand.
* **Real taxonomy.** Category/Type/Area are enum values, not ad-hoc strings; new domains were added to the enum, not smuggled in as text.
* **Honest levels.** `ERROR` only for genuine failures; loop chatter at `FINE` or below; nothing gated behind `if (debug)`.
* **Exceptions logged with `.exception(e)`** and published (`logNow` on the failure path), then rethrown unless recovery is intentional.
* **Flush coverage.** Every path that buffers also flushes — end of handler/execute, or a `finally`.
* **Context present.** Related objects on logs that concern records; durations on work worth trending; sanitized payloads on callouts.
* **Transactions correlated.** Cross-boundary work starts/resumes/stops a transaction; LWC passes the id (or uses cache).
* **No `System.debug`, no `Triton.instance`.** The first is invisible telemetry; the second is the removed 1.x singleton.

{% hint style="info" %}
**Two of these are non-negotiable and cheap to automate:** no `System.debug` in application code, and no `Triton.instance` anywhere (it doesn't exist in 2.0 — its presence means un-migrated code). Put both in CI so no reviewer has to remember them.
{% endhint %}

## CI guardrails

You don't need a heavyweight ruleset — a handful of targeted checks catch the regressions that actually happen. Start with grep-level checks in your pipeline; graduate to PMD when you want structure.

### Grep-level checks (fast, zero setup)

{% code lineNumbers="true" %}

```bash
# Fail the build on System.debug in application code (allow it in tests if you like)
if grep -rn "System.debug" force-app/main --include=*.cls | grep -v "Test.cls"; then
  echo "❌ System.debug found — use Triton logging instead."
  exit 1
fi

# Fail on any lingering 1.x singleton usage
if grep -rn "Triton\.instance" force-app/main --include=*.cls; then
  echo "❌ Triton.instance is 1.x — migrate to the static API (Triton.log/Triton.t)."
  exit 1
fi
```

{% endcode %}

### PMD rules

If you run PMD, encode the same two rules so they show up in the same report as the rest of your static analysis:

{% code lineNumbers="true" %}

```xml
<rule name="NoSystemDebug" language="apex"
      message="Use Triton logging, not System.debug"
      class="net.sourceforge.pmd.lang.rule.regex.RegexRule" severity="3">
  <properties>
    <property name="regex" value="System\.debug\s*\(" />
  </properties>
</rule>

<rule name="NoTritonInstance" language="apex"
      message="Triton.instance is the removed 1.x singleton — use the static API"
      class="net.sourceforge.pmd.lang.rule.regex.RegexRule" severity="3">
  <properties>
    <property name="regex" value="Triton\.instance" />
  </properties>
</rule>
```

{% endcode %}

### Higher-value checks (worth building once)

Regex can't see intent, but a couple of custom rules pay off:

* **Ad-hoc taxonomy strings.** Flag `.area('...')` / `.type('...')` string literals where an enum value exists, to catch drift before it fragments your data.
* **Caught-but-not-logged.** Flag `catch` blocks with no Triton call — the most common way errors go dark.
* **Positive coverage.** Require at least one Triton log in `@AuraEnabled` methods, Queueables, and Batch `start`/`finish` — the entry points where visibility matters most.

{% hint style="warning" %}
**Don't over-fit your CI.** A rule like "every `makeBuilder()` must be near a `setTemplate()`" sounds tidy but produces false positives — `makeBuilder()` is exactly how you *build* a template. Automate the checks with crisp true/false answers (`System.debug`, `Triton.instance`); leave the judgment calls to the review checklist.
{% endhint %}

## Testing your logging

You generally don't want tests coupled to Pharos storage internals, but for critical paths it's reasonable to assert that a log was actually written with the right classification. Logs surface as `pharos__Log__c` records after publishing:

{% code lineNumbers="true" %}

```apex
@IsTest
static void logsAnErrorOnFailure() {
    Test.startTest();
    try {
        MyService.doThatThrows();
    } catch (Exception e) {
        // expected
    }
    Test.stopTest();   // platform events publish here

    List<pharos__Log__c> logs = [
        SELECT Id, pharos__Category__c, pharos__Area__c, Log_Level__c
        FROM pharos__Log__c
    ];
    System.assert(!logs.isEmpty(), 'An error log should have been written');
    System.assertEquals('ERROR', logs[0].Log_Level__c);
}
```

{% endcode %}

{% hint style="info" %}
**Platform events publish on `Test.stopTest()`.** Buffered logs won't exist as `pharos__Log__c` records until the flush completes — so query for them *after* `Test.stopTest()`, not before.
{% endhint %}

For services where you'd rather not touch storage at all, hide logging behind a thin façade you can mock in unit tests, and reserve the `pharos__Log__c` assertions for a few integration-level tests on your highest-value flows.

## Rolling it out on a team

Standards land best gradually and with a light touch:

1. **Publish the vocabulary and the checklist** — the [taxonomy](/triton-best-practices/taxonomy-and-standards.md) and this page. People can't follow what isn't written down.
2. **Automate the two hard rules** — `System.debug` and `Triton.instance` in CI, day one.
3. **Enforce on new code first.** Hold PRs to the standard; don't block on retrofitting everything at once (that's what [Legacy Code Logging Rollout](/triton-best-practices/legacy-migration.md) is for).
4. **Tighten over time.** Add the higher-value checks once the basics are habitual, and revisit the standard as you learn what actually helps in production.

## Related reading

* The vocabulary these checks enforce: [Taxonomy & Standards](/triton-best-practices/taxonomy-and-standards.md).
* Retrofitting standards into old code safely: [Legacy Code Logging Rollout](/triton-best-practices/legacy-migration.md).
