@hasna/contracts 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -124,6 +124,12 @@ defaults are applied; output aliases such as `EvidenceRef` describe parsed data.
124
124
  - `hasna.validation_plan.v1`: deterministic checks a package or agent should run.
125
125
  - `hasna.proof_bundle.v1`: reviewable validation result that ties a subject to
126
126
  checks, evidence, verifier, and verdict.
127
+ - `hasna.scaffold_manifest.v1`: public, portable description of a scaffold's
128
+ type, status, capabilities, output shape, env vars, scripts, and validation
129
+ checks.
130
+ - `hasna.scaffold_install_record.v1`: portable receipt for a scaffold install
131
+ against a target repo or project, including installer, status, generated
132
+ resource refs, evidence, and proof refs.
127
133
 
128
134
  Every top-level contract includes a literal `schema` field. Consumers should
129
135
  reject objects whose embedded schema does not match the validator being used.
@@ -143,6 +149,7 @@ Common resource-kind mappings:
143
149
  | `open-todos` | `task`, `project`, `verification`, `proof_bundle` |
144
150
  | `open-loops` | `loop`, `workflow`, `run`, `artifact` |
145
151
  | `open-actions` | `action`, `tool`, `event`; decisions are emitted as `DecisionEnvelope` contracts, not resource kinds |
152
+ | `open-automations` | `action`, `tool`, `event`; deterministic recipe decisions are emitted as `DecisionEnvelope` contracts |
146
153
  | `open-sessions` | `session`, `run`, `machine`, `artifact` |
147
154
  | `open-context` | `context_pack`, `file`, `url`, `knowledge` |
148
155
  | `open-knowledge` / `open-mementos` | `knowledge`, `memento`, `context_pack` |
@@ -158,16 +165,28 @@ helpers. Owning packages still own storage and behavior.
158
165
 
159
166
  - `open-todos` owns tasks, task plans, locks, comments, and task evidence.
160
167
  - `open-loops` owns loop and workflow execution.
168
+ - `open-events` owns event envelopes, channels, delivery, replay, and
169
+ notification semantics.
161
170
  - `open-actions` owns executable action manifests.
171
+ - `open-automations` owns deterministic product/app automations and
172
+ connector/action recipes. It does not own agent workflow invocation,
173
+ admission queues, task/PR/review worker routing, or canonical workflow run
174
+ artifacts.
162
175
  - `open-sessions` owns transcript and trajectory ingestion.
163
176
  - `open-context` owns context-pack construction and retrieval.
164
- - `open-knowledge` owns durable knowledge records and promotion workflows.
177
+ - `open-knowledge` owns durable knowledge records and promotion workflows under
178
+ `.hasna/knowledge`.
165
179
  - `open-files` owns artifact storage, file indexing, and dereference logic.
166
180
  - `open-mementos` owns memory lifecycle and recall.
167
181
  - `open-reports` owns rendered reports and proof presentation.
168
182
  - `open-evals` owns evaluation execution and scored validation results.
169
183
  - `open-economy` owns budget, cost, and usage policy decisions.
170
184
  - `open-monitor` owns fleet health classification and alerting.
185
+ - `iapp-scaffolds` owns scaffold templates, registry behavior, install/setup
186
+ behavior, MCP tools, CLI UX, and private/internal scaffold metadata. It should
187
+ validate public scaffold manifests and install records with
188
+ `@hasna/contracts`, but `@hasna/contracts` must not import or execute
189
+ `iapp-scaffolds`.
171
190
 
172
191
  ## Downstream Integration Recipes
173
192
 
@@ -179,10 +198,16 @@ native domain objects immediately.
179
198
  records, versions, signed URLs, source manifests, and evidence assets.
180
199
  - `open-todos`: expose task refs as `ResourceRef`; verification evidence as
181
200
  `ProofBundle`; task execution receipts as `WorkRun`; review gates as
182
- `ValidationPlan`.
201
+ `ValidationPlan`; and workflow/run manifest pointers as compact task fields,
202
+ not embedded handoff artifacts.
183
203
  - `open-loops`: emit loop/workflow runs as `WorkRun`, audit traces as
184
204
  `AgentTrajectory`, logs/artifacts as `EvidenceRef`, and verifier output as
185
- `ProofBundle`.
205
+ `ProofBundle`. OpenLoops owns `WorkflowInvocation`, admission/work-item
206
+ queues, leases, workflow runs, retries, cancellation, worktrees, and run
207
+ artifacts.
208
+ - `open-events`: emit and replay validated event envelopes to channels.
209
+ OpenEvents delivers notifications only; it does not create workflow
210
+ invocations, own queue state, or retry agent work.
186
211
  - `open-sessions`: convert messages/tool calls to `AgentTrajectory`, token
187
212
  usage to `CostEstimate`, and transcript paths to `EvidenceRef`.
188
213
  - `open-context`: serialize built context as `ContextPack` with citations as
@@ -202,10 +227,95 @@ native domain objects immediately.
202
227
  - `open-actions`: keep domain action manifests, but expose shared `ActorRef`,
203
228
  `EvidenceRef`, `CapabilityCard`, `DecisionEnvelope`, and `WorkRun` adapter
204
229
  views.
230
+ - `iapp-scaffolds`: emit `ScaffoldManifest` documents for bundled templates,
231
+ write schema-tagged `ScaffoldInstallRecord` receipts for installs, and keep
232
+ template copying, setup wizards, source paths, and private metadata inside the
233
+ scaffold package.
234
+ - `open-automations`: keep deterministic app/product automation recipes and
235
+ connector/action recipes. Any agentic task, PR, review, or evaluation flow
236
+ must hand off to OpenLoops rather than creating a second workflow queue.
205
237
  - `open-reports`: consume `ProofBundle`, `WorkRun`, `ContextPack`,
206
238
  `CostEstimate`, and `EvidenceRef` to render compact Markdown/JSON/HTML proof
207
239
  reports.
208
240
 
241
+ ## WorkflowInvocation And App Storage Boundary
242
+
243
+ The canonical agent-work root is a `WorkflowInvocation`, not a todo task.
244
+ Only actionable unfinished work needs a todo. OpenTodos remains the
245
+ human-visible intent ledger; OpenLoops owns the durable workflow root,
246
+ admission queue, execution lifecycle, and canonical run artifacts.
247
+
248
+ A workflow invocation should carry these fields at the boundary:
249
+
250
+ - `id`
251
+ - `templateId` or `workflowId`
252
+ - `sourceRef`: a WorkflowInvocation-local source kind such as `task`, `event`,
253
+ `schedule`, `manual`, `pull_request`, `review`, or `knowledge`, plus an id
254
+ and dedupe key
255
+ - `subjectRef`: a WorkflowInvocation-local subject kind such as `repo`,
256
+ `pull_request`, `task`, `document`, `run`, or `metric`, plus a path, URL, or
257
+ id
258
+ - `intent`: `route`, `mutate`, `review`, `evaluate`, or `report`
259
+ - `scope`: project path, worktree policy, permissions, account policy, and
260
+ concurrency group
261
+ - `outputPolicy`: when to write reports and when to create a follow-up task
262
+
263
+ OpenLoops admission/work items are first-class records with route key,
264
+ idempotency key, source/subject refs, project key/group, priority, status,
265
+ attempts, next-attempt time, lease expiry, loop/workflow/run ids, and last
266
+ reason. Status values should be explicit: `queued`, `deferred`, `admitted`,
267
+ `running`, `succeeded`, `failed`, `dead_letter`, or `cancelled`.
268
+
269
+ Run artifacts live under:
270
+
271
+ ```text
272
+ .hasna/loops/runs/<project-slug>/<subject-key>/<run-id>/manifest.json
273
+ .hasna/loops/runs/<project-slug>/<subject-key>/<run-id>/triage.md
274
+ .hasna/loops/runs/<project-slug>/<subject-key>/<run-id>/plan.md
275
+ .hasna/loops/runs/<project-slug>/<subject-key>/<run-id>/worker-report.md
276
+ .hasna/loops/runs/<project-slug>/<subject-key>/<run-id>/evaluation.md
277
+ .hasna/loops/runs/<project-slug>/<subject-key>/<run-id>/evidence/
278
+ ```
279
+
280
+ The `<subject-key>` is never the raw subject reference. It must be a safe path
281
+ segment derived as `kind-safeSlug-shortHash`. Recommended normalization:
282
+ lowercase ASCII, replace non-alphanumeric runs with `-`, trim separators, cap
283
+ the slug portion at 72 characters, and append at least 12 hex characters from a
284
+ SHA-256 hash of the canonical raw `subjectRef`. Reject `.`/`..`, reserved device
285
+ names, path separators, empty keys, and path traversal. Store the raw
286
+ `subjectRef` only inside `manifest.json`.
287
+
288
+ OpenEvents webhooks/channels are notifications. A `task.created` notification
289
+ can be delivered through OpenEvents, but OpenLoops consumes the envelope and
290
+ upserts/admits work items. OpenEvents must not import OpenLoops or own
291
+ admission, retries, leases, verifier execution, or workflow run artifacts.
292
+
293
+ Every Hasna app stores local state under `.hasna/<app>/...`. The obsolete
294
+ `.hasna/apps/<app>` layout is not an operational read path. OpenKnowledge's
295
+ canonical storage is `.hasna/knowledge`, not `.hasna/apps/knowledge`.
296
+
297
+ No-backcompat migrations must preserve data without keeping legacy shims:
298
+
299
+ 1. Create a read-only backup or export before moving data.
300
+ 2. Atomically copy or rename into the canonical `.hasna/<app>` path.
301
+ 3. Verify JSON item counts, SQLite integrity and table counts, artifact counts,
302
+ hashes, and any storage-object or sync-snapshot evidence.
303
+ 4. Leave only a diagnostic tombstone at the old path.
304
+ 5. Treat mismatched counts, hash failures, or SQLite integrity failures as
305
+ blockers.
306
+
307
+ Unattended automatic routes must fail closed when the configured sandbox cannot
308
+ be proven. Acceptable sandbox evidence includes a successful preflight receipt
309
+ that names the isolation provider, filesystem/network policy, writable roots,
310
+ tool allowlist, environment redaction result, and timestamp for the exact route
311
+ or run. `danger-full-access` plus a worktree is a manual break-glass mode, not a
312
+ safe auto-route default.
313
+
314
+ `WorkflowInvocation` is documented here as the architecture boundary used by
315
+ OpenLoops and neighboring packages. It is not yet a wire schema in the current
316
+ catalog; add a `hasna.workflow_invocation.v1` schema only when at least two
317
+ packages need to validate the object directly at a shared boundary.
318
+
209
319
  ## Enforcement Model
210
320
 
211
321
  Validate at every boundary where another package, agent, process, or machine can
package/dist/cli/index.js CHANGED
@@ -992,7 +992,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
992
992
  this._exitCallback = (err) => {
993
993
  if (err.code !== "commander.executeSubCommandAsync") {
994
994
  throw err;
995
- }
995
+ } else {}
996
996
  };
997
997
  }
998
998
  return this;
@@ -6074,7 +6074,7 @@ var coerce = {
6074
6074
  };
6075
6075
  var NEVER = INVALID;
6076
6076
  // src/schemas.ts
6077
- var CONTRACTS_PACKAGE_VERSION = "0.1.0";
6077
+ var CONTRACTS_PACKAGE_VERSION = "0.2.0";
6078
6078
  var SCHEMA_IDS = {
6079
6079
  actorRef: "hasna.actor_ref.v1",
6080
6080
  resourceRef: "hasna.resource_ref.v1",
@@ -6086,7 +6086,9 @@ var SCHEMA_IDS = {
6086
6086
  contextPack: "hasna.context_pack.v1",
6087
6087
  agentTrajectory: "hasna.agent_trajectory.v1",
6088
6088
  validationPlan: "hasna.validation_plan.v1",
6089
- proofBundle: "hasna.proof_bundle.v1"
6089
+ proofBundle: "hasna.proof_bundle.v1",
6090
+ scaffoldManifest: "hasna.scaffold_manifest.v1",
6091
+ scaffoldInstallRecord: "hasna.scaffold_install_record.v1"
6090
6092
  };
6091
6093
  var SchemaIdSchema = exports_external.string().regex(/^hasna\.[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*\.v[0-9]+$/);
6092
6094
  var TimestampSchema = exports_external.string().datetime();
@@ -6390,6 +6392,144 @@ var ValidationPlanSchema = contractBaseSchema(SCHEMA_IDS.validationPlan).extend(
6390
6392
  verifier: ActorPointerSchema.optional(),
6391
6393
  requiredEvidenceKinds: exports_external.array(EvidenceKindSchema).default([])
6392
6394
  }).strict();
6395
+ var ScaffoldTypeSchema = exports_external.enum([
6396
+ "open_source",
6397
+ "internal_app",
6398
+ "platform",
6399
+ "app",
6400
+ "agent",
6401
+ "content",
6402
+ "overlay",
6403
+ "other"
6404
+ ]);
6405
+ var ScaffoldStatusSchema = exports_external.enum(["draft", "active", "deprecated", "archived"]);
6406
+ var ScaffoldCapabilitySchema = exports_external.enum([
6407
+ "cli",
6408
+ "mcp",
6409
+ "library",
6410
+ "sdk",
6411
+ "rest_api",
6412
+ "dashboard",
6413
+ "database",
6414
+ "auth",
6415
+ "billing",
6416
+ "worker",
6417
+ "daemon",
6418
+ "native",
6419
+ "browser_extension",
6420
+ "ai_provider",
6421
+ "media_pipeline",
6422
+ "data_pipeline",
6423
+ "tests",
6424
+ "ci",
6425
+ "deployment",
6426
+ "docs",
6427
+ "other"
6428
+ ]);
6429
+ var ScaffoldEnvVarSchema = exports_external.object({
6430
+ key: exports_external.string().regex(/^[A-Z][A-Z0-9_]*$/),
6431
+ description: exports_external.string().min(1),
6432
+ required: exports_external.boolean().default(false),
6433
+ secret: exports_external.boolean().default(false),
6434
+ group: exports_external.string().min(1).optional(),
6435
+ default: exports_external.string().optional()
6436
+ }).strict().superRefine((value, ctx) => {
6437
+ if (value.secret && value.default !== undefined) {
6438
+ ctx.addIssue({
6439
+ code: exports_external.ZodIssueCode.custom,
6440
+ message: "Secret scaffold env vars cannot include defaults",
6441
+ path: ["default"]
6442
+ });
6443
+ }
6444
+ });
6445
+ var ScaffoldScriptSchema = exports_external.object({
6446
+ name: exports_external.string().min(1),
6447
+ command: exports_external.string().min(1),
6448
+ description: exports_external.string().min(1).optional(),
6449
+ required: exports_external.boolean().default(false)
6450
+ }).strict();
6451
+ var ScaffoldOutputShapeSchema = exports_external.object({
6452
+ packageManager: exports_external.enum(["bun", "npm", "pnpm", "yarn", "cargo", "pip", "other"]).optional(),
6453
+ languages: exports_external.array(exports_external.string().min(1)).default([]),
6454
+ requiredFiles: exports_external.array(exports_external.string().min(1)).default([]),
6455
+ requiredDirectories: exports_external.array(exports_external.string().min(1)).default([]),
6456
+ optionalDirectories: exports_external.array(exports_external.string().min(1)).default([])
6457
+ }).strict();
6458
+ var ScaffoldManifestSchema = contractBaseSchema(SCHEMA_IDS.scaffoldManifest).extend({
6459
+ name: exports_external.string().min(1),
6460
+ version: exports_external.string().min(1),
6461
+ summary: exports_external.string().min(1),
6462
+ type: ScaffoldTypeSchema,
6463
+ status: ScaffoldStatusSchema.default("draft"),
6464
+ capabilities: exports_external.array(ScaffoldCapabilitySchema).default([]),
6465
+ techStack: exports_external.array(exports_external.string().min(1)).default([]),
6466
+ tags: TagsSchema,
6467
+ source: ResourcePointerSchema.optional(),
6468
+ output: ScaffoldOutputShapeSchema,
6469
+ env: exports_external.array(ScaffoldEnvVarSchema).default([]),
6470
+ scripts: exports_external.array(ScaffoldScriptSchema).default([]),
6471
+ validationChecks: exports_external.array(ValidationCheckSchema).default([]),
6472
+ evidenceRefs: exports_external.array(EvidencePointerSchema).default([])
6473
+ }).strict().superRefine((value, ctx) => {
6474
+ if (value.source?.uri?.startsWith("file://")) {
6475
+ ctx.addIssue({
6476
+ code: exports_external.ZodIssueCode.custom,
6477
+ message: "Public scaffold manifest source refs cannot use local file:// URIs",
6478
+ path: ["source", "uri"]
6479
+ });
6480
+ }
6481
+ if (value.status === "active" && value.validationChecks.length === 0) {
6482
+ ctx.addIssue({
6483
+ code: exports_external.ZodIssueCode.custom,
6484
+ message: "Active scaffold manifests require validation checks",
6485
+ path: ["validationChecks"]
6486
+ });
6487
+ }
6488
+ if (value.status === "active" && value.output.requiredFiles.length === 0 && value.output.requiredDirectories.length === 0) {
6489
+ ctx.addIssue({
6490
+ code: exports_external.ZodIssueCode.custom,
6491
+ message: "Active scaffold manifests require at least one required file or directory",
6492
+ path: ["output"]
6493
+ });
6494
+ }
6495
+ });
6496
+ var ScaffoldInstallStatusSchema = exports_external.enum(["installed", "failed", "cancelled", "partial", "unknown"]);
6497
+ var ScaffoldInstallRecordSchema = contractBaseSchema(SCHEMA_IDS.scaffoldInstallRecord).extend({
6498
+ scaffoldId: exports_external.string().min(1),
6499
+ scaffoldVersion: exports_external.string().min(1).optional(),
6500
+ manifestRef: ResourcePointerSchema.optional(),
6501
+ target: ResourcePointerSchema,
6502
+ status: ScaffoldInstallStatusSchema,
6503
+ installedAt: TimestampSchema.optional(),
6504
+ installer: ActorPointerSchema.optional(),
6505
+ packageManager: exports_external.enum(["bun", "npm", "pnpm", "yarn", "cargo", "pip", "other"]).optional(),
6506
+ options: MetadataSchema.optional(),
6507
+ generatedFiles: exports_external.array(ResourcePointerSchema).default([]),
6508
+ evidenceRefs: exports_external.array(EvidencePointerSchema).default([]),
6509
+ proofBundleRefs: exports_external.array(ResourcePointerSchema).default([])
6510
+ }).strict().superRefine((value, ctx) => {
6511
+ if (value.status === "installed" && !value.installedAt) {
6512
+ ctx.addIssue({
6513
+ code: exports_external.ZodIssueCode.custom,
6514
+ message: "Installed scaffold records require installedAt",
6515
+ path: ["installedAt"]
6516
+ });
6517
+ }
6518
+ if (value.status === "installed" && value.generatedFiles.length === 0 && value.evidenceRefs.length === 0 && value.proofBundleRefs.length === 0) {
6519
+ ctx.addIssue({
6520
+ code: exports_external.ZodIssueCode.custom,
6521
+ message: "Installed scaffold records require generated files, evidence, or proof bundle refs",
6522
+ path: ["generatedFiles"]
6523
+ });
6524
+ }
6525
+ if ((value.status === "failed" || value.status === "partial") && value.evidenceRefs.length === 0 && value.proofBundleRefs.length === 0) {
6526
+ ctx.addIssue({
6527
+ code: exports_external.ZodIssueCode.custom,
6528
+ message: "Failed or partial scaffold records require evidence or proof bundle refs",
6529
+ path: ["evidenceRefs"]
6530
+ });
6531
+ }
6532
+ });
6393
6533
  var ProofCheckResultSchema = exports_external.object({
6394
6534
  checkId: exports_external.string().min(1),
6395
6535
  status: ContractStatusSchema,
@@ -6536,7 +6676,9 @@ var ContractSchemaRegistry = {
6536
6676
  [SCHEMA_IDS.contextPack]: ContextPackSchema,
6537
6677
  [SCHEMA_IDS.agentTrajectory]: AgentTrajectorySchema,
6538
6678
  [SCHEMA_IDS.validationPlan]: ValidationPlanSchema,
6539
- [SCHEMA_IDS.proofBundle]: ProofBundleSchema
6679
+ [SCHEMA_IDS.proofBundle]: ProofBundleSchema,
6680
+ [SCHEMA_IDS.scaffoldManifest]: ScaffoldManifestSchema,
6681
+ [SCHEMA_IDS.scaffoldInstallRecord]: ScaffoldInstallRecordSchema
6540
6682
  };
6541
6683
 
6542
6684
  // src/validators.ts
package/dist/index.js CHANGED
@@ -3989,7 +3989,7 @@ var coerce = {
3989
3989
  var NEVER = INVALID;
3990
3990
  // src/schemas.ts
3991
3991
  var CONTRACTS_PACKAGE_NAME = "@hasna/contracts";
3992
- var CONTRACTS_PACKAGE_VERSION = "0.1.0";
3992
+ var CONTRACTS_PACKAGE_VERSION = "0.2.0";
3993
3993
  var SCHEMA_IDS = {
3994
3994
  actorRef: "hasna.actor_ref.v1",
3995
3995
  resourceRef: "hasna.resource_ref.v1",
@@ -4001,7 +4001,9 @@ var SCHEMA_IDS = {
4001
4001
  contextPack: "hasna.context_pack.v1",
4002
4002
  agentTrajectory: "hasna.agent_trajectory.v1",
4003
4003
  validationPlan: "hasna.validation_plan.v1",
4004
- proofBundle: "hasna.proof_bundle.v1"
4004
+ proofBundle: "hasna.proof_bundle.v1",
4005
+ scaffoldManifest: "hasna.scaffold_manifest.v1",
4006
+ scaffoldInstallRecord: "hasna.scaffold_install_record.v1"
4005
4007
  };
4006
4008
  var SchemaIdSchema = exports_external.string().regex(/^hasna\.[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*\.v[0-9]+$/);
4007
4009
  var TimestampSchema = exports_external.string().datetime();
@@ -4305,6 +4307,144 @@ var ValidationPlanSchema = contractBaseSchema(SCHEMA_IDS.validationPlan).extend(
4305
4307
  verifier: ActorPointerSchema.optional(),
4306
4308
  requiredEvidenceKinds: exports_external.array(EvidenceKindSchema).default([])
4307
4309
  }).strict();
4310
+ var ScaffoldTypeSchema = exports_external.enum([
4311
+ "open_source",
4312
+ "internal_app",
4313
+ "platform",
4314
+ "app",
4315
+ "agent",
4316
+ "content",
4317
+ "overlay",
4318
+ "other"
4319
+ ]);
4320
+ var ScaffoldStatusSchema = exports_external.enum(["draft", "active", "deprecated", "archived"]);
4321
+ var ScaffoldCapabilitySchema = exports_external.enum([
4322
+ "cli",
4323
+ "mcp",
4324
+ "library",
4325
+ "sdk",
4326
+ "rest_api",
4327
+ "dashboard",
4328
+ "database",
4329
+ "auth",
4330
+ "billing",
4331
+ "worker",
4332
+ "daemon",
4333
+ "native",
4334
+ "browser_extension",
4335
+ "ai_provider",
4336
+ "media_pipeline",
4337
+ "data_pipeline",
4338
+ "tests",
4339
+ "ci",
4340
+ "deployment",
4341
+ "docs",
4342
+ "other"
4343
+ ]);
4344
+ var ScaffoldEnvVarSchema = exports_external.object({
4345
+ key: exports_external.string().regex(/^[A-Z][A-Z0-9_]*$/),
4346
+ description: exports_external.string().min(1),
4347
+ required: exports_external.boolean().default(false),
4348
+ secret: exports_external.boolean().default(false),
4349
+ group: exports_external.string().min(1).optional(),
4350
+ default: exports_external.string().optional()
4351
+ }).strict().superRefine((value, ctx) => {
4352
+ if (value.secret && value.default !== undefined) {
4353
+ ctx.addIssue({
4354
+ code: exports_external.ZodIssueCode.custom,
4355
+ message: "Secret scaffold env vars cannot include defaults",
4356
+ path: ["default"]
4357
+ });
4358
+ }
4359
+ });
4360
+ var ScaffoldScriptSchema = exports_external.object({
4361
+ name: exports_external.string().min(1),
4362
+ command: exports_external.string().min(1),
4363
+ description: exports_external.string().min(1).optional(),
4364
+ required: exports_external.boolean().default(false)
4365
+ }).strict();
4366
+ var ScaffoldOutputShapeSchema = exports_external.object({
4367
+ packageManager: exports_external.enum(["bun", "npm", "pnpm", "yarn", "cargo", "pip", "other"]).optional(),
4368
+ languages: exports_external.array(exports_external.string().min(1)).default([]),
4369
+ requiredFiles: exports_external.array(exports_external.string().min(1)).default([]),
4370
+ requiredDirectories: exports_external.array(exports_external.string().min(1)).default([]),
4371
+ optionalDirectories: exports_external.array(exports_external.string().min(1)).default([])
4372
+ }).strict();
4373
+ var ScaffoldManifestSchema = contractBaseSchema(SCHEMA_IDS.scaffoldManifest).extend({
4374
+ name: exports_external.string().min(1),
4375
+ version: exports_external.string().min(1),
4376
+ summary: exports_external.string().min(1),
4377
+ type: ScaffoldTypeSchema,
4378
+ status: ScaffoldStatusSchema.default("draft"),
4379
+ capabilities: exports_external.array(ScaffoldCapabilitySchema).default([]),
4380
+ techStack: exports_external.array(exports_external.string().min(1)).default([]),
4381
+ tags: TagsSchema,
4382
+ source: ResourcePointerSchema.optional(),
4383
+ output: ScaffoldOutputShapeSchema,
4384
+ env: exports_external.array(ScaffoldEnvVarSchema).default([]),
4385
+ scripts: exports_external.array(ScaffoldScriptSchema).default([]),
4386
+ validationChecks: exports_external.array(ValidationCheckSchema).default([]),
4387
+ evidenceRefs: exports_external.array(EvidencePointerSchema).default([])
4388
+ }).strict().superRefine((value, ctx) => {
4389
+ if (value.source?.uri?.startsWith("file://")) {
4390
+ ctx.addIssue({
4391
+ code: exports_external.ZodIssueCode.custom,
4392
+ message: "Public scaffold manifest source refs cannot use local file:// URIs",
4393
+ path: ["source", "uri"]
4394
+ });
4395
+ }
4396
+ if (value.status === "active" && value.validationChecks.length === 0) {
4397
+ ctx.addIssue({
4398
+ code: exports_external.ZodIssueCode.custom,
4399
+ message: "Active scaffold manifests require validation checks",
4400
+ path: ["validationChecks"]
4401
+ });
4402
+ }
4403
+ if (value.status === "active" && value.output.requiredFiles.length === 0 && value.output.requiredDirectories.length === 0) {
4404
+ ctx.addIssue({
4405
+ code: exports_external.ZodIssueCode.custom,
4406
+ message: "Active scaffold manifests require at least one required file or directory",
4407
+ path: ["output"]
4408
+ });
4409
+ }
4410
+ });
4411
+ var ScaffoldInstallStatusSchema = exports_external.enum(["installed", "failed", "cancelled", "partial", "unknown"]);
4412
+ var ScaffoldInstallRecordSchema = contractBaseSchema(SCHEMA_IDS.scaffoldInstallRecord).extend({
4413
+ scaffoldId: exports_external.string().min(1),
4414
+ scaffoldVersion: exports_external.string().min(1).optional(),
4415
+ manifestRef: ResourcePointerSchema.optional(),
4416
+ target: ResourcePointerSchema,
4417
+ status: ScaffoldInstallStatusSchema,
4418
+ installedAt: TimestampSchema.optional(),
4419
+ installer: ActorPointerSchema.optional(),
4420
+ packageManager: exports_external.enum(["bun", "npm", "pnpm", "yarn", "cargo", "pip", "other"]).optional(),
4421
+ options: MetadataSchema.optional(),
4422
+ generatedFiles: exports_external.array(ResourcePointerSchema).default([]),
4423
+ evidenceRefs: exports_external.array(EvidencePointerSchema).default([]),
4424
+ proofBundleRefs: exports_external.array(ResourcePointerSchema).default([])
4425
+ }).strict().superRefine((value, ctx) => {
4426
+ if (value.status === "installed" && !value.installedAt) {
4427
+ ctx.addIssue({
4428
+ code: exports_external.ZodIssueCode.custom,
4429
+ message: "Installed scaffold records require installedAt",
4430
+ path: ["installedAt"]
4431
+ });
4432
+ }
4433
+ if (value.status === "installed" && value.generatedFiles.length === 0 && value.evidenceRefs.length === 0 && value.proofBundleRefs.length === 0) {
4434
+ ctx.addIssue({
4435
+ code: exports_external.ZodIssueCode.custom,
4436
+ message: "Installed scaffold records require generated files, evidence, or proof bundle refs",
4437
+ path: ["generatedFiles"]
4438
+ });
4439
+ }
4440
+ if ((value.status === "failed" || value.status === "partial") && value.evidenceRefs.length === 0 && value.proofBundleRefs.length === 0) {
4441
+ ctx.addIssue({
4442
+ code: exports_external.ZodIssueCode.custom,
4443
+ message: "Failed or partial scaffold records require evidence or proof bundle refs",
4444
+ path: ["evidenceRefs"]
4445
+ });
4446
+ }
4447
+ });
4308
4448
  var ProofCheckResultSchema = exports_external.object({
4309
4449
  checkId: exports_external.string().min(1),
4310
4450
  status: ContractStatusSchema,
@@ -4451,7 +4591,9 @@ var ContractSchemaRegistry = {
4451
4591
  [SCHEMA_IDS.contextPack]: ContextPackSchema,
4452
4592
  [SCHEMA_IDS.agentTrajectory]: AgentTrajectorySchema,
4453
4593
  [SCHEMA_IDS.validationPlan]: ValidationPlanSchema,
4454
- [SCHEMA_IDS.proofBundle]: ProofBundleSchema
4594
+ [SCHEMA_IDS.proofBundle]: ProofBundleSchema,
4595
+ [SCHEMA_IDS.scaffoldManifest]: ScaffoldManifestSchema,
4596
+ [SCHEMA_IDS.scaffoldInstallRecord]: ScaffoldInstallRecordSchema
4455
4597
  };
4456
4598
 
4457
4599
  // src/validators.ts
@@ -4533,6 +4675,15 @@ export {
4533
4675
  TagsSchema,
4534
4676
  Sha256DigestSchema,
4535
4677
  SchemaIdSchema,
4678
+ ScaffoldTypeSchema,
4679
+ ScaffoldStatusSchema,
4680
+ ScaffoldScriptSchema,
4681
+ ScaffoldOutputShapeSchema,
4682
+ ScaffoldManifestSchema,
4683
+ ScaffoldInstallStatusSchema,
4684
+ ScaffoldInstallRecordSchema,
4685
+ ScaffoldEnvVarSchema,
4686
+ ScaffoldCapabilitySchema,
4536
4687
  SCHEMA_IDS,
4537
4688
  ResourceRefSchema,
4538
4689
  ResourcePointerSchema,