feat: add distribution contract schemas (app, release, rollout_record, announcement, audience)#5
Conversation
…, announcement, audience)
- hasna.app.v1: canonical app identity (appId slug, npmName, repoFolder,
githubUrl, projectSlug, surfaces {bins, mcp?, http?}, lifecycle,
releaseChannel)
- hasna.release.v1: publish receipt with publishPath (skill|ci|backfilled),
deferred-legal changelogRef, and evidence required unless backfilled
- hasna.rollout_record.v1: per-machine rollout receipt with
install|update|rollback|freeze-blocked action/result coupling and
verifiedBy {cliVersion?, mcpHealth?}
- hasna.announcement.v1: campaign receipt with per-channel delivery status
and audienceRef
- hasna.audience.v1: tag/attribute/group predicate definition with consent
policy and suppressionSyncedAt
- resolve identity overlap: hasna.app_cloud_manifest.v1 now references
hasna.app.v1 by AppIdSchema appId and is documented as non-identity
- extend ResourceKindSchema with app/release/rollout/announcement/audience/
feedback kinds; register schemas, fixtures, and conformance cases
|
OpenLoops worker review for task Validated on PR head
Merge blockers found during adversarial review:
Residual reproducibility issue: after |
…/contracts 0.4.1) (#6) * feat(auth): stateless verifiable API-key auth kit HMAC-signed, self-describing keys (prefix hasna_<app>_) with a scope grammar (<app>:<action> + wildcards), TTL, and hashed-at-rest records. - keys.ts: mint/parse/verify signed tokens; sha256 at rest, secret shown once - scopes.ts: scope grammar + wildcard matcher - store.ts: DB-backed hashed-record store + revocation list (kit-agnostic client) - middleware.ts: Express/Hono-agnostic verifyApiKey() + per-request audit hook * feat(sdk): typed SDK generator from a serve OpenAPI document generateSdkFromOpenApi() emits a dependency-free fetch client + interfaces from components.schemas; sends the API key as x-api-key for self_hosted use. * feat(cli): issue-key issuer + bootstrap key Mints a scoped API key, persists only the hashed record to the app RDS, and prints the secret once. --bootstrap mints an <app>:* admin key. * chore(release): export auth+sdk, document usage, bump to 0.4.1 Wire ./auth and ./sdk package exports and build entrypoints (pinned bun --root src), clean dist before the release self-scan, and document the exact serve import path + env vars. Bump @hasna/contracts to 0.4.1.
…s/1a260112-fb07-433d-8381-0269374169fc-22aeea87
|
OpenLoops worker review for task |
What
Implements the contracts foundation of the distribution apps plan (Lane A): five new versioned Zod schemas alongside the existing 21 in
src/schemas.ts, following the existing schema/versioning/fixture/conformance patterns.hasna.app.v1(AppSchema): canonical app identity — stableappIdslug,npmName,repoFolder,githubUrl,projectSlug,surfaces { bins[], mcp?, http? },lifecycle (active|stub|deprecated|archived),releaseChannel (stable|beta|canary|internal).hasna.release.v1(ReleaseSchema):appId,package, semverversion,gitSha,publishedAt,publishPath (skill|ci|backfilled), OPTIONALchangelogRef(deferred refs are legal),evidenceRefs(required unless backfilled).hasna.rollout_record.v1(RolloutRecordSchema):appId,package,version,machine,action (install|update|rollback|freeze-blocked),result(contract status; freeze-blocked must be blocked/skipped, succeeded install/update requiresverifiedBy),verifiedBy { cliVersion?, mcpHealth? },at.hasna.announcement.v1(AnnouncementSchema):campaignId,appId?,releaseRef?(kindrelease),channels[]with per-channel delivery status,audienceRef(kindaudience),sentAt.hasna.audience.v1(AudienceSchema):audienceId,name,definition(tag/attribute/group predicates with all/any matching),consentPolicy,suppressionSyncedAt.hasna.app_cloud_manifest.v1 disposition
Resolved the competing-identity question:
hasna.app_cloud_manifest.v1is now explicitly NOT an identity schema. ItsappIdfield is tightened to the sharedAppIdSchemaslug and documented (schema comment + README) as a reference to the canonicalhasna.app.v1document.Also extends
ResourceKindSchemawithapp,release,rollout,announcement,audience,feedback(additive), registers the five schemas inContractSchemaRegistry/ type maps, and adds valid+invalid fixtures for each with expected issue paths.Verification
bun run typecheck: passbun test: 115 pass, 6 skip (pre-existing live-pg skips), 0 fail, 499 expect() calls across 10 filesbun run conformance: all fixtures ok (valid pass, invalid fail on expected paths)bun run build: pass (27 modules)Follow-ups
@hasna/eventsgains a typed distribution event catalog binding these schema ids (companion PR in hasna/events).hasna.feedback.v1contract is owned by a later lane; thefeedbackresource kind added here is the anchor for it.