Skip to content
This repository was archived by the owner on Aug 31, 2026. It is now read-only.

chore: ratchet weak public API types - #304

Merged
neubig merged 8 commits into
mainfrom
feat/oss-6127-public-type-ratchet
Aug 21, 2026
Merged

chore: ratchet weak public API types#304
neubig merged 8 commits into
mainfrom
feat/oss-6127-public-type-ratchet

Conversation

@neubig

@neubig neubig commented Jul 27, 2026

Copy link
Copy Markdown
Member

HUMAN:

I have not tested this because it's in CI, but we can test after it goes in.

  • A human has tested these changes.

Why

The client previously had no repository-wide guard against new or widened weak
public types. Important domain boundaries could silently degrade even when
generated Agent Server operations remained strong.

Fixes OpenHands/software-agent-sdk#4755
Linear: OSS-6127

Summary

  • Scan the built declaration graph reachable from every public package entry
    point and check in a reviewed budget of existing unknown sites.
  • Fail CI when a weak site is added, widened, changed, or removed without
    permanently lowering the budget.
  • Replace all remaining reachable public any declarations with unknown,
    with runtime narrowing for remote full-state events.
  • Merge main (including the Agent Server 1.40.0 schema and the merged
    canonical MCP settings operations from feat: add canonical MCP settings operations #302/feat(events): mirror SDK event payloads #318) and refresh the weak-type
    budget for the events/types refactor.

Issue Number

OpenHands/software-agent-sdk#4755 / OSS-6127

How to Test

  • npm run build
  • npm run test:public-type-budget
  • npm run check:public-type-budget
  • env -u AGENT_SERVER_URL npm run test:coverage — 18 suites, 304 tests
  • npm run check:agent-server-api — checked-in contract is current for 1.40.0
  • npm run test:agent-server-api-tooling
  • npm run test:endpoint-audit-tooling
  • npm run lint — 0 errors
  • npm run format:check — all files formatted

Live evidence refresh (2026-08-08, head 63acaf7 = final branch commit after "trigger CI")

Re-ran the full validation on the current head (Node v26.6.0):

  • npm ci -> OK.
  • npm run build -> tsc && copy-json-assets && rewrite-relative-imports OK.
  • npm run check:public-type-budget -> Public weak-type budget unchanged: 103 sites (0 any, 103 unknown).
  • npm run test:public-type-budget -> self-test passed.
  • env -u AGENT_SERVER_URL npm run test:coverage -> 18 suites, 304 tests passed (includes the remote-state and agent changes).
  • npm run lint -> 0 errors, 8 pre-existing warnings (unchanged).
  • npm run format:check -> all files Prettier-clean.

Live evidence (AGENT)

Merge conflict resolution against main was validated locally on commit
cbfeeff:

$ npm run build                     # tsc + asset copy: OK
$ npm run test:public-type-budget   # public-type-budget self-test passed
$ npm run check:public-type-budget  # Public weak-type budget unchanged: 103 sites (0 any, 103 unknown)
$ npm run check:agent-server-api    # Checked-in Agent Server contract is current (1.40.0 artifact)
$ env -u AGENT_SERVER_URL npm run test:coverage  # 18 suites, 304 tests passed
$ npm run lint                      # 0 errors, 8 pre-existing warnings
$ npm run format:check              # All matched files use Prettier code style!

The budget refresh mirrors main's events/types refactor (handwritten
interfaces became schema-derived type aliases): six stale interface: budget
tuples were removed and two type: tuples added at the current fingerprints.

Video/Screenshots

Not applicable: this adds public type and CI enforcement without changing the
Canvas UI.

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

Notes

Depends on #302. Public domain contracts, generic transport internals, and
deliberately opaque Cloud proxy payloads have separate budget groups. Generated
Agent Server declarations remain governed by the SDK OpenAPI quality allowlist
and the pinned generated-file drift check.

neubig and others added 3 commits July 27, 2026 12:39
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
@github-actions github-actions Bot added the type: chore Maintenance / chores label Jul 27, 2026
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Endpoint audit

⚠️ 7 actionable Agent Server contract divergence(s) · report-only

Contract: pinned release artifact

Category Count
Actionable client-only calls 0
Actionable server-only operations 7
Documented non-divergences 13
Agent Server contract operations 117
Audited handwritten client endpoints 119

Actionable client-only calls (0)

none

Actionable server-only operations (7)

  • GET /api/conversations/{}/events
  • GET /api/file/archive
  • GET /api/git/commits
  • GET /api/git/commits/{}/changes
  • GET /api/init
  • POST /api/conversations/{}/load_plugin
  • POST /api/init
Documented non-divergences (13)

Client calls intentionally absent from the filtered contract (11)

  • GET /
  • GET /alive
  • GET /health
  • GET /ready
  • GET /server_info

Reason: Operational Agent Server endpoints intentionally excluded from the filtered public release artifact.
Owner: OpenHands runtime maintainers

  • DELETE /api/meta-profiles/{}
  • GET /api/meta-profiles
  • GET /api/meta-profiles/{}
  • POST /api/meta-profiles/{}
  • POST /api/meta-profiles/{}/activate

Reason: Client-ahead API stacked on the pending Agent Server meta-profiles implementation.
Owner: OpenHands SDK maintainers
Tracking: OpenHands/software-agent-sdk#3744

  • POST /api/profiles/{}/validate

Reason: Client-ahead API stacked on the pending Agent Server pre-flight LLM validation endpoint.
Owner: OpenHands TypeScript client maintainers
Tracking: OpenHands/software-agent-sdk#4422

Server operations covered by an exposed browser URL (2)

  • GET /api/conversations/{}/workspace
  • GET /api/conversations/{}/workspace/{}

Reason: RemoteWorkspace.startWorkspaceSession exposes these authenticated URLs for browser iframe and file requests; they are not HttpClient method calls.
Owner: OpenHands TypeScript client maintainers

@neubig
neubig force-pushed the feat/oss-6123-canonical-mcp-settings branch from b7dc267 to dfe83a5 Compare July 28, 2026 18:42
Base automatically changed from feat/oss-6123-canonical-mcp-settings to main July 29, 2026 13:44
neubig and others added 2 commits August 2, 2026 17:13
…type-ratchet

Resolve merge conflicts against main:
- Adopt main's generated Agent Server schema (1.40.0) and the merged
  canonical MCP settings operations (endpoint-based create/patch/delete).
- Keep the PR's Canonical* type aliases and the public-type ratchet.
- Refresh config/public-type-budget.json for the events/types refactor
  (interface -> type aliases) merged from main.

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
@neubig
neubig marked this pull request as ready for review August 15, 2026 00:15
@neubig
neubig requested a review from all-hands-bot August 15, 2026 03:32
@all-hands-bot

Copy link
Copy Markdown
Contributor

🤖 OpenHands is reviewing this PR.

Head commit: 83f7eefb82a7477b19e62fca5d11d52de9aff661
View the conversation: https://oss-agent-canvas.ngrok.dev/conversations/ffaa676b-40fa-470f-a7b7-920cd2029fb3

This comment was posted by an AI agent (OpenHands).

@all-hands-bot all-hands-bot 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.

This review was created by an AI agent (OpenHands) on behalf of the repository maintainers.

Summary

This PR introduces a repository-wide weak-type ratchet over the built public declaration graph, replaces reachable public any with unknown, and adds runtime narrowing for remote full-state events. I reviewed the diff across all 11 files, the budget config, the checker script, and the remote-state runtime changes.

No material issues found. The change is well-structured and the type tightenings are correct.

Key observations

  • remote-state.ts runtime narrowing is a genuine improvement. The previous Object.assign(this.cachedState, event.value) silently coerced non-object primitives (e.g. a string value would spread its characters as numeric-key properties into the cache). The new isRecord guard throws a descriptive error before any mutation, leaving the cache intact. The AsyncLock.acquire finally block releases the lock on the throw, and the new test verifies post-throw cache integrity (getExecutionStatus still resolves). Correct.

  • normalizeFullState rewrite is type-safe. Replacing (info as any).full_state with a typed intersection ConversationInfo & { full_state?: ConversationInfo } preserves the unwrap behavior without weakening types. Good.

  • getWorkspace() return type (ConversationInfo['workspace'] = unknown) and modelDump() (Record<string, unknown>) are correct tightenings from any. Callers depending on the old any ergonomics will now get compile-time narrowing requirements, which is the intended ratchet effect.

  • Budget tooling is sound. The checker keys sites by module::semantic-path::kind with occurrence dedup, fingerprints the containing type node text, excludes dist/generated/, and enforces add/widen/change/remove failures. The self-test covers the four comparison cases plus budget-document validation. check:public-type-budget builds first so it always scans fresh .d.ts output.

  • Canonical MCP type aliases in clients.ts/index.ts are pure type-level re-exports with no runtime impact.

  • CI/pre-commit integration runs both the self-test and the budget check, matching the documented workflow.

Risk assessment

Low risk. This is a chore/type-safety PR. The only runtime behavioral change (updateStateFromEvent now throws on non-object full-state values instead of silently corrupting the cache) is strictly more defensive and is covered by a regression test. No public API signatures change beyond type tightening, and generated declarations remain governed by the separate OpenAPI drift check.

all-hands-bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Review complete.

This review was performed through OpenHands Cloud Automation. You can log in and view the conversation here.

@all-hands-bot all-hands-bot 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.

🟢 Good taste — Clean, pragmatic ratchet design. All 15 CI checks pass, including the new public-type-budget job.

The ratchet approach is the right call here: check in the current weak-type budget as a baseline, fail CI on any regression, and let individual PRs shrink the budget incrementally. The script is well-structured — traversal, fingerprinting, validation, and comparison are cleanly separated; the --print-current mode is clearly an inspection aid, not an auto-updater; and the built-in self-test covers compareBudget without requiring any build infrastructure.

Two small suggestions noted inline, neither blocking.

[RISK ASSESSMENT]

  • [Overall PR] ⚠️ Risk Assessment: 🟢 LOW
    The runtime changes in remote-state.ts are strictly type-tightening: anyunknown with explicit isRecord guards. The only behavioral delta is that updateStateFromEvent now throws on a non-object __full_state__ value rather than silently no-oping — which is the correct fail-fast behavior. All CI checks pass.

VERDICT:
Worth merging: Core logic is sound.

KEY INSIGHT:
Centralising full_state unwrapping in getConversationInfo() via normalizeFullState() is the right simplification — it eliminates the need for every accessor to know about the wrapper format, and makes the cache-hit path as safe as the fetch path.

This review was generated by an AI agent (OpenHands) on behalf of the user through OpenHands Automation. View conversation


Improve this review? If any feedback above seems incorrect or irrelevant to this repository, you can teach the reviewer to do better:

  1. Add a .agents/skills/custom-codereview-guide.md file to your branch (or edit it if one already exists) with the /codereview trigger and the context the reviewer is missing. See the customization docs for the required frontmatter format.
  2. Re-request a review - the reviewer reads guidelines from the PR branch, so your changes take effect immediately.
  3. When your PR is merged, the guideline file goes through normal code review by repository maintainers.

Resolve with AI? Install the iterate skill in your agent and run /iterate to automatically drive this PR through CI, review, and QA until it's merge-ready.

Was this review helpful? React with 👍 or 👎 to give feedback.

const group = groups.get(key) ?? {
category,
reason,
owner: 'neubig',

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.

🟡 Suggestion: printCurrentSites() hardcodes owner: 'neubig'. Since the docs describe this flag as an inspection aid for intentional updates, another contributor running --print-current to capture a new site will get JSON that misattributes ownership if they commit it directly.

Consider reading the owner from an env var or CLI flag, or at minimum add a comment reminding the user to edit before committing:

Suggested change
owner: 'neubig',
owner: process.env.BUDGET_OWNER ?? 'neubig',

id: 'evt-2',
kind: 'ConversationStateUpdateEvent',
timestamp: '2024-01-01T00:00:00Z',
key: '__full_state__',

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.

🟡 Suggestion: This uses the raw string '__full_state__' rather than the FULL_STATE_KEY constant (not exported from remote-state.ts). If the constant is ever renamed, the test will silently exercise the wrong code path. Exporting FULL_STATE_KEY and importing it here would make the coupling explicit, or a minimal fix is a comment:

Suggested change
key: '__full_state__',
key: '__full_state__', // must match FULL_STATE_KEY in remote-state.ts

@juanmichelini juanmichelini 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.

LGTM

@neubig
neubig merged commit 4e81c6c into main Aug 21, 2026
15 checks passed
@openhands-release-bot openhands-release-bot Bot added the released: v1.39.0 Shipped in v1.39.0 label Aug 27, 2026
@openhands-release-bot

Copy link
Copy Markdown
Contributor

🚀 Released in v1.39.0.

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

Labels

released: v1.39.0 Shipped in v1.39.0 type: chore Maintenance / chores

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ratchet weak public API types after the Agent Server contract rollout

3 participants