Skip to content

governance(changelog): replace heuristic fallback ambiguity with a deterministic contract for unnumbered governed commits #675

Description

@qnbs

Context

PR #674 (fix(docs): upgrade CHANGELOG completeness check, backfill Unreleased) substantially strengthens scripts/check-doc-metrics.mjs so every post-tag governed feat|fix|perf commit must be represented in CHANGELOG.md [Unreleased].

The PR has already hardened exact PR-reference matching, parsed-entry scoping, branch-local ancestry handling, one-to-one entry assignment, negation/polarity handling, short-history correctness, and several false-positive edge cases.

During late review convergence, a structural limit of the remaining unnumbered-commit subject-slug fallback became clear: a percentage-based lexical overlap cannot reliably establish semantic equivalence between a commit subject and a release-note entry.

This issue owns that structural limitation after #674. It is intentionally separate so #674 can converge without turning a bounded changelog-governance fix into an open-ended natural-language parser.

Problem

For governed commits that do not have an authoritative trailing squash-merge PR reference, the current fallback derives significant subject tokens and accepts a changelog entry when the configured overlap threshold is met.

That is useful as a bounded compatibility fallback, but it cannot guarantee semantic identity.

Two strings can share nearly all words while describing materially different or opposite behavior.

Version discriminator example

Commit:
fix(project): reject schema v2 documents

CHANGELOG entry:
Reject schema v1 documents.

A matcher that drops or underweights short alphanumeric tokens can treat these as equivalent even though v1 vs v2 is the entire behavioral distinction.

Numeric discriminator example

Commit:
fix(db): cap retry delay at 10 seconds

CHANGELOG entry:
Cap retry delay at 20 seconds.

The prose is otherwise nearly identical, but the number changes the actual contract.

Antonym / directional discriminator example

Commit:
fix(sync): refuse remote project overwrite when local revision is newer

CHANGELOG entry:
Refuse remote project overwrite when local revision is older.

Even after retaining all significant words, a ratio-based matcher can still pass because only one discriminator differs.

The same class exists for pairs such as:

enable / disable
accept / reject
add / remove
increase / decrease
before / after
earlier / later
newer / older
retain / discard
allow / deny

Trying to close this by continuously adding lexical exceptions, polarity verbs, antonym tables, version patterns, and numeric special cases risks turning a simple governance checker into a brittle ad-hoc NLP subsystem.

Why this needs a separate architecture decision

The key question is not "which next word should the slug matcher understand?"

The key question is:

What deterministic contract should WorldScript use to prove CHANGELOG coverage for governed commits that do not have an authoritative PR number?

A mandatory governance gate should prefer stable identifiers and explicit metadata over inferred semantic equivalence wherever possible.

The post-#674 matcher can remain a bounded fallback, but it should not become the permanent semantic authority without an explicit design decision.

Goals

  1. Define a deterministic identification contract for unnumbered governed commits.
  2. Reduce or eliminate dependence on fuzzy natural-language overlap for mandatory completeness admission.
  3. Preserve the useful exact-PR path introduced/hardened by fix(docs): upgrade CHANGELOG completeness check, backfill Unreleased #674.
  4. Keep the checker deterministic, local, fast, auditable, and suitable for mandatory CI/pre-push use.
  5. Explicitly define what residual semantic ambiguity remains acceptable, if any.
  6. Prevent future review waves from repeatedly expanding lexical exception lists without an architectural boundary.

Design options to evaluate

The final solution is not prescribed by this issue, but evaluation should include at least the following.

Option A — explicit commit identifier in CHANGELOG entries

For unnumbered governed commits, require a deterministic commit reference, e.g. a full/short SHA under a defined grammar.

Example concept:

- Reject schema v2 documents. Commit abc1234.

Questions:

  • minimum safe SHA length;
  • collision handling;
  • rebases/amends before integration;
  • direct-to-main vs local feature-branch behavior;
  • whether only integrated-history SHAs are authoritative.

Option B — explicit release-note/change identifier

Introduce a repository-controlled change ID that is carried by both the governed commit/change and the corresponding CHANGELOG entry.

Example concept:

Change-Id: WS-2026-00123

or an equivalent machine-readable marker.

Questions:

  • generation/ownership;
  • uniqueness;
  • human usability;
  • interaction with squash merge;
  • whether this adds more process than the problem justifies.

Option C — require PR-number authority for governed changes

Adopt a policy that every governed user-visible feat|fix|perf change must ultimately enter main through a PR and therefore have a deterministic PR number.

Then fuzzy matching would be retained only for explicitly grandfathered historical/direct commits.

Questions:

  • whether direct commits to main are already disallowed by policy/protection;
  • emergency/hotfix path;
  • automation/bot merges;
  • imported/historical history.

Option D — hybrid deterministic path + bounded legacy fallback

Use deterministic identifiers for all new history and keep subject-slug matching only for legacy commits that predate the policy.

This may provide the best migration path if the repository does not want to retroactively rewrite historical release-note metadata.

Required discriminator inventory

Before choosing the final contract, inspect the actual post-release commit history and tests for cases where meaning depends on compact tokens.

At minimum classify:

VERSION        v1, v2, v3, schemaVersion
NUMERIC        10, 20, 250ms, 5 retries
PROTOCOL       IPv4, IPv6, HTTP/2, TLS 1.3
ARCH           x64, arm64, 32-bit, 64-bit
IDENTIFIER     R-15, S1, PR/issue IDs where semantically part of prose
ALGORITHM      SHA-256, AES-256-GCM, etc.
DIRECTION      newer/older, before/after, increase/decrease
POLARITY       enable/disable, allow/deny, accept/reject

The purpose is to prove why lexical percentage alone is insufficient, not to create an exhaustive dictionary.

Current #674 boundary

#674 should continue to own bounded concrete correctness fixes in the checker it introduces, including exact-reference correctness, branch-local classification, parsed-entry matching, and narrowly scoped tokenizer defects that can be fixed without architectural expansion.

This issue owns the general semantic ambiguity that remains after those bounded fixes.

Therefore:

Required implementation properties

Whatever design is selected must preserve:

  • exact and deterministic matching;
  • no false admission from arbitrary prose outside parsed CHANGELOG entries;
  • compatibility with squash-merge history;
  • strict push-to-main enforcement;
  • feature-branch/pre-push usability;
  • fail-closed behavior for Git/history errors where applicable;
  • no dependence on network calls or external AI/NLP services;
  • low local CPU/RAM cost;
  • deterministic CI behavior;
  • clear actionable failure messages;
  • bounded migration for existing history.

Testing requirements

The eventual implementation must include regression coverage for at least:

Exact discriminator mismatches

v1 vs v2
10 vs 20
IPv4 vs IPv6
x64 vs arm64

Opposite semantics

enable vs disable
accept vs reject
newer vs older
increase vs decrease

Positive controls

A genuinely equivalent entry must still pass under the selected deterministic contract.

History shape

Cover:

Migration / compatibility

Do not rewrite existing Git history.

If a new deterministic identifier contract is adopted:

  1. define an activation boundary (release/tag/commit/date);
  2. enforce the new contract only for history after that boundary;
  3. retain bounded legacy handling for older history where necessary;
  4. document when the legacy fallback can eventually be removed.

The migration itself must not make already-released history impossible to validate.

Non-goals

Acceptance criteria

  • The repository has an explicit written contract for how every new governed feat|fix|perf change is deterministically tied to a CHANGELOG entry.
  • New governed history no longer depends solely on percentage-based lexical overlap for completeness admission, unless that residual use is explicitly justified and bounded.
  • Version, numeric, protocol, architecture, and directional discriminators cannot be silently ignored under the selected contract.
  • Opposite semantics cannot pass merely because most words overlap.
  • The solution does not require general NLP, embeddings, an LLM, or network access.
  • Exact-PR matching from fix(docs): upgrade CHANGELOG completeness check, backfill Unreleased #674 remains authoritative for normal squash-merged PR history.
  • Legacy unnumbered history has a bounded compatibility path with a documented removal condition if applicable.
  • Feature-branch, local pre-push, detached-HEAD CI, and post-merge-main behavior remain deterministic.
  • Git/history failures remain fail-closed where governance authority would otherwise be uncertain.
  • Targeted unit/integration tests cover discriminator mismatches and positive controls.
  • Documentation/AGENTS/governance guidance is updated to state the new contract.
  • Future review findings in this structural class can be linked to this issue instead of expanding unrelated PR scope.

Relationship to PR #674

Found during late review convergence of #674.

#674 remains the owner of the current changelog-completeness gate and its bounded correctness fixes. This issue is the follow-up owner for the residual architectural limitation of heuristic matching.

Disposition: follow-up governance architecture work; explicitly non-blocking for #674 once its current concrete correctness findings and required exact-head gates are resolved.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions