Skip to content

changelog: history normalizer is lossy on rich entries (un-skip normalize-history idempotency test) #587

Description

@travis-hoover-glean

Background

In #586 the vitest test normalizeHistoryEntries > dry-runs every historical changelog entry without parse failures (packages/changelog-generator/src/__tests__/normalize-history.test.ts) was skipped (it.skip) so the suite could go green. This issue tracks fixing the root cause and re-enabling it.

Requested in #586 (comment).

Problem

The history normalizer (packages/changelog-generator/src/commands/normalize-history.tsnormalizeRelease / renderNormalizedRelease) re-renders every entry through a flat NormalizedChange model. That model can't represent several constructs used by rich, hand-authored entries, so re-normalizing them is lossy and the test's expect(result.rewritten).toBe(0) invariant fails.

Failure modes

  1. Markdown links stripped[Label](/relative/path) becomes bare Label. (relative links aren't recognized as source refs).
  2. Prose sections deleted — anything that isn't a ## Changes / ## Source bullet list (e.g. a "When to use it" paragraph) is dropped.
  3. Nested lists flattened — sub-bullets are collapsed to a single level.
  4. Frontmatter tags dropped — not re-emitted by the renderer.
  5. Stray trailing periods — a . is appended even to lines ending in : or to URL paths (e.g. /people/{person_id}/photo/people/{person_id}/photo.).

Example cases (use as test fixtures)

Case 1 — 2026-05-18-custom-metadata-api-ga.md (rich entry)

Input (excerpt):

---
title: 'Custom Metadata API is Generally Available'
categories: ['API']
tags: ['Indexing API', 'Feature']
---

## What's new

- **New endpoints** under `/rest/api/index`:
  - `PUT /custom-metadata/schema/{groupName}` — create or update a metadata group schema
  - `GET /custom-metadata/schema/{groupName}` — retrieve a metadata group schema

## When to use it

Custom Metadata works with any document already indexed in Glean. ... see [Custom Properties vs Custom Metadata](/api-info/indexing/custom-metadata/custom-properties-vs-custom-metadata).

## Documentation

- [Custom Metadata Overview](/api-info/indexing/custom-metadata/overview)
- [Authentication](/api-info/indexing/custom-metadata/authentication)

Current normalizer output (degraded):

---
title: 'Custom Metadata API is Generally Available'
categories: ['API']
---

## Changes

- **New endpoints** under `/rest/api/index`:.
- `PUT /custom-metadata/schema/{groupName}` — create or update a metadata group schema.
- `GET /custom-metadata/schema/{groupName}` — retrieve a metadata group schema.
...
- Custom Metadata Overview.
- Authentication.

Regressions: tags dropped, ## When to use it section deleted, nested endpoint list flattened, doc links → orphaned bare-text bullets, stray :..

Case 2 — 2026-05-05-rest-api-changes-open-api.md (URL period + spurious source)

-- Added endpoint: /people/{person_id}/photo
+- Added endpoint: /people/{person_id}/photo.
 ...
 - [open-api d485d2c](https://github.com/gleanwork/open-api/commit/d485d2c...)
+- [Release notes](https://github.com/gleanwork/open-api/commit/2800e7a...)

Regressions: trailing . appended to a URL path; an inferred "Release notes" source link added.

The full set of entries that currently fail to normalize idempotently (7): 2026-05-05, 2026-05-07, 2026-05-11, 2026-05-12, 2026-05-13 (-rest-api-changes-open-api), 2026-05-18-custom-metadata-api-ga, 2026-06-09-rest-api-changes-open-api.

Possible approaches

  • Idempotency guard — leave already-well-formed entries untouched (entry has a title + none of the known broken-formatting patterns the sibling test enforces). Lowest risk; doesn't touch the live release-generation renderer. (A prototype of this existed earlier in fix(test): repair vitest suite (JSX transform) + run tests in CI #586.)
  • Enrich the model — teach NormalizedChange/renderNormalizedRelease to preserve links, prose sections, nesting, and tags. Higher value but changes the shared renderer used by live changelog generation (broader blast radius).
  • Fix the narrow bugs regardless of approach: don't append . after : or to URL paths; preserve relative-link bullets.

Acceptance criteria

  • Re-running the normalizer over current history reports 0 rewrites with no content loss (links, sections, nesting, tags preserved).
  • The narrow bugs above are fixed (no stray periods on :/URLs).
  • Add the example cases above as regression test fixtures.
  • Un-skip normalizeHistoryEntries > dry-runs every historical changelog entry without parse failures and confirm it passes.
  • renderNormalizedRelease output for live release generation is unchanged (or intentionally improved with sign-off).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions