Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
cf40501
chore: merge release v3.93.1 back to main [skip ci]
github-actions[bot] Jun 29, 2026
c7b30a4
refactor(self-heal): comp does no classification — every dynamic fail…
tofikwest Jun 30, 2026
e2f1fc0
feat(self-heal): add /reveal endpoint — persist the real fail (custom…
tofikwest Jun 30, 2026
5892ccf
refactor(self-heal): delete ALL comp-side classification — the agent …
tofikwest Jun 30, 2026
923a9e9
feat(devices): show integration-imported devices in People tab + Intu…
tofikwest Jun 30, 2026
b43f545
Merge branch 'main' into tofik/dynamic-check-versioning
tofikwest Jun 30, 2026
dda64db
fix(devices): address cubic review on device-import display + sync de…
tofikwest Jun 30, 2026
3cecc79
fix(self-heal): address cubic — validate task org on reveal/rerun + h…
tofikwest Jun 30, 2026
a5f815f
feat(pentest): auto-grant one free pentest credit to new organizations
tofikwest Jun 30, 2026
9bbf38b
fix(self-heal): reveal also syncs the task status (cubic P1)
tofikwest Jun 30, 2026
ea93f29
fix(devices): second cubic pass — JumpCloud error handling, a11y, unt…
tofikwest Jun 30, 2026
597225c
test(devices): split device_sync test harness to stay under the 300-l…
tofikwest Jun 30, 2026
199e890
fix(pentest): gate initial free credit to new (un-onboarded) orgs only
tofikwest Jun 30, 2026
e50dd81
test(devices): cover JumpCloud getJson retry paths (network error, 5x…
tofikwest Jun 30, 2026
5e3c6e3
fix(self-heal): reveal task-sync only flips ACTIVE statuses (cubic P2)
tofikwest Jun 30, 2026
1a4f9de
Merge pull request #3305 from trycompai/tofik/device-import-intune-ju…
tofikwest Jun 30, 2026
c9a6697
fix(isms): resolve back nav, approver/owner display, party tags, and …
tofikwest Jun 30, 2026
1ef786e
chore(devices): remove tools/device-sync-definitions; keep device syn…
tofikwest Jun 30, 2026
608f71e
Merge pull request #3310 from trycompai/tofik/device-sync-defs-db-only
tofikwest Jun 30, 2026
cf89c5c
Merge branch 'main' into tofik/dynamic-check-versioning
tofikwest Jun 30, 2026
41ebfc4
Merge branch 'main' into tofik/free-pentest-credit-new-orgs
tofikwest Jun 30, 2026
33d6ff0
Merge pull request #3306 from trycompai/tofik/dynamic-check-versioning
tofikwest Jun 30, 2026
5e56363
Merge branch 'main' into tofik/cs-680-bug-bugs-and-ui-issues
tofikwest Jun 30, 2026
b9e88f0
Merge branch 'main' into tofik/free-pentest-credit-new-orgs
tofikwest Jun 30, 2026
5d03c15
Merge pull request #3307 from trycompai/tofik/free-pentest-credit-new…
tofikwest Jun 30, 2026
b2cf5fa
Merge branch 'main' into tofik/cs-680-bug-bugs-and-ui-issues
tofikwest Jun 30, 2026
0addc3f
Merge pull request #3309 from trycompai/tofik/cs-680-bug-bugs-and-ui-…
tofikwest Jun 30, 2026
9056d1b
fix(devices): address cubic review on the device-import display PR (#…
tofikwest Jun 30, 2026
96bc715
Merge pull request #3311 from trycompai/tofik/device-display-cubic-3308
tofikwest Jun 30, 2026
ae88ec6
feat(people): add "Don't auto-sync" option to employee sync source
tofikwest Jun 30, 2026
541a72f
fix(people): lock sync-source dropdown while disabling auto-sync
tofikwest Jun 30, 2026
3a6d4aa
Merge pull request #3312 from trycompai/tofik/cs-576-disable-employee…
tofikwest Jun 30, 2026
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
4 changes: 4 additions & 0 deletions apps/api/src/frameworks/frameworks-people-score.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ async function getMembersWithInstalledDevices({
where: {
organizationId,
memberId: { in: memberIds },
// Integration-imported devices are inventory records, not proof that
// the member installed the device agent — exclude them so they don't
// falsely satisfy the device-agent step in the people score.
source: { not: 'integration' },
},
select: { memberId: true },
distinct: ['memberId'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,23 @@ export class InternalIntegrationDebugController {
});
}

/**
* Persist the REAL result for a check (success/failed — never held) — called by
* the self-heal agent when it verdicts a held check as a genuine fail (a
* customer-side error or a real compliance finding) so the customer sees the red.
*/
@Post('connections/:connectionId/reveal')
async revealConnectionCheck(
@Param('connectionId') connectionId: string,
@Body() body: RerunCheckBody,
) {
return this.debugService.revealAndPersistCheck({
connectionId,
checkId: body.checkId,
taskId: body.taskId,
});
}

/**
* Read recently captured OAuth callback errors (recorded by the frontend on a
* failed connect). Use this to diagnose "the integration won't connect" for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@ import {
countEffectiveFailures,
decideTaskStatus,
decideRunStatus,
splitFailuresByDisposition,
failureSignalsFromEvidence,
type ClassifiableFailure,
type FailingFinding,
} from '../utils/task-check-evaluation';
import {
capEvidence,
Expand All @@ -66,9 +64,9 @@ interface ConnectionCheckOutcome {
status: 'success' | 'failed' | 'error';
findings: number;
passing: number;
/** This account's failing findings (+ redacted error signals) for exception
* filtering and self-heal classification. */
failures: ClassifiableFailure[];
/** This account's failing findings (identity only) for exception filtering.
* comp does not classify — the self-heal agent decides our-bug vs real fail. */
failures: FailingFinding[];
}

interface TaskIntegrationCheck {
Expand Down Expand Up @@ -411,7 +409,10 @@ export class TaskIntegrationsController {
let lastCheckRunId: string | undefined;
// Failing findings across all accounts (keyed like an exception) so task
// status can exclude explicitly-excepted ones below.
const failingFindings: ClassifiableFailure[] = [];
const failingFindings: FailingFinding[] = [];
// Checks HELD ('inconclusive') across accounts this run — including error-only
// runs with no findings — so a held check keeps the task pending, not 'done'.
let heldRunCount = 0;

// Sequential so each per-account run commits as it completes — a slow or
// failing account still leaves the earlier accounts' results persisted.
Expand All @@ -432,6 +433,9 @@ export class TaskIntegrationsController {
totalFindings += outcome.findings;
totalPassing += outcome.passing;
if (outcome.status === 'error') hasExecutionError = true;
// For dynamic, any non-success account run was held (pending) — count it so
// an error-only account (no findings) still keeps the task pending.
if (isDynamic && outcome.status !== 'success') heldRunCount++;
failingFindings.push(...outcome.failures);
lastCheckRunId = outcome.checkRunId;
}
Expand All @@ -449,29 +453,23 @@ export class TaskIntegrationsController {
// rule, via the shared helpers). Any real (non-excepted) finding → failed;
// else any passing result → done; else leave unchanged.
const exceptions = await loadActiveExceptionSet(organizationId);
// For DYNAMIC integrations, hold our-side/transient failures as inconclusive
// (same rule as the scheduled path): they must not fail the task. Static/AWS
// behavior is unchanged. Safe degradation: a finding with no readable error
// signal classifies as a real failure, exactly as today.
const statusFailures = isDynamic
? splitFailuresByDisposition(failingFindings).effective
: failingFindings;
const heldCount = failingFindings.length - statusFailures.length;
// For DYNAMIC integrations EVERY failure is held (pending) — comp never
// classifies; the self-heal agent decides our-bug vs real fail. So none fail
// the task here. Static/AWS behavior is unchanged (no holding).
const statusFailures = isDynamic ? [] : failingFindings;
// heldCount = checks HELD this run (incl. error-only, no-findings) so any held
// check keeps the task pending instead of slipping to 'done'.
const heldCount = heldRunCount;
if (heldCount > 0) {
this.logger.log(
`Held ${heldCount} our-side/transient finding(s) as inconclusive for task ${taskId} (manual run; not shown as failed)`,
`Held ${heldCount} check(s) as inconclusive (pending) for task ${taskId} (manual run) — not failed, not done`,
);
}
const effectiveFailures = countEffectiveFailures(
statusFailures,
exceptions,
);
// Held findings are indeterminate — exclude them from the finding count so an
// all-held run doesn't flip the task to "done" either.
const effectiveFailures = countEffectiveFailures(statusFailures, exceptions);
const newStatus = decideTaskStatus(
effectiveFailures,
totalPassing,
totalFindings - heldCount,
totalFindings,
heldCount,
);

Expand Down Expand Up @@ -674,16 +672,13 @@ export class TaskIntegrationsController {
connectionId,
checkId: checkDef.id,
resourceId: f.resourceId,
// Redacted error signals for self-heal classification (dynamic only).
...failureSignalsFromEvidence(f.evidence, checkResult.status),
}));

// Per-account run status (shared rule): a dynamic run that failed only for
// our-side/transient reasons is held as 'inconclusive' (customer never sees
// it; the agent fixes it). Static integrations keep success/failed.
// Per-account run status (shared rule): a DYNAMIC run that didn't succeed is
// held as 'inconclusive' (pending, hidden) and handed to the self-heal agent
// — comp never classifies. Static integrations keep success/failed.
const runStatus = decideRunStatus({
resultStatus: checkResult.status,
failures,
isDynamic,
});

Expand Down

This file was deleted.

138 changes: 0 additions & 138 deletions apps/api/src/integration-platform/services/check-failure-classifier.ts

This file was deleted.

Loading
Loading