Initial Commit
This commit is contained in:
73
openspec/archive/2026-05-25-api-manifest/proposal.md
Normal file
73
openspec/archive/2026-05-25-api-manifest/proposal.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# `@suaveplan/api-manifest` — Zod manifest schema, validators, dep + capability resolvers
|
||||
|
||||
**Package**: `@suaveplan/api-manifest`
|
||||
**Status**: PROPOSED
|
||||
**Type**: new-package
|
||||
**Stage**: 1 (Contracts) — see umbrella `openspec/changes/api-plugin-foundation/`
|
||||
|
||||
## Why
|
||||
|
||||
Every plugin in the API foundation ships a `plugin.json` (design §3). The host validates that manifest at install AND at every boot — a manifest that drifted between install and reload is a hard failure. Without a single Zod-validated source of truth for the manifest shape, every consumer (host loader, admin API, CLI scaffolder, OpenAPI emitter, `definePlugin` typings) would re-derive the schema and the surface would drift instantly. `api-manifest` is that source of truth: runtime Zod schemas + parsed types + the two graph resolvers (`dependsOn` → topological install order; `requiredCapabilities` / `optionalCapabilities` → capability resolution against the registered adapters).
|
||||
|
||||
Per design §3: "Manifest is the source of truth for declarative facts... The host validates and acts on these *before* loading the plugin's code."
|
||||
|
||||
## What changes
|
||||
|
||||
Net-new package with both runtime Zod schemas and graph algorithms. Subpath exports per concern.
|
||||
|
||||
- ADDED: `./schema` — Zod schemas for every top-level manifest key (`identity`, `compat`, `isolation`, `permissions`, `dependsOn`, `routes`, `graphql`, `websockets`, `sse`, `sagas`, `twoPhaseCommit`, `database`, `jobs`, `events`, `secrets`, `config`, `scopes`, `telemetry`, `openapi`, `errors`, `uninstall`) plus the composite `pluginManifestSchema`.
|
||||
- ADDED: `./parse` — `parseManifest(input: unknown): PluginManifest` (throws `MANIFEST.PARSE_FAILED` with Zod issues attached); `parseManifestSafe(input)` returning a discriminated result.
|
||||
- ADDED: `./validators` — cross-field validators that Zod alone can't express: `validateSagaOwnership` (every step plugin is listed in `dependsOn` or is self), `validatePermissionsWhenSandboxed` (sandboxed isolation requires `permissions.budgets`), `validateOpenApiRequiresGraphqlOrRoutes`, `validateErrorStatusMapShape` (every code matches `PLUGIN.<UPPER>.<UPPER>` and status is 4xx/5xx).
|
||||
- ADDED: `./dep-graph` — `resolveInstallOrder(manifests): readonly PluginManifest[]` (topological sort over `dependsOn`); detects cycles → `MANIFEST.DEPENDENCY_CYCLE`; detects missing deps → `MANIFEST.MISSING_DEPENDENCY`; detects version mismatch (semver) → `MANIFEST.DEPENDENCY_VERSION_MISMATCH`.
|
||||
- ADDED: `./capability-resolver` — `resolveCapabilities({ manifest, registered }): CapabilityResolution`; reports missing required capabilities (`MANIFEST.CAPABILITY_REQUIRED_MISSING`), missing optional capabilities (warning bucket, no error), version mismatches (`MANIFEST.CAPABILITY_VERSION_MISMATCH`).
|
||||
- ADDED: `./hash` — `hashManifest(manifest): string` returning `"sha256:<hex>"` over a canonical JSON form (sorted keys, stable separators) — used by `RollbackJournal` entries and the plugin ledger to detect drift across reload.
|
||||
- ADDED: `./error-codes` — frozen `MANIFEST_CODES` registry via `defineCodes`.
|
||||
- ADDED: `./testing` — `runManifestConformance(parse)` reusable harness for downstream consumers that want to verify parser equivalence.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Loading `plugin.json` from disk — that's `api-loader`. This package only parses an `unknown` input.
|
||||
- `definePlugin` runtime — lives in `api-host`. `api-manifest` only exports the *manifest* side of the contract.
|
||||
- Manifest migrations (v1 → v2) — forward-compat only at v1. Migrators will land when v2 ships.
|
||||
|
||||
## Source mapping
|
||||
|
||||
Genesis-original. Source of design intent: `docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md`.
|
||||
|
||||
| Requirement | Design doc section |
|
||||
|---|---|
|
||||
| Top-level manifest shape | §3 (full JSON example, lines 213-368) |
|
||||
| `isolation` enum + `permissions` budgets | §2.2, §3 (lines 229-242) |
|
||||
| `dependsOn` resolver semantics | §3, §4 (install order constraints) |
|
||||
| `requiredCapabilities` / `optionalCapabilities` | §3, §5.2 |
|
||||
| Sagas ownership validation | §3 ("sagas" block), §9.1 |
|
||||
| Errors `statusMap` shape | §3 ("errors" block), §12.1 |
|
||||
| Manifest hash for RollbackJournal | §8.1a (`manifestHash` field) |
|
||||
|
||||
### Requirements with no source line
|
||||
|
||||
| Requirement | Justification |
|
||||
|---|---|
|
||||
| `runManifestConformance` harness | Pattern established by `i18n-shared` conformance harnesses; downstream tooling (admin CLI, IDE plugin) needs a way to assert parser equivalence. |
|
||||
|
||||
## Verification approach
|
||||
|
||||
- `src/schema/*.test.ts` — golden manifest fixtures (minimal, maximal, every edge case from §3); `expectTypeOf` on inferred `PluginManifest` matching the type imported from `@suaveplan/api-types`.
|
||||
- `src/parse/parse.test.ts` — accept/reject suites; verifies Zod issues are surfaced verbatim on `PARSE_FAILED`.
|
||||
- `src/validators/*.test.ts` — every cross-field validator: accept path, every rejection branch with the right code.
|
||||
- `src/dep-graph/dep-graph.test.ts` — topological-order property tests (Fisher-Yates shuffled input → identical sorted output); cycle detection; missing-dep detection.
|
||||
- `src/capability-resolver/capability-resolver.test.ts` — required vs optional, version semver matching, missing capability surfacing.
|
||||
- `src/hash/hash.test.ts` — key-order independence; nested-object stability; sha256 KAT vector.
|
||||
- `tests/dep-graph.test.ts` — pins this package depends only on `@suaveplan/api-types`, `@suaveplan/error`, `zod`, and `@suaveplan/types` (logger contract for warnings).
|
||||
|
||||
## Migration impact
|
||||
|
||||
None — additive only. Nothing in the monorepo depends on `@suaveplan/api-manifest` yet. The umbrella's Stage-2 (`api-host`) is the first consumer.
|
||||
|
||||
## Impact
|
||||
|
||||
### Affected dependents
|
||||
|
||||
Run `bunx turbo ls --filter=...@suaveplan/api-manifest` to enumerate. No dependents at Stage 1 — every consumer lands in Stage 2 or later.
|
||||
|
||||
- (none) — [x] no action needed (reason: net-new package; no existing consumers in the genesis branch)
|
||||
@@ -0,0 +1,160 @@
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
# Capability Spec Delta: api-manifest (api-manifest)
|
||||
|
||||
**Package**: `@suaveplan/api-manifest`
|
||||
**Status**: PROPOSED
|
||||
|
||||
> Every requirement below carries a `**Verified by:**` line at authoring time. (See `openspec/AGENTS.md` Section 13.)
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Plugin manifest Zod schema covers every documented field
|
||||
|
||||
The package SHALL export `pluginManifestSchema: z.ZodSchema<PluginManifest>` covering every top-level manifest key documented in design §3: identity (`$schema`, `name`, `version`, `displayName`, `description`, `license`), compat (`apiHost`, `requiredCapabilities`, `optionalCapabilities`), `isolation`, `trustLevel`, `permissions`, `dependsOn`, `routes`, `graphql`, `websockets`, `sse`, `sagas`, `twoPhaseCommit`, `database`, `jobs`, `events`, `secrets`, `config`, `scopes`, `telemetry`, `openapi`, `errors`, `uninstall`. The inferred type MUST be structurally assignable to `PluginManifest` from `@suaveplan/api-types`. Unknown top-level keys MUST be rejected (`.strict()`).
|
||||
|
||||
#### Scenario: minimal manifest parses
|
||||
|
||||
- **WHEN** the smallest legal manifest (`{ name, version, apiHost }`) is parsed
|
||||
- **THEN** parsing succeeds AND optional fields are absent (NOT defaulted to empty objects)
|
||||
|
||||
#### Scenario: maximal manifest parses
|
||||
|
||||
- **WHEN** the full §3 example is parsed
|
||||
- **THEN** every documented field round-trips with the documented type
|
||||
|
||||
#### Scenario: unknown top-level key rejected
|
||||
|
||||
- **WHEN** `{ name, version, apiHost, badKey: 1 }` is parsed
|
||||
- **THEN** `parseManifest` throws `MANIFEST.PARSE_FAILED` AND the Zod issue path includes `badKey`
|
||||
|
||||
**Verified by:** `src/schema/plugin-manifest.test.ts::"minimal manifest parses"` AND `src/schema/plugin-manifest.test.ts::"maximal manifest round-trips every field"` AND `src/schema/plugin-manifest.spec-conformance.test.ts::"rejects unknown top-level keys"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: `import z from "zod"` everywhere
|
||||
|
||||
Every `.ts` file using Zod MUST use the default-import form (`import z from "zod"`). The named-import form (`import z from "zod"`) is forbidden — Bun's monorepo resolution treats the named form as unstable.
|
||||
|
||||
#### Scenario: no named import of `z`
|
||||
|
||||
- **WHEN** the test greps every `src/**/*.ts` for `import z`
|
||||
- **THEN** zero matches are returned
|
||||
|
||||
**Verified by:** `tests/zod-import-form.test.ts::"every src file uses default-import z"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: `parseManifest` and `parseManifestSafe`
|
||||
|
||||
The package SHALL export `parseManifest(input: unknown): PluginManifest` (throws `SuaveplanError` with code `MANIFEST.PARSE_FAILED` on failure) and `parseManifestSafe(input: unknown): { ok: true; data: PluginManifest } | { ok: false; error: SuaveplanError }`. The thrown / returned error MUST carry the raw Zod issues array under `cause.issues`.
|
||||
|
||||
#### Scenario: throw form on invalid input
|
||||
|
||||
- **WHEN** `parseManifest({})` is called
|
||||
- **THEN** it throws `SuaveplanError` with code `MANIFEST.PARSE_FAILED` AND `error.cause.issues` is a non-empty array
|
||||
|
||||
#### Scenario: safe form preserves the error envelope
|
||||
|
||||
- **WHEN** `parseManifestSafe({})` is called
|
||||
- **THEN** the result is `{ ok: false, error }` AND the error's code is `MANIFEST.PARSE_FAILED`
|
||||
|
||||
**Verified by:** `src/parse/parse.test.ts::"throw form on invalid input"` AND `src/parse/parse.test.ts::"safe form preserves error envelope"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Cross-field validators that Zod alone can't express
|
||||
|
||||
The package SHALL export `validateSagaOwnership`, `validatePermissionsWhenSandboxed`, `validateOpenApiRequiresGraphqlOrRoutes`, and `validateErrorStatusMapShape`. Each validator takes a parsed `PluginManifest` and returns `{ ok: true } | { ok: false; error: SuaveplanError }` with a stable code. `validateSagaOwnership` MUST reject sagas whose steps reference a `plugin` name that is not the manifest's own name and not declared in `dependsOn`. `validatePermissionsWhenSandboxed` MUST reject `isolation: "sandboxed"` manifests missing `permissions.budgets`.
|
||||
|
||||
#### Scenario: saga step referencing unknown plugin rejected
|
||||
|
||||
- **WHEN** a saga lists a step with `plugin: "@acme/unknown"` AND `dependsOn` does not contain `@acme/unknown`
|
||||
- **THEN** `validateSagaOwnership` returns `{ ok: false, error: { code: "MANIFEST.INVALID_SAGA_OWNERSHIP" } }`
|
||||
|
||||
#### Scenario: sandboxed isolation without budgets rejected
|
||||
|
||||
- **WHEN** `isolation: "sandboxed"` AND `permissions.budgets` is absent
|
||||
- **THEN** `validatePermissionsWhenSandboxed` returns `MANIFEST.SANDBOX_REQUIRES_BUDGETS`
|
||||
|
||||
**Verified by:** `src/validators/validate-saga-ownership.test.ts::"rejects step referencing undeclared dependency"` AND `src/validators/validate-permissions-when-sandboxed.test.ts::"sandboxed isolation requires permissions.budgets"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: `dependsOn` topological resolver
|
||||
|
||||
The package SHALL export `resolveInstallOrder(manifests: readonly PluginManifest[]): readonly PluginManifest[]` returning the manifests in dependency order (dependencies before dependents). Cycles MUST surface `MANIFEST.DEPENDENCY_CYCLE` with the cycle path attached. Missing dependencies (`dependsOn` references a name not in the set) MUST surface `MANIFEST.MISSING_DEPENDENCY`. Version-range mismatches (semver) MUST surface `MANIFEST.DEPENDENCY_VERSION_MISMATCH`.
|
||||
|
||||
#### Scenario: order is deterministic regardless of input order
|
||||
|
||||
- **WHEN** the same set of manifests is shuffled and resolved
|
||||
- **THEN** the output order is identical across every shuffle
|
||||
|
||||
#### Scenario: cycle reported with the cycle path
|
||||
|
||||
- **WHEN** A depends on B AND B depends on A
|
||||
- **THEN** `MANIFEST.DEPENDENCY_CYCLE` is thrown AND the error's `details.path` is `["A", "B", "A"]`
|
||||
|
||||
**Verified by:** `src/dep-graph/dep-graph.test.ts::"order is deterministic across shuffles"` AND `src/dep-graph/dep-graph.test.ts::"reports cycle path in details"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Capability resolver
|
||||
|
||||
The package SHALL export `resolveCapabilities({ manifest, registered }): CapabilityResolution`. `registered` is a `readonly CapabilityDescriptor[]` (each with `id`, `version`). Missing required capabilities MUST be reported as errors with code `MANIFEST.CAPABILITY_REQUIRED_MISSING` containing the missing id list. Missing optional capabilities MUST be reported as warnings (resolution still succeeds). Version mismatches between manifest's `apiHost` semver and the registered descriptor's version MUST surface `MANIFEST.CAPABILITY_VERSION_MISMATCH`.
|
||||
|
||||
#### Scenario: missing required capability blocks resolution
|
||||
|
||||
- **WHEN** `manifest.requiredCapabilities` is `["db"]` AND `registered` is `[]`
|
||||
- **THEN** resolution returns `{ ok: false, error: { code: "MANIFEST.CAPABILITY_REQUIRED_MISSING", details: { missing: ["db"] } } }`
|
||||
|
||||
#### Scenario: missing optional capability surfaces as warning
|
||||
|
||||
- **WHEN** `manifest.optionalCapabilities` is `["secrets.bun"]` AND `registered` lacks it
|
||||
- **THEN** resolution returns `{ ok: true, warnings: [{ code: "MANIFEST.OPTIONAL_CAPABILITY_MISSING", details: { id: "secrets.bun" } }] }`
|
||||
|
||||
**Verified by:** `src/capability-resolver/capability-resolver.test.ts::"missing required capability blocks resolution"` AND `src/capability-resolver/capability-resolver.test.ts::"missing optional surfaces as warning"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Canonical manifest hash
|
||||
|
||||
The package SHALL export `hashManifest(manifest: PluginManifest): string` returning `"sha256:<hex>"`. The hash MUST be computed over a canonical-JSON encoding: keys sorted lexicographically at every depth, no whitespace, no trailing newline. Two manifests differing only in key insertion order MUST hash identically. Two manifests differing in any value (including nested arrays' order if the array is semantically ordered) MUST hash differently.
|
||||
|
||||
#### Scenario: key-order independence
|
||||
|
||||
- **WHEN** two manifests with identical content but different top-level key order are hashed
|
||||
- **THEN** the hashes are equal
|
||||
|
||||
#### Scenario: KAT vector
|
||||
|
||||
- **WHEN** the minimal manifest `{ name: "x", version: "0.1.0", apiHost: "^1" }` is hashed
|
||||
- **THEN** the output equals a published vector pinned in the test (so this never silently drifts)
|
||||
|
||||
**Verified by:** `src/hash/hash.test.ts::"key-order independent"` AND `src/hash/hash.test.ts::"known-answer vector for minimal manifest"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Error codes registry frozen
|
||||
|
||||
The package SHALL export `MANIFEST_CODES` via `defineCodes` (from `@suaveplan/error`) containing exactly: `PARSE_FAILED`, `DEPENDENCY_CYCLE`, `MISSING_DEPENDENCY`, `DEPENDENCY_VERSION_MISMATCH`, `CAPABILITY_REQUIRED_MISSING`, `CAPABILITY_VERSION_MISMATCH`, `OPTIONAL_CAPABILITY_MISSING`, `INVALID_SAGA_OWNERSHIP`, `SANDBOX_REQUIRES_BUDGETS`, `INVALID_ERROR_STATUS_MAP`. Every code MUST be thrown (or attached to a warning) by at least one production code path.
|
||||
|
||||
#### Scenario: every code is reachable
|
||||
|
||||
- **WHEN** the test enumerates `MANIFEST_CODES` and greps each into the production source
|
||||
- **THEN** every code has at least one production-path reference outside `error-codes.ts` itself
|
||||
|
||||
**Verified by:** `src/error-codes/error-codes.spec-conformance.test.ts::"every MANIFEST_CODES entry is thrown by a production path"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Subpath exports per concern
|
||||
|
||||
Every category (`schema`, `parse`, `validators`, `dep-graph`, `capability-resolver`, `hash`, `error-codes`, `testing`) SHALL be reachable via its own subpath export. The root barrel MUST use explicit named re-exports (rule 10) — no `export *`.
|
||||
|
||||
#### Scenario: every documented subpath resolves
|
||||
|
||||
- **WHEN** the test imports each subpath
|
||||
- **THEN** every import resolves at type-check + runtime
|
||||
|
||||
**Verified by:** `tests/subpaths.test.ts::"every documented subpath is importable"` AND `tests/dep-graph.test.ts::"root barrel has no export-star"`
|
||||
71
openspec/archive/2026-05-25-api-manifest/tasks.md
Normal file
71
openspec/archive/2026-05-25-api-manifest/tasks.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Tasks — `@suaveplan/api-manifest`
|
||||
|
||||
> Every checkbox MUST reflect work actually done in this change.
|
||||
|
||||
## 0. Pre-flight (non-negotiable)
|
||||
|
||||
- [x] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each)
|
||||
- [x] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
|
||||
- [x] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest.
|
||||
|
||||
## 1. Spec authoring
|
||||
|
||||
- [x] 1.1 Read design doc §3 (manifest schema), §4 (lifecycle reinstall criteria), §5.2 (capability resolution), §8.1a (manifest hash)
|
||||
- [x] 1.2 Read `openspec/specs/api-manifest/spec.md` (does not exist yet — net-new)
|
||||
- [x] 1.3 Read `openspec/AGENTS.md` Sections 13 and 14
|
||||
- [x] 1.4 Draft `proposal.md` from `openspec/templates/proposal.md`
|
||||
- [x] 1.5 Source-mapping table — every requirement cites a design-doc section
|
||||
- [x] 1.6 Draft `specs/api-manifest/spec.md` from `openspec/templates/spec-delta.md`
|
||||
- [x] 1.7 Every requirement carries a `**Verified by:**` line naming concrete `it(...)` titles
|
||||
|
||||
## 2. Implementation
|
||||
|
||||
- [x] 2.1 Scaffold `packages/api/api-manifest/` (`"version": "0.1.0"`, `"sideEffects": false`, deps: `@suaveplan/api-types` workspace, `@suaveplan/error` workspace, `zod` catalog, `@suaveplan/types` workspace)
|
||||
- [x] 2.2 `src/schema/` — one file per top-level manifest key (`identity.ts`, `compat.ts`, `isolation.ts`, `permissions.ts`, `dependsOn.ts`, `routes.ts`, `graphql.ts`, `websockets.ts`, `sse.ts`, `sagas.ts`, `twoPhaseCommit.ts`, `database.ts`, `jobs.ts`, `events.ts`, `secrets.ts`, `config.ts`, `scopes.ts`, `telemetry.ts`, `openapi.ts`, `errors.ts`, `uninstall.ts`); composite `plugin-manifest.ts` joining them. Always `import z from "zod"`.
|
||||
- [x] 2.3 `src/parse/` — `parseManifest`, `parseManifestSafe`
|
||||
- [x] 2.4 `src/validators/` — `validate-saga-ownership`, `validate-permissions-when-sandboxed`, `validate-openapi-requires-graphql-or-routes`, `validate-error-status-map-shape`
|
||||
- [x] 2.5 `src/dep-graph/` — Kahn's-algorithm topological sort with cycle detection; semver-range matcher
|
||||
- [x] 2.6 `src/capability-resolver/` — required vs optional resolution against a `CapabilityDescriptor[]` source
|
||||
- [x] 2.7 `src/hash/` — canonical-JSON serializer + sha256 (from `@suaveplan/crypto-hash`)
|
||||
- [x] 2.8 `src/error-codes/` — `MANIFEST_CODES` registry: `PARSE_FAILED`, `DEPENDENCY_CYCLE`, `MISSING_DEPENDENCY`, `DEPENDENCY_VERSION_MISMATCH`, `CAPABILITY_REQUIRED_MISSING`, `CAPABILITY_VERSION_MISMATCH`, `INVALID_SAGA_OWNERSHIP`, `SANDBOX_REQUIRES_BUDGETS`, `INVALID_ERROR_STATUS_MAP`
|
||||
- [x] 2.9 `src/testing/` — `runManifestConformance(parse)` harness
|
||||
- [x] 2.10 `src/index.ts` — explicit named re-exports (rule 10); namespace re-exports per subpath
|
||||
- [x] 2.11 `package.json` `exports` — one subpath per category
|
||||
|
||||
## 3. Tests
|
||||
|
||||
- [x] 3.1 Schema unit tests per feature (`<feature>.test.ts`) — golden fixtures
|
||||
- [x] 3.2 Spec-conformance tests (`<feature>.spec-conformance.test.ts`) — type-level + code-registry pinning
|
||||
- [x] 3.3 Property tests (`fast-check`) on `dep-graph` topological sort + `hash` key-order stability
|
||||
- [x] 3.4 Every `it(...)` carries `{ timeout: 5000 }`
|
||||
|
||||
## 4. Documentation
|
||||
|
||||
- [x] 4.1 Sibling `.md` for every implementation `.ts` (≥200 words, basic + advanced)
|
||||
- [x] 4.2 `README.md` at root linking every co-located `.md` under `## Modules`
|
||||
- [x] 4.3 No CLAUDE.md update needed (covered by umbrella entry)
|
||||
|
||||
## 5. Pre-archive verification (the 5-gate triple)
|
||||
|
||||
- [x] 5.1 `bun run check` — Biome 0/0/0
|
||||
- [x] 5.2 `bunx turbo lint typecheck test --filter=@suaveplan/api-manifest` — green
|
||||
- [x] 5.3 `bunx turbo test:coverage --filter=@suaveplan/api-manifest` — 100/100/100/100
|
||||
- [x] 5.4 `bun scripts/openspec-conformance.ts @suaveplan/api-manifest` — exit 0
|
||||
- [x] 5.5 `bunx turbo build --filter=@suaveplan/api-manifest` — green
|
||||
|
||||
## 6. Pre-archive 7-row checklist (AGENTS.md Section 13)
|
||||
|
||||
- [x] 6.1 Generics on `parseManifest<T extends PluginManifest>`; `expectTypeOf` on inferred Zod outputs vs `api-types` shape
|
||||
- [x] 6.2 Zod schemas wired at the public boundary (`parseManifest`); every input goes through Zod
|
||||
- [x] 6.3 Every `MANIFEST_CODES` entry thrown by a production path with a test asserting it
|
||||
- [x] 6.4 Strict-mode behavior — Zod `.strict()` rejecting unknown top-level keys is tested both with strict on and off (per-field override)
|
||||
- [x] 6.5 OTel instruments — N/A (this is a pure parser; instrumentation lives in `api-host` callers)
|
||||
- [x] 6.6 Lifecycle dedup — N/A (no async lifecycle in this package)
|
||||
- [x] 6.7 No retroactive tick
|
||||
|
||||
## 7. Archive
|
||||
|
||||
- [x] 7.1 Fold spec delta into `openspec/specs/api-manifest/spec.md`
|
||||
- [x] 7.2 Move `openspec/changes/api-manifest/` → `openspec/archive/<yyyy-mm-dd>-api-manifest/`
|
||||
- [x] 7.3 Tick `1.2` in umbrella `openspec/changes/api-plugin-foundation/tasks.md`
|
||||
- [x] 7.4 Refresh `MANIFEST.md`
|
||||
Reference in New Issue
Block a user