Skip to content

feat: improve flag enricher#1786

Open
k11kirky wants to merge 1 commit intomainfrom
04-21-feat_improve_flag_enricher
Open

feat: improve flag enricher#1786
k11kirky wants to merge 1 commit intomainfrom
04-21-feat_improve_flag_enricher

Conversation

@k11kirky
Copy link
Copy Markdown
Contributor

@k11kirky k11kirky commented Apr 21, 2026

TLDR

Problem

Changes

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@k11kirky k11kirky marked this pull request as ready for review April 21, 2026 20:36
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 21, 2026

Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/enricher/src/enricher.test.ts
Line: 402-499

Comment:
**Prefer parameterised tests**

The five new `toComments` tests share the same three-step skeleton — parse a snippet, call `mockApiResponses`, assert on `toComments()` output — and differ only in the flag configuration and expected substrings. Per the team's convention they should be a single `test.each` table, e.g.:

```ts
test.each([
  {
    name: "rich line with url, active, rollout, evals",
    flagOpts: { id: 42, filters: { groups: [{ rollout_percentage: 60, properties: [] }] } },
    flagKey: "my-flag",
    evalStats: [["my-flag", 1240, 230]] as [string, number, number][],
    contains: ["active", "60% rolled out", "1,240 evals / 230 users (7d)", "feature_flags/42"],
    notContains: [],
  },
  // … inactive, ghost, quiet-flag variants …
])("toComments $name", async ({ flagKey, flagOpts, evalStats, contains, notContains }) => {
  const result = await enricher.parse(`posthog.getFeatureFlag('${flagKey}');`, "javascript");
  mockApiResponses({ flags: [makeFlag(flagKey, flagOpts)], flagEvalStats: evalStats });
  const annotated = (await result.enrichFromApi(API_CONFIG)).toComments();
  contains.forEach((s) => expect(annotated).toContain(s));
  notContains.forEach((s) => expect(annotated).not.toContain(s));
});
```

This reduces repetition without losing coverage.

**Context Used:** Do not attempt to comment on incorrect alphabetica... ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/enricher/src/comment-formatter.ts
Line: 20-22

Comment:
**"inactive" appears twice for the same flag**

When a flag has `active: false`, the comment will contain both the explicit `"inactive"` segment (line 21) and `"STALE (inactive)"` from the staleness classifier — confirmed by the test that asserts both are present. The staleness segment already communicates the reason the flag is inactive, so displaying `"inactive"` as a separate part is redundant and violates the OnceAndOnlyOnce simplicity rule.

Consider either suppressing the `active`/`inactive` label when `flag.staleness === "inactive"`, or only showing the active/inactive label and dropping it from the staleness display.

**Context Used:** Do not attempt to comment on incorrect alphabetica... ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "feat: improve flag enricher" | Re-trigger Greptile

Comment on lines +402 to 499
flagEvalStats: [["my-flag", 1240, 230]],
});
const enriched = await result.enrichFromApi(API_CONFIG);

expect(enriched.flags[0].url).toBe(
"https://test.posthog.com/project/1/feature_flags/42",
);
expect(enriched.flags[0].evaluationStats).toEqual({
evaluations: 1240,
uniqueUsers: 230,
});
});

test("toComments renders rich flag line with url, active, rollout, evals", async () => {
const code = `posthog.getFeatureFlag('my-flag');`;
const result = await enricher.parse(code, "javascript");

mockApiResponses({
flags: [
makeFlag("my-flag", {
id: 42,
filters: { groups: [{ rollout_percentage: 60, properties: [] }] },
}),
],
flagEvalStats: [["my-flag", 1240, 230]],
});
const enriched = await result.enrichFromApi(API_CONFIG);

const annotated = enriched.toComments();
expect(annotated).toContain(`Flag: "my-flag"`);
expect(annotated).toContain("active");
expect(annotated).toContain("60% rolled out");
expect(annotated).toContain("1,240 evals / 230 users (7d)");
expect(annotated).toContain(
"https://test.posthog.com/project/1/feature_flags/42",
);
});

test("toComments marks inactive flags explicitly", async () => {
const code = `posthog.getFeatureFlag('off-flag');`;
const result = await enricher.parse(code, "javascript");

mockApiResponses({ flags: [makeFlag("off-flag", { active: false })] });
const enriched = await result.enrichFromApi(API_CONFIG);

const annotated = enriched.toComments();
expect(annotated).toContain("inactive");
expect(annotated).toContain("STALE (inactive)");
});

test("toComments handles flag not in PostHog", async () => {
const code = `posthog.getFeatureFlag('ghost-flag');`;
const result = await enricher.parse(code, "javascript");

mockApiResponses({ flags: [] });
const enriched = await result.enrichFromApi(API_CONFIG);

const annotated = enriched.toComments();
expect(annotated).toContain(`Flag: "ghost-flag" \u2014 not in PostHog`);
});

test("toComments omits evaluation segment when stats missing", async () => {
const code = `posthog.getFeatureFlag('quiet-flag');`;
const result = await enricher.parse(code, "javascript");

mockApiResponses({ flags: [makeFlag("quiet-flag", { id: 7 })] });
const enriched = await result.enrichFromApi(API_CONFIG);

const annotated = enriched.toComments();
expect(annotated).toContain(`Flag: "quiet-flag"`);
expect(annotated).not.toContain("evals /");
expect(annotated).toContain(
"https://test.posthog.com/project/1/feature_flags/7",
);
});

test("getFlagEvaluationStats is called with detected flag keys", async () => {
const code = `posthog.getFeatureFlag('tracked-flag');`;
const result = await enricher.parse(code, "javascript");

mockApiResponses({
flags: [makeFlag("tracked-flag")],
flagEvalStats: [["tracked-flag", 10, 5]],
});
await result.enrichFromApi(API_CONFIG);

const calls = vi.mocked(fetch).mock.calls;
const queryPost = calls.find(
([url, init]) =>
String(url).includes("/query/") && init?.method === "POST",
);
expect(queryPost).toBeDefined();
const body = String(queryPost?.[1]?.body ?? "");
expect(body).toContain("$feature_flag_called");
expect(body).toContain("tracked-flag");
});

test("enrichFromApi with no detected usage returns empty enrichment", async () => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Prefer parameterised tests

The five new toComments tests share the same three-step skeleton — parse a snippet, call mockApiResponses, assert on toComments() output — and differ only in the flag configuration and expected substrings. Per the team's convention they should be a single test.each table, e.g.:

test.each([
  {
    name: "rich line with url, active, rollout, evals",
    flagOpts: { id: 42, filters: { groups: [{ rollout_percentage: 60, properties: [] }] } },
    flagKey: "my-flag",
    evalStats: [["my-flag", 1240, 230]] as [string, number, number][],
    contains: ["active", "60% rolled out", "1,240 evals / 230 users (7d)", "feature_flags/42"],
    notContains: [],
  },
  // … inactive, ghost, quiet-flag variants …
])("toComments $name", async ({ flagKey, flagOpts, evalStats, contains, notContains }) => {
  const result = await enricher.parse(`posthog.getFeatureFlag('${flagKey}');`, "javascript");
  mockApiResponses({ flags: [makeFlag(flagKey, flagOpts)], flagEvalStats: evalStats });
  const annotated = (await result.enrichFromApi(API_CONFIG)).toComments();
  contains.forEach((s) => expect(annotated).toContain(s));
  notContains.forEach((s) => expect(annotated).not.toContain(s));
});

This reduces repetition without losing coverage.

Context Used: Do not attempt to comment on incorrect alphabetica... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/enricher/src/enricher.test.ts
Line: 402-499

Comment:
**Prefer parameterised tests**

The five new `toComments` tests share the same three-step skeleton — parse a snippet, call `mockApiResponses`, assert on `toComments()` output — and differ only in the flag configuration and expected substrings. Per the team's convention they should be a single `test.each` table, e.g.:

```ts
test.each([
  {
    name: "rich line with url, active, rollout, evals",
    flagOpts: { id: 42, filters: { groups: [{ rollout_percentage: 60, properties: [] }] } },
    flagKey: "my-flag",
    evalStats: [["my-flag", 1240, 230]] as [string, number, number][],
    contains: ["active", "60% rolled out", "1,240 evals / 230 users (7d)", "feature_flags/42"],
    notContains: [],
  },
  // … inactive, ghost, quiet-flag variants …
])("toComments $name", async ({ flagKey, flagOpts, evalStats, contains, notContains }) => {
  const result = await enricher.parse(`posthog.getFeatureFlag('${flagKey}');`, "javascript");
  mockApiResponses({ flags: [makeFlag(flagKey, flagOpts)], flagEvalStats: evalStats });
  const annotated = (await result.enrichFromApi(API_CONFIG)).toComments();
  contains.forEach((s) => expect(annotated).toContain(s));
  notContains.forEach((s) => expect(annotated).not.toContain(s));
});
```

This reduces repetition without losing coverage.

**Context Used:** Do not attempt to comment on incorrect alphabetica... ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +20 to +22
if (flag.rollout !== null) {
parts.push(`${flag.rollout}% rolled out`);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 "inactive" appears twice for the same flag

When a flag has active: false, the comment will contain both the explicit "inactive" segment (line 21) and "STALE (inactive)" from the staleness classifier — confirmed by the test that asserts both are present. The staleness segment already communicates the reason the flag is inactive, so displaying "inactive" as a separate part is redundant and violates the OnceAndOnlyOnce simplicity rule.

Consider either suppressing the active/inactive label when flag.staleness === "inactive", or only showing the active/inactive label and dropping it from the staleness display.

Context Used: Do not attempt to comment on incorrect alphabetica... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/enricher/src/comment-formatter.ts
Line: 20-22

Comment:
**"inactive" appears twice for the same flag**

When a flag has `active: false`, the comment will contain both the explicit `"inactive"` segment (line 21) and `"STALE (inactive)"` from the staleness classifier — confirmed by the test that asserts both are present. The staleness segment already communicates the reason the flag is inactive, so displaying `"inactive"` as a separate part is redundant and violates the OnceAndOnlyOnce simplicity rule.

Consider either suppressing the `active`/`inactive` label when `flag.staleness === "inactive"`, or only showing the active/inactive label and dropping it from the staleness display.

**Context Used:** Do not attempt to comment on incorrect alphabetica... ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))

How can I resolve this? If you propose a fix, please make it concise.

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.

1 participant