Skip to content

Add release validator and regression tracking - #590

Open
alisawavezen12 wants to merge 18 commits into
masterfrom
feat/release-validator-worker
Open

alisawavezen12 wants to merge 18 commits into
masterfrom
feat/release-validator-worker

Conversation

@alisawavezen12

@alisawavezen12 alisawavezen12 commented Sep 17, 2026 •

Copy link
Copy Markdown

Summary

Introduce release-based detection of likely fixed events and track regressions when resolved events occur again.

Changes

  • Add a scheduled release-validator worker.
  • Check unchecked releases between 24 hours and 30 days old.
  • Mark events with the first eligible resolvedInRelease.
  • Store release names directly on new repetitions.
  • Track regressionInRelease for occurrences in the resolved or newer releases.
  • Support repeated resolution and regression cycles.
  • Add tests for validation, grouping, release maps, and regression scenarios.

@codex-assistant

Copy link
Copy Markdown

Please add a PR description 🙂

Respect the reviewers — a description helps others understand the changes and review them faster. Keep it short, clear, and to the point. It also serves as documentation for future reference.

The PR was moved to Draft until a description is added.

@codex-assistant

Copy link
Copy Markdown

Please add a PR description 🙂

Respect the reviewers — a description helps others understand the changes and review them faster. Keep it short, clear, and to the point. It also serves as documentation for future reference.

The PR was moved to Draft until a description is added.

@codex-assistant

Copy link
Copy Markdown

Please add a PR description 🙂

Respect the reviewers — a description helps others understand the changes and review them faster. Keep it short, clear, and to the point. It also serves as documentation for future reference.

The PR was moved to Draft until a description is added.

@codex-assistant

Copy link
Copy Markdown

Please add a PR description 🙂

Respect the reviewers — a description helps others understand the changes and review them faster. Keep it short, clear, and to the point. It also serves as documentation for future reference.

The PR was moved to Draft until a description is added.

@alisawavezen12 alisawavezen12 changed the title Add release validator worker Add release validator and regression tracking Sep 18, 2026
@codex-assistant

Copy link
Copy Markdown

Please add a PR description 🙂

Respect the reviewers — a description helps others understand the changes and review them faster. Keep it short, clear, and to the point. It also serves as documentation for future reference.

The PR was moved to Draft until a description is added.

@codex-assistant
codex-assistant Bot marked this pull request as ready for review September 18, 2026 15:25
@codex-assistant

Copy link
Copy Markdown

Thanks for adding a description — the PR is now marked as Ready for Review.

Comment thread workers/grouper/src/index.ts Outdated
return this.saveRepetition(task.projectId, newRepetition);
});

if (task.payload.release && existedEvent.resolvedInRelease) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to check if task.payload.release is newer than existedEvent.resolvedInRelease

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do this later in the markRegression function.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then we need to rename markRegression to checkForRegression

Comment thread workers/release-validator/src/utils/build-event-release-map.ts Outdated
Comment thread workers/release-validator/src/validate-releases.ts Outdated
Comment thread workers/release-validator/src/validate-releases.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Release history compatibility, durable regression reconciliation, deployment wiring, and bounded database processing remain unresolved.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 3 High severity · 1 Medium severity

Open (4)
What changed in this PR

Implements release-based event resolution and regression tracking across the validator and grouper workers.

Changes:

  • Adds release validation logic and worker scaffolding.
  • Records repetition releases and regression cycles.
  • Adds supporting tests and updates shared types.
File Description
yarn.lock Updates Hawk types dependency.
workers/​release-validator/​tests/​validate-releases.test.ts Tests release validation scenarios.
workers/​release-validator/​tests/​utils/​group-releases-by-project.test.ts Tests release grouping.
workers/​release-validator/​tests/​utils/​build-event-release-map.test.ts Tests event-release mapping.
workers/​release-validator/​tests/​index.test.ts Tests worker queue configuration.
workers/​release-validator/​src/​validate-releases.ts Implements release validation.
workers/​release-validator/​src/​utils/​group-releases-by-project.ts Groups releases by project.
workers/​release-validator/​src/​utils/​build-event-release-map.ts Maps events to releases.
workers/​release-validator/​src/​index.ts Defines the validator worker.
workers/​release-validator/​README.md Documents worker behavior.
workers/​release-validator/​package.json Declares worker metadata.
workers/​grouper/​tests/​index.test.ts Tests repetition and regression behavior.
workers/​grouper/​src/​mark-regression.ts Implements regression updates.
workers/​grouper/​src/​index.ts Records releases and invokes regression tracking.
package.json Adds scripts and updates types.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread package.json
Comment thread workers/grouper/src/index.ts
Comment thread workers/release-validator/src/utils/build-event-release-map.ts
Comment thread workers/release-validator/src/validate-releases.ts Outdated
Comment thread workers/grouper/src/mark-regression.ts Outdated
}

/**
* Mark a resolved event as regressed in the resolved or a newer release.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Mark a resolved event as regressed in the resolved or a newer release.
* Mark a resolved event as regressed if ...

Comment thread workers/grouper/src/mark-regression.ts Outdated
@@ -0,0 +1,67 @@
import { Db, ObjectID } from 'mongodb';

interface ReleaseRecord {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
interface ReleaseRecord {
type ReleaseRecordPart = Pick<ReleaseDBScheme, '_id' | 'projectId' | 'release'>

const hasRegressionForCurrentCycle = previousRegressionRelease &&
previousRegressionRelease._id.toHexString() >= resolvedReleaseId;

if (!isResolvedOrNewerRelease || hasRegressionForCurrentCycle) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

documentation needed

Comment thread workers/grouper/tests/index.test.ts Outdated
expect((await eventsCollection.findOne({})).regressionInRelease).toBe('release-c');
});

test('Should mark a resolved event as regressed in the resolved release', async () => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
test('Should mark a resolved event as regressed in the resolved release', async () => {
test('Should mark as regressed ff we later encounter this event with a release that is considered a resolving release.', async () => {

Comment thread workers/grouper/tests/index.test.ts Outdated
expect(savedRepetition.release).toBeUndefined();
});

test('Should mark a resolved event as regressed in a newer repetition release', async () => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets wrap them into describe('Regression marking, ...`

releasesByName: Map<string, ReleaseDBScheme>
): Promise<void> {
const eventGroupHashes = events.map(event => event.groupHash);
const repetitions = await repetitionsCollection.find({

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems to be deadly big. You're getting all millions of repetitions.
You need to retrieve only distinct "release" variations of each event.

Comment on lines +106 to +107
* For a regressed event, continue validation from the regression release
* instead of its original release. This enables repeated resolve cycles.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs seems unlcear to me. What is "repeated resolve cycles"?

return projectRelease._id.toHexString() > releaseId && releasesWithEvent.has(projectRelease.release);
});

if (!isNewerThanLastOccurrence || occurredInRelease || occurredInNewerRelease) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doc is missing

continue;
}

/**

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

describe the logic first, then technical notes

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redundant test

* Skip events whose original or latest occurrence release is missing from
* the project release history.
*/
if (!lastOccurrenceRelease) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

payload.release is the release where the event first occurred. For an event older than MAX_DAYS_NUMBER, the archiver has already removed that release record while the event itself is still alive. So it's skipped here on every run and never gets resolved. A removed release is always older than any candidate, so could we treat a missing one as such instead of skipping?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. If the original release was archived, I now use the event timestamp instead. I also use MAX_DAYS_NUMBER as the maximum release age.

const releasesByProject = groupReleasesByProject(releasesToCheck);

for (const [projectId, projectReleases] of releasesByProject) {
await validateProject(db, projectId, projectReleases);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it throws for one project, all remaining projects are skipped. Better log and continue.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

const repetitions = await repetitionsCollection.aggregate<Pick<RepetitionDBScheme, 'groupHash' | 'release'>>([
{
$match: {
groupHash: { $in: eventGroupHashes },

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should add { groupHash: 1, release: 1 } index to avoid excessive data reading when we need distinct releases only?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a migration for existing projects and an API change for new projects. The current code works with the existing groupHash index, I think we can move this optimization to next pr

Comment thread workers/grouper/src/index.ts Outdated
return this.saveRepetition(task.projectId, newRepetition);
});

if (task.payload.release && existedEvent.resolvedInRelease) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This issues releases query for every repetition of resolved event, even after regression is already recorded, so a noisy regressed event pays an extra job per repetition.

Do we need the previous cycle's regression release for anything?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +166 to +188
const releasesWithEvent = eventReleases.get(event.groupHash) || new Set<string>();

for (const release of releasesToCheck) {
const releaseId = release._id.toHexString();
const isNewerThanLastOccurrence = releaseId > lastOccurrenceRelease._id.toHexString();
const occurredInRelease = releasesWithEvent.has(release.release);

/**
* A repetition in any later release, including one younger than 24 hours,
* proves that this candidate did not fix the event.
*/
const occurredInNewerRelease = allProjectReleases.some(projectRelease => {
return projectRelease._id.toHexString() > releaseId && releasesWithEvent.has(projectRelease.release);
});

/**
* A candidate resolves the event only if it was deployed after the latest
* occurrence and the event appears neither in that candidate nor in any
* newer release. Otherwise, continue with the next candidate.
*/
if (!isNewerThanLastOccurrence || occurredInRelease || occurredInNewerRelease) {
continue;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allProjectReleases is rescanned for every candidate of every event. A candidate resolves the event only if it's newer than every release the event occurred in, so that bound can be computed once per event.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants