Skip to content

feat(sdk): add a read-only programmatic API for workspace provenance - #41

Merged
takashi-matsuyama merged 1 commit into
mainfrom
feat/sdk-v0.2-read-api
Jun 4, 2026
Merged

takashi-matsuyama merged 1 commit into
mainfrom
feat/sdk-v0.2-read-api

Conversation

@takashi-matsuyama

Copy link
Copy Markdown
Member

Summary

@basou/sdk v0.2 — the package gains a runtime, read-only, semver-stable programmatic API for reading a Basou workspace's .basou/ provenance (it was types-only before). It is a thin, ergonomic facade over @basou/core's readers, so third-party tooling can read provenance without stitching low-level calls together and without any risk of mutating it (no writers are exposed). This is the keystone that unlocks third-party tooling and justifies the deferred schema-reference work.

API

  • openWorkspace(repoRoot, options?) => Promise<Workspace> — repoRoot is any directory holding a .basou/ and needs no git (normalized to an absolute path). Throws WorkspaceNotFoundError if .basou/ is missing/invalid.
  • resolveWorkspaceRoot(cwd) => Promise<string> — git-based convenience to find the root.
  • The Workspace handle: manifest(), status(), listSessions() / getSession(), readEvents() / streamEvents(), listTasks() / getTask(), listApprovals() / getApproval(), stats(options?), renderHandoff() / renderDecisions().
  • Session/task/event lookups accept a full id or a unique prefix (no match → null / empty stream; multiple → AmbiguousIdError). getApproval takes an exact id.
  • A now clock is injectable for deterministic reads. Read types (Session, Event, Task, WorkStatsResult, …) are re-exported so consumers import only from @basou/sdk. BASOU_SDK_VERSION is now 0.2.0.

Design (operator-confirmed)

Handle-based + bare-root open; full read facade; read-only (no writers); types re-exported from @basou/core (the SDK is the stability boundary on function signatures).

Robustness (from adversarial review)

  • listApprovals reports an id present in both pending/ and resolved/ once, under resolved (core loadApproval is resolved-first; matches the CLI's dedupe).
  • openWorkspace normalizes a relative root to an absolute path.
  • The id-resolver error mapping matches the core resolver's exact contract strings, not a loose substring, so an unrelated error containing "not found" is never swallowed to null.
  • The internal .basou/ layout (BasouPaths) is not exposed on the handle, keeping the stable surface narrow.

Compatibility

Adds a @basou/core dependency to the SDK. No @basou/core schema or CLI changes. This PR branches from main independently of #40.

Test plan

  • pnpm -r build && pnpm typecheck && pnpm test && pnpm lint all green (core 698 + sdk 17 + cli 414; lint clean apart from pre-existing unrelated warnings).
  • New SDK tests cover: open + WorkspaceNotFoundError (with cause), manifest/status, session list/get (full id, prefix, unknown→null, ambiguous→throw), events (eager + stream), tasks list/get, listApprovals dedupe of a resolved-and-stale-pending id, getApproval (resolved-first, exact id, unknown→null), stats, handoff/decisions rendering, relative-root normalization, and now injection.

🤖 Generated with Claude Code

`@basou/sdk` was a types-only package (a single version constant). This makes
it a runtime, read-only, semver-stable facade over `@basou/core`'s readers, so
third-party tooling can read a workspace's `.basou/` provenance without
stitching low-level core calls together and without any risk of mutating it
(no writers are exposed).

`openWorkspace(repoRoot, options?)` returns a `Workspace` handle. `repoRoot`
is any directory holding a `.basou/` and needs no git (it is normalized to an
absolute path); `resolveWorkspaceRoot(cwd)` is a git-based convenience for
finding the root. The handle wraps the core readers: `manifest()`, `status()`,
`listSessions()` / `getSession()`, `readEvents()` / `streamEvents()`,
`listTasks()` / `getTask()`, `listApprovals()` / `getApproval()`,
`stats(options?)`, and `renderHandoff()` / `renderDecisions()`.

Session, task, and event lookups accept a full id or a unique prefix: a prefix
that matches nothing yields null (or an empty stream), a prefix that matches
more than one record throws `AmbiguousIdError`. `getApproval` takes an exact
id. A missing or invalid `.basou/` throws `WorkspaceNotFoundError`. A `now`
clock is injectable for deterministic reads. Read types (Session, Event, Task,
WorkStatsResult, etc.) are re-exported so consumers import only from
`@basou/sdk`. `BASOU_SDK_VERSION` is now 0.2.0; the package gains a
`@basou/core` dependency.

Robustness (from adversarial review):
- `listApprovals` reports an id present in both pending/ and resolved/ once,
  under resolved, matching the CLI's dedupe (core `loadApproval` is
  resolved-first, so a stale pending file would otherwise double-report).
- `openWorkspace` normalizes a relative root to an absolute path so `root`
  honors its documented contract.
- The id-resolver error mapping matches the core resolver's exact contract
  strings rather than a loose substring, so an unrelated error containing
  "not found" is never swallowed to null.
- The internal `.basou/` layout (`BasouPaths`) is not exposed on the handle,
  keeping the stable surface narrow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@takashi-matsuyama
takashi-matsuyama force-pushed the feat/sdk-v0.2-read-api branch from ba6cf01 to 2d33e3e Compare June 4, 2026 07:44
@takashi-matsuyama
takashi-matsuyama merged commit 626ea29 into main Jun 4, 2026
7 checks passed
@takashi-matsuyama
takashi-matsuyama deleted the feat/sdk-v0.2-read-api branch June 4, 2026 08:08
takashi-matsuyama added a commit that referenced this pull request Jun 4, 2026
…s) (#43)

Promote the Unreleased section to 0.6.0 and bump all four package
manifests (core / cli / sdk / basou) to 0.6.0 in lockstep.

Highlights since 0.5.0:
- billing-grade work-measurement v2: Codex active time from real
  per-turn intervals, plus a machine_active_time_ms model-compute
  measure (#40).
- @basou/sdk v0.2: a runtime, read-only programmatic API over
  @basou/core's readers for reading .basou/ provenance (#41).
- @basou/core JSON Schema artifacts for the on-disk formats, generated
  from the Zod schemas and shipped under ./schemas/* (#42).

Also: document the four-package lockstep bump in CONTRIBUTING, and
record the 0.6.0 pre-publish dry-run tarball contents in the release
checklist (core now ships its schemas/, cli ships two entry points).

This commit only prepares the release; tagging v0.6.0 and the OIDC
publish remain maintainer actions.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant