Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/gittensory-engine/src/prompt-packet.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Metadata-only prompt-packet builder (#2321): four analyze-phase text fields scrubbed with the same PUBLIC_UNSAFE_TERMS / PUBLIC_LOCAL_PATH_INLINE vocabulary as src/signals/redaction.ts (duplicated here so gittensory-engine stays standalone).

/** Canonical economic/identity term vocabulary (alternation source only — mirrors `PUBLIC_UNSAFE_TERMS`). */
const PUBLIC_UNSAFE_TERMS = String.raw`(?:reward|score|wallet|hotkey|coldkey|mnemonic|payout|ranking)\w*|farming|raw[-_\s]?trust|trust[-_\s]?score|private[-_\s]?reviewability|reviewability`;
const PUBLIC_UNSAFE_TERMS = String.raw`(?:reward|score|wallet|hotkey|coldkey|mnemonic|payout|ranking|cohort)\w*|miner[-_\s]?originated|human[-_\s]?originated|farming|raw[-_\s]?trust|trust[-_\s]?score|private[-_\s]?reviewability|reviewability`;

/** Canonical local-filesystem-root vocabulary (alternation source only — mirrors `PUBLIC_LOCAL_PATH_INLINE`). */
const PUBLIC_LOCAL_PATH_INLINE = String.raw`/Users/|/home/|/root/|/var/|/opt/|/tmp/|/private/|[A-Za-z]:[\\/]Users[\\/]|[A-Za-z]:[\\/]Program Files[\\/]`;
Expand Down
7 changes: 3 additions & 4 deletions src/review/maintainer-recap-wire.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,9 @@ export async function runMaintainerRecapJob(
for (const repoFullName of repoNames) {
try {
const [gatePrecision, calibration] = await Promise.all([
// #4521: a periodic digest is exactly the "occasional aggregate view" includeCohorts was designed
// for -- unlike a hot webhook path, one extra Gittensor API call per repo per recap run is a small,
// bounded cost, so this call site opts in by default rather than needing its own separate flag.
loadGatePrecisionReport(env, repoFullName, { windowDays: resolvedWindowDays, includeCohorts: true }),
// Keep scheduled notification digests on the public-safe aggregate path: cohort splits are
// maintainer-authenticated diagnostics, not Discord/Slack recap content.
loadGatePrecisionReport(env, repoFullName, { windowDays: resolvedWindowDays }),
buildRepoOutcomeCalibration(env, repoFullName, resolvedWindowDays),
]);
repos.push({ gatePrecision, calibration });
Expand Down
25 changes: 0 additions & 25 deletions src/services/maintainer-recap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,27 +133,14 @@ export function buildMaintainerRecap(args: MaintainerRecapInputs): RecapReport {
totals.gateFalsePositiveRate !== null
? `Gate false-positive rate: ${Math.round(totals.gateFalsePositiveRate * 100)}% (${totals.gateFalsePositives}/${totals.blocked} block(s) later merged).`
: `Gate false-positive rate: not enough blocked PRs in the window to report.`;
// #4521: an additional summary line ONLY when the cohort split was actually requested this run — omitted
// (not "N/A") when absent, so a legacy call site's summary output is byte-identical to before this existed.
const cohortLine = cohorts ? [formatCohortSummaryLine(cohorts)] : [];
const summary = [
`Maintainer recap over the last ${windowDays} day(s): ${repos.length} repo(s), ${totals.reviewed} reviewed, ${totals.merged} merged, ${totals.closed} closed.`,
rateLine,
`${totals.gateOverrides} maintainer override(s), ${totals.reversals} recommendation reversal(s).`,
...cohortLine,
].map(sanitizeRecapText);
return { generatedAt: args.generatedAt, windowDays, repos, totals: { ...totals, ...(cohorts ? { cohorts } : {}) }, summary };
}

/** #4521: "N of M blocked PRs were miner-originated, precision X% vs human Y%" — mirrors rateLine's own
* null-below-sample handling per cohort (a cohort's own falsePositiveRate is already null when its blocked
* count is 0, from GatePrecisionCohortReport's MIN_SAMPLE floor at the source). */
function formatCohortSummaryLine(cohorts: { miner: MaintainerRecapCohortCounts; human: MaintainerRecapCohortCounts }): string {
const rate = (counts: MaintainerRecapCohortCounts): string =>
counts.gateFalsePositiveRate !== null ? `${Math.round(counts.gateFalsePositiveRate * 100)}%` : "n/a";
return `Miner-originated: ${cohorts.miner.blocked} blocked (${rate(cohorts.miner)} false-positive) — Human-originated: ${cohorts.human.blocked} blocked (${rate(cohorts.human)} false-positive).`;
}

/** Redact one free-text line bound for the public digest body. Two arms mirroring weekly-value-report.ts's
* sanitizeReportText: scrub any absolute local path to `<redacted-path>`, then blank the WHOLE line to
* `<redacted>` if any economic/identity term (reward/score/wallet/payout/…) survives. Defense in depth — the
Expand Down Expand Up @@ -199,24 +186,12 @@ export function formatMaintainerRecap(report: RecapReport): string {
`- Overrides: ${totals.gateOverrides}`,
`- Reversals: ${totals.reversals}`,
"",
// #4521: an entire section, only when the cohort split was requested this run -- omitted (not an empty
// header) when absent, so the digest degrades gracefully to exactly today's output.
...(totals.cohorts ? ["## Cohorts", ...formatCohortLines(totals.cohorts), ""] : []),
"## Per-repo",
...recapSectionLines(perRepoLines, "_No repositories in this window._"),
];
return `${lines.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd()}\n`;
}

/** #4521: render the aggregate miner-vs-human split as two bullet lines, mirroring the Totals section's own
* "gate false positives: N/M (rate)" phrasing per cohort. */
function formatCohortLines(cohorts: { miner: MaintainerRecapCohortCounts; human: MaintainerRecapCohortCounts }): string[] {
const line = (label: string, counts: MaintainerRecapCohortCounts): string => {
const cohortRate = counts.gateFalsePositiveRate !== null ? `${Math.round(counts.gateFalsePositiveRate * 100)}%` : "n/a";
return `- ${label}: ${counts.gateFalsePositives}/${counts.blocked} gate false positives (${cohortRate})`;
};
return [line("Miner-originated", cohorts.miner), line("Human-originated", cohorts.human)];
}

export type RunMaintainerRecapResult =
| { skipped: true; reason: "disabled" }
Expand Down
4 changes: 2 additions & 2 deletions src/signals/redaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// `isPublicSafeText` first, so a single regex governs redaction and new surfaces cannot drift their own copy.
//
// It rejects gittensor economic/identity signals (rewards, raw/trust score, wallet/hotkey/coldkey/mnemonic,
// farming, payout, ranking, (private) reviewability) and local filesystem paths.
// farming, payout, ranking, cohort diagnostics, (private) reviewability) and local filesystem paths.
//
// The pattern is intentionally NON-GLOBAL so `.test()` stays stateless (no `lastIndex` carry-over between
// calls) and the exported constant can be reused safely across call sites and modules.
Expand All @@ -20,7 +20,7 @@
// estimate" and extra terms like "seed phrase"/"private key" for cleaner output, and deliberately do not
// redact a bare "score"/"reward"). Those are curated for their surface, not drift of this core, so they are
// intentionally NOT collapsed onto `PUBLIC_UNSAFE_TERMS`.
export const PUBLIC_UNSAFE_TERMS = String.raw`(?:reward|score|wallet|hotkey|coldkey|mnemonic|payout|ranking)\w*|farming|raw[-_\s]?trust|trust[-_\s]?score|private[-_\s]?reviewability|reviewability`;
export const PUBLIC_UNSAFE_TERMS = String.raw`(?:reward|score|wallet|hotkey|coldkey|mnemonic|payout|ranking|cohort)\w*|miner[-_\s]?originated|human[-_\s]?originated|farming|raw[-_\s]?trust|trust[-_\s]?score|private[-_\s]?reviewability|reviewability`;

// `PUBLIC_LOCAL_PATH_INLINE` is the canonical local-filesystem-root vocabulary (alternation source only —
// no flags, no anchors), the path analogue of `PUBLIC_UNSAFE_TERMS`. Public surfaces that detect or scrub
Expand Down
22 changes: 7 additions & 15 deletions test/unit/maintainer-recap-format.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,7 @@ describe("formatMaintainerRecap (#2240)", () => {
expect(body).not.toContain("payout");
});

// #4521: the whole "## Cohorts" section is additive -- absent when totals.cohorts is, present (with both
// cohort lines) when it's supplied.
it("omits the Cohorts section entirely when totals.cohorts is absent (byte-identical to before the split existed)", () => {
const body = formatMaintainerRecap(emptyReport());
expect(body).not.toContain("## Cohorts");
expect(body).not.toContain("Miner-originated");
});

it("renders the Cohorts section with both lines when totals.cohorts is present", () => {
it("omits cohort diagnostics from the public recap even when totals.cohorts is present", () => {
const report: RecapReport = {
...emptyReport(),
totals: {
Expand All @@ -109,14 +101,14 @@ describe("formatMaintainerRecap (#2240)", () => {
human: { blocked: 5, gateFalsePositives: 0, gateFalsePositiveRate: 0 },
},
},
summary: ["Miner-originated: 3 blocked", "Human-originated: 5 blocked", "Cohorts diagnostics"],
};
const body = formatMaintainerRecap(report);
expect(body).toContain("## Cohorts");
expect(body).toContain("- Miner-originated: 1/3 gate false positives (33%)");
expect(body).toContain("- Human-originated: 0/5 gate false positives (0%)");
// The section sits between Totals and Per-repo, and the trailing-blank-line collapse still holds.
expect(body.indexOf("## Totals")).toBeLessThan(body.indexOf("## Cohorts"));
expect(body.indexOf("## Cohorts")).toBeLessThan(body.indexOf("## Per-repo"));
expect(body).not.toContain("## Cohorts");
expect(body).not.toContain("Miner-originated");
expect(body).not.toContain("Human-originated");
expect(body).not.toContain("Cohorts diagnostics");
expect(body.match(/- <redacted>/g)).toHaveLength(3);
expect(body).not.toMatch(/\n{3,}/);
});
});
18 changes: 6 additions & 12 deletions test/unit/maintainer-recap-wire.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,7 @@ describe("runMaintainerRecapJob — cross-repo digest (#1963, #2248)", () => {
expect(posted).toHaveLength(1);
});

// #4521: runMaintainerRecapJob always opts loadGatePrecisionReport into includeCohorts -- proves the split
// actually reaches the finished report/formatted digest, not just that the wiring doesn't crash (every
// OTHER test in this file also exercises includeCohorts implicitly since it's now unconditional, but none
// of them seed a gate block or a miner author, so none would catch a real miner-vs-human misclassification).
it("populates totals.cohorts end-to-end when a blocked PR's author is a confirmed miner", async () => {
it("keeps miner cohort diagnostics out of the scheduled external recap", async () => {
const env = createTestEnv({ DISCORD_WEBHOOK_URL: HOOK });
await seedRegisteredRepo(env, "owner/alpha");
await upsertPullRequestFromGitHub(env, "owner/alpha", { number: 1, title: "miner PR", state: "closed", user: { login: "miner-alice" } });
Expand All @@ -211,13 +207,11 @@ describe("runMaintainerRecapJob — cross-repo digest (#1963, #2248)", () => {

const { report, formatted } = ranRecap(await runMaintainerRecapJob(env));

expect(report.totals.cohorts).toMatchObject({ miner: { blocked: 1 }, human: { blocked: 1 } });
expect(report.repos[0]?.cohorts).toMatchObject({ miner: { blocked: 1 }, human: { blocked: 1 } });
expect(formatted).toContain("## Cohorts");
// Neither PR merged (both stay "closed"), so blockedThenMerged is 0 for both cohorts -- only `blocked`
// differs from zero here.
expect(formatted).toContain("Miner-originated: 0/1 gate false positives");
expect(formatted).toContain("Human-originated: 0/1 gate false positives");
expect(report.totals.cohorts).toBeUndefined();
expect(report.repos[0]?.cohorts).toBeUndefined();
expect(formatted).not.toContain("## Cohorts");
expect(formatted).not.toContain("Miner-originated");
expect(formatted).not.toContain("Human-originated");
});

it("threads a custom windowDays through to the report and the per-repo aggregators", async () => {
Expand Down
5 changes: 3 additions & 2 deletions test/unit/maintainer-recap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,9 @@ describe("buildMaintainerRecap cohort split (#4521)", () => {
miner: { blocked: 2, gateFalsePositives: 1, gateFalsePositiveRate: 0.5 },
human: { blocked: 4, gateFalsePositives: 1, gateFalsePositiveRate: 0.25 },
});
expect(report.summary[3]).toContain("Miner-originated: 2 blocked (50% false-positive)");
expect(report.summary[3]).toContain("Human-originated: 4 blocked (25% false-positive)");
expect(report.summary).toHaveLength(3);
expect(report.summary.join("\n")).not.toContain("Miner-originated");
expect(report.summary.join("\n")).not.toContain("Human-originated");
});

it("sums cohorts ACROSS repos, correctly reporting n/a for a zero-blocked cohort", () => {
Expand Down
10 changes: 9 additions & 1 deletion test/unit/prompt-packet-redaction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ function enumerateUnsafeTermFamilies(source: string): Array<{ id: string; sample
}
continue;
}
if (branch === "miner[-_\\s]?originated") {
families.push({ id: "miner-originated", sample: "miner-originated" });
continue;
}
if (branch === "human[-_\\s]?originated") {
families.push({ id: "human-originated", sample: "human_originated" });
continue;
}
if (branch === "raw[-_\\s]?trust") {
families.push({ id: "raw-trust", sample: "raw-trust" });
continue;
Expand Down Expand Up @@ -88,7 +96,7 @@ const LOCAL_PATH_SAMPLES = enumerateLocalPathSamples(PUBLIC_LOCAL_PATH_INLINE);
describe("buildPromptPacket redaction (#2321 adversarial allowlist)", () => {
it("enumerates every unsafe-term family from PUBLIC_UNSAFE_TERMS", () => {
expect(UNSAFE_TERM_FAMILIES.map((entry) => entry.id).sort()).toEqual(
["coldkey", "farming", "hotkey", "mnemonic", "payout", "private-reviewability", "ranking", "raw-trust", "reviewability", "reward", "score", "trust-score", "wallet"].sort(),
["cohort", "coldkey", "farming", "hotkey", "human-originated", "miner-originated", "mnemonic", "payout", "private-reviewability", "ranking", "raw-trust", "reviewability", "reward", "score", "trust-score", "wallet"].sort(),
);
});

Expand Down