Skip to content

Incident and deploy types#359

Merged
thisrohangupta merged 7 commits into
harness:mainfrom
piis3:incident-and-deploy-types
Jun 18, 2026
Merged

Incident and deploy types#359
thisrohangupta merged 7 commits into
harness:mainfrom
piis3:incident-and-deploy-types

Conversation

@piis3

@piis3 piis3 commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Description

Adds support for AI-SRE incident types and AI-SRE detected deployment types.

Type of Change

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • Other

Checklist

  • Tests pass
  • Typecheck passes

@CLAassistant

CLAassistant commented Jun 17, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Resolves README.md conflict by regenerating counts via docs:generate
(218 resource types, 38 toolsets after merging incidents + deploys
on top of the new chaos resources).

AI-Session-Id: 7619c357-b0da-4b97-b960-9cc632e86121
AI-Tool: claude-code
AI-Model: unknown
@thisrohangupta

Copy link
Copy Markdown
Collaborator

@cursoragent can you run Sunil's Architecture Review on this?

@sunilgattupalle sunilgattupalle self-requested a review June 18, 2026 05:07
operationPolicy: { risk: "read", retryPolicy: "safe" },
pathParams: { incident_id: "incidentId" },
responseExtractor: passthrough,
description: "Get incident details by ID",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Architecture violation: passthrough on real endpoints

All four incident operations (get, create, update, close) use responseExtractor: passthrough, which leaks raw backend envelope/debug/meta fields across the tool boundary.

CLAUDE.md explicitly prohibits this: "Never passthrough on a real endpoint. Backend envelope/debug/meta must not cross the tool boundary."

The deploys.ts in this same PR demonstrates the correct pattern with deployGetExtract. Incidents need an equivalent incidentGetExtract that projects a stable, documented shape (prettyId, title, status, severity, summary, impactedServices, environments, commanderHarnessUserId, createdAt, updatedAt).

Comment thread src/registry/toolsets/deploys.ts Outdated
return { service: b.service, version: b.version };
});
}
if (item.deployTimestamp !== undefined) slim.deployTimestamp = item.deployTimestamp;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Inconsistent null handling for deployTimestamp between list and get

compactDeploy uses if (item.deployTimestamp !== undefined) which keeps null, but deployGetExtract uses if (typeof raw.deployTimestamp === 'number') which drops null.

An agent that lists deploys will see deployTimestamp: null and then fetch the detail view expecting the same field, only to find it absent. Recommend aligning both to typeof === 'number' since a real deploy timestamp is never null.

}
if (Array.isArray(item.environments)) slim.environments = item.environments;
if (Array.isArray(item.buildVersions)) {
slim.services = item.buildVersions.map((bv) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

compactDeploy lacks isRecord guard on buildVersions items

deployGetExtract (same file) correctly uses if (!isRecord(bv)) return bv before accessing bv.service / bv.version. compactDeploy instead uses (bv ?? {}) which coerces null to {} but leaves strings/numbers as-is, then casts to Record<string, unknown>.

If the API ever returns a non-object element in buildVersions, the result is { service: undefined, version: undefined } silently emitted into the list output. The isRecord guard from type-guards.ts is already imported in this file — use it here the same way deployGetExtract does.

Comment thread src/tools/harness-list.ts
result.items = compactItems(items, compactFn);
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

harness-search doesn't receive compactFn — per-resource compaction silently skipped for search results

harness-list.ts is updated to pass registry.getResource(resourceType).compactItem into compactItems. But harness-search.ts still calls compactItems(r.items) with the old single-arg signature.

This means searching for deploy resources returns the generic whitelist-compacted shape (missing services, untruncated summary), while listing returns the correct compactDeploy shape. An agent doing search + list for the same resource type will see two different response shapes for the same data.

Comment thread src/registry/toolsets/deploys.ts Outdated
* projectIdentifier. The client still appends its default `accountIdentifier`
* query param; the Java side ignores unknown params.
*/
const DEPLOY_SCOPE = { account: "accountId", org: "orgId", project: "projectId" } as const;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

DEPLOY_SCOPE and INCIDENT_SCOPE are identical — and duplicate STO_SCOPE

Both are { account: 'accountId', org: 'orgId', project: 'projectId' } as const, and this is already the third definition of this shape (STO has the same in sto.ts). The comment in both files even says "both route through /api/v1/mc/".

This should be a single shared constant (e.g. MC_SCOPE) exported from a shared location. If the Harness platform ever renames one of these query params (it has happened between API generations), only one of the three copies will get fixed.

Comment thread src/utils/compact.ts
export function compactItems(
items: unknown[],
compactFn?: (item: Record<string, unknown>) => Record<string, unknown>,
): unknown[] {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

compactFn path bypasses the openInHarness → markdown hyperlink transform

When compactFn is provided, compactItems returns early from the custom function and never reaches the post-loop block that merges openInHarness into name as a markdown link (the slim.name = `[...](url) logic).

If a deploy item ever gains an openInHarness field, the link will either leak as a raw URL (if compactDeploy preserves it) or be silently dropped — neither is the intended behavior. The compactFn contract should document this gap, or the transform should be applied after compactFn returns.

@thisrohangupta thisrohangupta merged commit a17225e into harness:main Jun 18, 2026
7 checks passed
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