Skip to content

Audit the public API and dependencies before M2 - #83

Merged
siddhss5 merged 4 commits into
mainfrom
audit/public-api
Oct 5, 2026
Merged

siddhss5 merged 4 commits into
mainfrom
audit/public-api

Conversation

@siddhss5

@siddhss5 siddhss5 commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Closes #32
Closes #82
Closes #84
Refs #85

This applies the milestone consolidation gate to the surface M1 shipped, before M2 adds to it. The full record, covering method, each disposition, dependencies, baseline measures, and legacy code, is docs/decisions/0001-m1-public-api-audit.md.

Public names: 86 → 87, now closed

Two rules decide the top level:

11 names left:

  • check_command, check_request, check_observation, check_applied;
  • json_schema, record_types, encode, decode;
  • the type aliases InstantCommand, RuntimeEvent, TraceSink, whose members are all public.

12 names joined, because public results already exposed them and the old list omitted them:

  • through RobotPackage.manifest and PackageReport.model_format: ModelEntry, ModelFormat, ProfileEntry, FileEntry, InferenceSettings, InferenceMode, CandidateChoice;
  • through RobotPackage.files and PackageReport.files: ResolvedFile;
  • through RobotPackage.inference and PackageReport.inference: InferenceReport, Candidate, CandidateKind, CandidateOutcome.

The candidate outcome enum was renamed from Outcome to CandidateOutcome, because ssrobot.Outcome says nothing on its own. Its values, and so the wire form, are unchanged; only the generated description text in two schemas changes.

AssetStore, Value, PackageManifest, and SourceItem stay. dumps and loads are now typed against Value | Record instead of the private Struct base. The round-trip test encodes and decodes observations containing ArrayValue using only top-level imports.

contracts.md has a Public surface section that owns the list of 87 names, grouped by who uses them, and states the closure rule. The only public interfaces beyond them are the ssrobot.conformance module and the ssrobot command.

MobileBase and BaseTwistCommand: kept for Opendubs

Neither Geodude nor ADA has a mobile base. Opendubs, with two OpenArm manipulators, a pan-tilt head, and a mecanum base, is their first external consumer, in M2 (#85). Both docs/architecture.md and the decision record name it. If #85 has not used them by the M2 gate, that gate removes them. Mobile-base inference (#71) stays deferred.

Dependencies and evidence

  • scripts/check_core_imports.py gains a fourth check, closure. It walks every public name's parameters, return values, fields, and properties transitively, and fails on any package type that is not public.
    • With AssetStore, PackageManifest, and SourceItem removed from __all__, it reports exactly those three, reached from loads and load_package.
  • --report PATH writes imports.json, with the requirements, the non-standard-library modules loaded, the submodules, the public names, and any failures. The installed job writes it from the clean wheel install and uploads it with installed-conformance.
  • On a clean install it reports 87 names, 0 requirements, 0 third-party modules loaded, and no failures. Two runs were byte-identical.

Legacy code

The dispositions for mj_manipulator, mj_environment, and the asset manager are deferred to #30 (M5). M1 adds nothing that replaces them, and the gate requires #30 to name what it retires.

Verification

  • ruff format --check, ruff check, mypy --strict, and git diff --check are clean.
  • The schema drift and meta-schema checks pass, so no wire form changed.
  • 50 tests pass, and conformance runs.
  • The contracts.md table was checked against ssrobot.__all__: 87 names, no duplicates, no difference.
  • Relative links in the README and docs all resolve.

Fifteen names that only ssrobot's own modules or repository tooling use
leave the top level and stay importable from their modules, so ssrobot
exports 71 names instead of 86. contracts.md lists the public surface by
who uses it. The dependency gate can write an import report, which the
installed-wheel job uploads. Decision record 0001 gives the method, each
disposition, the baseline gate measures, and defers legacy dispositions to
the M5 migration. MobileBase and BaseTwistCommand, which have no consumer,
go to #82.

Closes #32
@siddhss5

siddhss5 commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

The audit is well scoped and the dependency evidence is useful, but two boundary fixes are needed before merge.

  1. Keep the public API closed under its own types. The audit removes AssetStore, although it remains an explicit argument of the public dumps() and loads() functions and is required for public records containing ArrayValue. It also removes PackageManifest and SourceItem, although public loader results expose them through RobotPackage.manifest, RobotPackage.items, and PackageReport.items. Importing these from modules that this PR declares to be implementation details is not a complete supported API. Keep the audited public API closed under its own signatures #84 records the alternatives and acceptance criteria. The smallest fix here is to restore those three exports and update the count and tables.

  2. Complete the mobile-base exception required by Decide whether MobileBase and BaseTwistCommand stay public before M2 #82. The current decision says only that unspecified mobile-base robots are planned. Decide whether MobileBase and BaseTwistCommand stay public before M2 #82 requires the architecture status table to name the consumer and milestone. Opendubs is now that concrete consumer, with its bimanual OpenArms, pan-tilt head, and mecanum base. Validate Opendubs as the first mobile-base consumer #85 tracks its M2 validation. Please name Opendubs, M2, and Validate Opendubs as the first mobile-base consumer #85 in docs/architecture.md and the decision record before closing Decide whether MobileBase and BaseTwistCommand stay public before M2 #82.

Local verification on the PR head is otherwise clean: 50 tests pass; Ruff formatting and lint, strict mypy, schema drift, and the dependency/import report all pass. GitHub's installed-wheel and Python 3.13 jobs also pass. The Python 3.11 and reference-robot jobs were cancelled with no steps after waiting for runners, so they should be rerun rather than treated as test failures.

Review follow-ups: #84, #85.

Every type a public name accepts, returns, or exposes is now public:
AssetStore, Value, PackageManifest, and SourceItem stay top-level, and the
manifest parts, model format, resolved files, and inference types join
them, giving 87 names. dumps and loads are typed against Value and Record
rather than the private Struct base. The dependency gate fails on any
non-public type the API exposes. The architecture and the audit name
Opendubs in M2 (#85) as the first consumer of MobileBase and
BaseTwistCommand.

Closes #84
@siddhss5

siddhss5 commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

Addressed both in 0d09224.

  1. Closure (Keep the audited public API closed under its own signatures #84). I restored AssetStore, PackageManifest, and SourceItem, then checked mechanically for other gaps, and there were more. Public results also exposed ModelEntry, ModelFormat, ProfileEntry, FileEntry, ResolvedFile, the inference settings and report types, and the private Struct base through dumps/loads.
    • Those 12 types are now top-level, and dumps/loads are typed against Value | Record, with Value public. The count is 86 → 87: 11 internal names left and 12 exposed types joined.
    • So it can't regress, the dependency gate now has a closure check. It walks parameters, returns, fields, and properties transitively and fails on any non-public package type. Removing your three names makes it report exactly those three.
    • contracts.md states the closure rule, and its table matches __all__.
  2. Opendubs (Decide whether MobileBase and BaseTwistCommand stay public before M2 #82). docs/architecture.md's status table and the decision record name Opendubs, M2, and Validate Opendubs as the first mobile-base consumer #85 as the first consumer of MobileBase and BaseTwistCommand. If Validate Opendubs as the first mobile-base consumer #85 hasn't used them by the M2 gate, that gate removes them.

Verified: ruff, mypy --strict, the schema checks (no wire change), and 50 tests pass. A clean wheel install reports 87 names, 0 requirements, 0 third-party modules, and no failures.

Outcome became a top-level name in this audit and says nothing on its own.
CandidateOutcome matches Candidate, CandidateKind, and CandidateChoice. The
enum values, and so the wire form, are unchanged; only the generated
description text in two schemas names the new class.
@siddhss5
siddhss5 merged commit 9c3b4bc into main Oct 5, 2026
8 of 12 checks passed
@siddhss5
siddhss5 deleted the audit/public-api branch October 5, 2026 21:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant