mdkg 0.4.0 → 0.4.2

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.
@@ -21,6 +21,7 @@ const visibility_1 = require("../graph/visibility");
21
21
  const sqlite_index_1 = require("../graph/sqlite_index");
22
22
  const errors_1 = require("../util/errors");
23
23
  const skill_mirror_1 = require("./skill_mirror");
24
+ const VALIDATION_PROFILES = new Set(["omni-room"]);
24
25
  exports.RECOMMENDED_HEADINGS = {
25
26
  task: [
26
27
  "Overview",
@@ -143,6 +144,69 @@ function collectManifestCompatibilityWarnings(qid, filePath, node) {
143
144
  }
144
145
  return [];
145
146
  }
147
+ function collectContractProfileWarnings(qid, node) {
148
+ if (!(0, agent_file_types_1.isAgentFileType)(node.type)) {
149
+ return [];
150
+ }
151
+ const warnings = [];
152
+ if (node.frontmatter.profile !== undefined) {
153
+ warnings.push(`${qid}: contract-profile.ambiguous-field warning: profile is ambiguous and is not a supported alias; use contract_profile for mdkg contract surfaces`);
154
+ }
155
+ const contractProfile = node.frontmatter.contract_profile;
156
+ if (typeof contractProfile === "string" && !agent_file_types_1.KNOWN_CONTRACT_PROFILES.has(contractProfile)) {
157
+ warnings.push(`${qid}: contract-profile.unknown warning: unknown contract_profile ${contractProfile}; generic validation accepts well-shaped values, but explicit profile validation may reject it`);
158
+ }
159
+ if (node.type === "receipt") {
160
+ const receiptKind = node.frontmatter.receipt_kind;
161
+ if (typeof receiptKind === "string" && !agent_file_types_1.KNOWN_RECEIPT_KINDS.has(receiptKind)) {
162
+ warnings.push(`${qid}: receipt-kind.unknown warning: unknown receipt_kind ${receiptKind}; generic validation accepts well-shaped values, but explicit profile validation may reject it`);
163
+ }
164
+ const redactionClass = node.frontmatter.redaction_class;
165
+ if (typeof redactionClass === "string" && !agent_file_types_1.KNOWN_REDACTION_CLASSES.has(redactionClass)) {
166
+ warnings.push(`${qid}: redaction-class.unknown warning: unknown redaction_class ${redactionClass}; generic validation accepts well-shaped values, but explicit profile validation may reject it`);
167
+ }
168
+ if (typeof redactionClass === "string" && node.frontmatter.redaction_policy === undefined) {
169
+ warnings.push(`${qid}: redaction-class.missing-policy warning: redaction_class should be paired with redaction_policy so receipt handling and sensitivity are both explicit`);
170
+ }
171
+ }
172
+ return warnings;
173
+ }
174
+ function normalizeValidationProfile(profile) {
175
+ if (profile === undefined) {
176
+ return undefined;
177
+ }
178
+ if (!VALIDATION_PROFILES.has(profile)) {
179
+ throw new errors_1.UsageError("--profile must be one of omni-room");
180
+ }
181
+ return profile;
182
+ }
183
+ function collectContractProfileErrors(qid, node, profile) {
184
+ if (!(0, agent_file_types_1.isAgentFileType)(node.type)) {
185
+ return [];
186
+ }
187
+ const errors = [];
188
+ if (node.frontmatter.profile !== undefined) {
189
+ errors.push(`${qid}: contract-profile.ambiguous-field error: profile is ambiguous and is not a supported alias; use contract_profile for ${profile} validation`);
190
+ }
191
+ const contractProfile = node.frontmatter.contract_profile;
192
+ if (typeof contractProfile === "string" && contractProfile !== profile) {
193
+ errors.push(`${qid}: contract-profile.incompatible error: contract_profile ${contractProfile} is incompatible with validation profile ${profile}`);
194
+ }
195
+ if (node.type === "receipt") {
196
+ const receiptKind = node.frontmatter.receipt_kind;
197
+ if (typeof receiptKind === "string" && !agent_file_types_1.KNOWN_RECEIPT_KINDS.has(receiptKind)) {
198
+ errors.push(`${qid}: receipt-kind.incompatible error: receipt_kind ${receiptKind} is incompatible with validation profile ${profile}`);
199
+ }
200
+ const redactionClass = node.frontmatter.redaction_class;
201
+ if (typeof redactionClass === "string" && !agent_file_types_1.KNOWN_REDACTION_CLASSES.has(redactionClass)) {
202
+ errors.push(`${qid}: redaction-class.incompatible error: redaction_class ${redactionClass} is incompatible with validation profile ${profile}`);
203
+ }
204
+ if (typeof redactionClass === "string" && node.frontmatter.redaction_policy === undefined) {
205
+ errors.push(`${qid}: redaction-class.missing-policy error: redaction_class requires redaction_policy for validation profile ${profile}`);
206
+ }
207
+ }
208
+ return errors;
209
+ }
146
210
  function collectChangedPaths(root) {
147
211
  const result = (0, child_process_1.spawnSync)("git", ["-C", root, "status", "--porcelain", "--", ".mdkg"], {
148
212
  encoding: "utf8",
@@ -234,6 +298,20 @@ function warningDiagnostic(message, nodes) {
234
298
  remediation: "Rename legacy SPEC.md files to MANIFEST.md and update transitional type: spec frontmatter to type: manifest before the compatibility release closes.",
235
299
  };
236
300
  }
301
+ const contractProfileMatch = /(contract-profile|receipt-kind|redaction-class)\.([a-z-]+)/.exec(message);
302
+ if (contractProfileMatch) {
303
+ return {
304
+ id: `${contractProfileMatch[1]}.${contractProfileMatch[2]}`,
305
+ category: "contract-profile",
306
+ severity: "warning",
307
+ message,
308
+ qid,
309
+ node_type: nodeType,
310
+ path: pathValue,
311
+ ref: qid,
312
+ remediation: "Use contract_profile and explicit policy/kind/class fields only when they are intentional generic mdkg contract metadata.",
313
+ };
314
+ }
237
315
  if (message.includes("sqlite") || message.includes("index")) {
238
316
  return {
239
317
  id: "cache.index",
@@ -485,6 +563,7 @@ function validateEventsJsonl(root, config, errors) {
485
563
  }
486
564
  }
487
565
  function collectValidateReceipt(options) {
566
+ const validationProfile = normalizeValidationProfile(options.profile);
488
567
  const config = (0, config_1.loadConfig)(options.root);
489
568
  const templateSchemaInfo = (0, template_schema_1.loadTemplateSchemasWithInfo)(options.root, config, node_1.ALLOWED_TYPES);
490
569
  const templateSchemas = templateSchemaInfo.schemas;
@@ -543,6 +622,10 @@ function collectValidateReceipt(options) {
543
622
  }
544
623
  warnings.push(...collectRawContentWarnings(qid, node));
545
624
  warnings.push(...collectManifestCompatibilityWarnings(qid, filePath, node));
625
+ warnings.push(...collectContractProfileWarnings(qid, node));
626
+ if (validationProfile) {
627
+ errors.push(...collectContractProfileErrors(qid, node, validationProfile));
628
+ }
546
629
  }
547
630
  catch (err) {
548
631
  const message = err instanceof Error ? err.message : "unknown error";
@@ -641,6 +724,7 @@ function collectValidateReceipt(options) {
641
724
  warning_diagnostics: filteredWarningDiagnostics,
642
725
  warning_summary: buildWarningSummary(filteredWarningDiagnostics),
643
726
  errors: uniqueErrors,
727
+ ...(validationProfile ? { validation_profile: validationProfile } : {}),
644
728
  ...(outPath ? { report_path: outPath } : {}),
645
729
  ...(options.changedOnly
646
730
  ? { warning_filter: { mode: "changed-only", changed_paths: Array.from(changedPaths).sort() } }
@@ -44,6 +44,7 @@ const RECEIPT_STATUSES = new Set(["recorded", "verified", "rejected", "supersede
44
44
  const OUTCOMES = new Set(["success", "partial", "failure"]);
45
45
  const REDACTION_POLICIES = new Set(["refs_and_hashes_only", "redacted_summary", "external_private"]);
46
46
  const WORKFLOW_VALIDATE_TYPES = new Set(agent_file_types_1.AGENT_FILE_TYPES);
47
+ const CONTRACT_METADATA_RE = /^[a-z][a-z0-9_]*(?:-[a-z0-9_]+)*$/;
47
48
  function parseCsvList(raw) {
48
49
  if (!raw) {
49
50
  return [];
@@ -84,6 +85,16 @@ function normalizeEnum(value, flag, allowed) {
84
85
  }
85
86
  return normalized;
86
87
  }
88
+ function normalizeContractMetadataToken(value, flag) {
89
+ if (value === undefined) {
90
+ return undefined;
91
+ }
92
+ const normalized = value.toLowerCase();
93
+ if (!CONTRACT_METADATA_RE.test(normalized)) {
94
+ throw new errors_1.UsageError(`${flag} must be lowercase snake/kebab style`);
95
+ }
96
+ return normalized;
97
+ }
87
98
  function normalizeSha256Ref(value, flag) {
88
99
  if (value === undefined) {
89
100
  return undefined;
@@ -251,6 +262,10 @@ function workflowDiagnosticCode(message) {
251
262
  if (manifestCompatMatch) {
252
263
  return `manifest.compat.${manifestCompatMatch[1]}`;
253
264
  }
265
+ const contractProfileMatch = /(contract-profile|receipt-kind|redaction-class)\.([a-z-]+)/.exec(message);
266
+ if (contractProfileMatch) {
267
+ return `${contractProfileMatch[1]}.${contractProfileMatch[2]}`;
268
+ }
254
269
  if (message.includes("references missing") || message.includes("references missing node")) {
255
270
  return "reference.missing";
256
271
  }
@@ -325,7 +340,7 @@ function buildWorkValidateReceipt(options) {
325
340
  .sort((a, b) => a.qid.localeCompare(b.qid));
326
341
  }
327
342
  const candidatePaths = workflowCandidatePaths({ root: options.root, config, ws, type, target });
328
- const validation = (0, validate_1.collectValidateReceipt)({ root: options.root });
343
+ const validation = (0, validate_1.collectValidateReceipt)({ root: options.root, profile: options.profile });
329
344
  const warnings = filterWorkflowMessages(validation.warnings, targets, candidatePaths);
330
345
  const errors = filterWorkflowMessages(validation.errors, targets, candidatePaths);
331
346
  const diagnostics = [
@@ -346,6 +361,7 @@ function buildWorkValidateReceipt(options) {
346
361
  action: "work.validate",
347
362
  ok: errors.length === 0,
348
363
  type: type ?? "all",
364
+ ...(options.profile ? { validation_profile: options.profile } : {}),
349
365
  ...(target ? { target: (0, query_output_1.toNodeSummaryJson)(target) } : {}),
350
366
  checked_count: target ? 1 : candidatePaths.filter((value) => !path_1.default.isAbsolute(value)).length,
351
367
  nodes: targets.map((node) => (0, query_output_1.toNodeSummaryJson)(node)),
@@ -625,6 +641,19 @@ function writeFrontmatterFile(filePath, frontmatter, body) {
625
641
  const content = ["---", ...lines, "---", body.trimStart()].join("\n");
626
642
  (0, atomic_1.atomicWriteFile)(filePath, content.endsWith("\n") ? content : `${content}\n`);
627
643
  }
644
+ function mergeRenderedFrontmatter(content, filePath, additions) {
645
+ const entries = Object.entries(additions).filter((entry) => entry[1] !== undefined);
646
+ if (entries.length === 0) {
647
+ return content;
648
+ }
649
+ const { frontmatter, body } = (0, frontmatter_1.parseFrontmatter)(content, filePath);
650
+ for (const [key, value] of entries) {
651
+ frontmatter[key] = value;
652
+ }
653
+ const lines = ["---", ...(0, frontmatter_1.formatFrontmatter)(frontmatter, frontmatter_1.DEFAULT_FRONTMATTER_KEY_ORDER), "---", body.trimStart()];
654
+ const merged = lines.join("\n");
655
+ return merged.endsWith("\n") ? merged : `${merged}\n`;
656
+ }
628
657
  function createAgentWorkflowNode(options) {
629
658
  const config = (0, config_1.loadConfig)(options.root);
630
659
  const ws = normalizeWorkspace(options.ws);
@@ -641,7 +670,7 @@ function createAgentWorkflowNode(options) {
641
670
  const slug = slugifyTitle(options.title);
642
671
  const filePath = path_1.default.resolve(options.root, workspace.path, workspace.mdkg_dir, "work", `${id}-${slug}`, agent_file_types_1.AGENT_FILE_BASENAMES[options.type]);
643
672
  const template = (0, loader_1.loadTemplate)(options.root, config, options.type);
644
- const content = (0, loader_1.renderTemplate)(template, {
673
+ const renderedContent = (0, loader_1.renderTemplate)(template, {
645
674
  id,
646
675
  type: options.type,
647
676
  title: options.title,
@@ -649,6 +678,7 @@ function createAgentWorkflowNode(options) {
649
678
  updated: today,
650
679
  ...options.overrides,
651
680
  });
681
+ const content = mergeRenderedFrontmatter(renderedContent, filePath, options.overrides);
652
682
  try {
653
683
  (0, atomic_1.writeFileExclusive)(filePath, content);
654
684
  }
@@ -745,6 +775,9 @@ function createWorkOrderForWork(options) {
745
775
  request_ref: requestRef,
746
776
  trigger_ref: triggerRef,
747
777
  payload_hash: payloadHash,
778
+ contract_profile: normalizeContractMetadataToken(options.contractProfile, "--contract-profile"),
779
+ validation_policy_ref: options.validationPolicyRef,
780
+ evidence_policy_ref: options.evidencePolicyRef,
748
781
  input_refs: inputRefs,
749
782
  queue_refs: queueRefs,
750
783
  requested_outputs: requestedOutputs,
@@ -766,6 +799,7 @@ function runWorkContractNewCommandLocked(options) {
766
799
  ? [agentId]
767
800
  : [];
768
801
  const requiredCapabilities = parseCsvList(options.requiredCapabilities);
802
+ const contractProfile = normalizeContractMetadataToken(options.contractProfile, "--contract-profile");
769
803
  const receipt = createAgentWorkflowNode({
770
804
  root: options.root,
771
805
  ws,
@@ -776,6 +810,7 @@ function runWorkContractNewCommandLocked(options) {
776
810
  overrides: {
777
811
  agent_id: agentId,
778
812
  kind,
813
+ contract_profile: contractProfile,
779
814
  pricing_model: normalizeEnum(options.pricingModel ?? "quoted", "--pricing-model", PRICING_MODELS),
780
815
  required_capabilities: requiredCapabilities.length > 0 ? requiredCapabilities : [kind],
781
816
  inputs: parseCsvList(options.inputs),
@@ -807,6 +842,9 @@ function runWorkOrderNewCommandLocked(options) {
807
842
  queueRefs: options.queueRefs,
808
843
  requestedOutputs: options.requestedOutputs,
809
844
  constraintRefs: options.constraintRefs,
845
+ contractProfile: options.contractProfile,
846
+ validationPolicyRef: options.validationPolicyRef,
847
+ evidencePolicyRef: options.evidencePolicyRef,
810
848
  now: options.now,
811
849
  });
812
850
  printReceipt("order created", created.receipt, options.json);
@@ -979,6 +1017,11 @@ function runWorkReceiptNewCommandLocked(options) {
979
1017
  outcome: normalizeEnum(options.outcome, "--outcome", OUTCOMES),
980
1018
  cost_ref: options.costRef ?? "cost.redacted",
981
1019
  redaction_policy: normalizeEnum(options.redactionPolicy ?? "refs_and_hashes_only", "--redaction-policy", REDACTION_POLICIES),
1020
+ contract_profile: normalizeContractMetadataToken(options.contractProfile, "--contract-profile"),
1021
+ receipt_kind: normalizeContractMetadataToken(options.receiptKind, "--receipt-kind"),
1022
+ redaction_class: normalizeContractMetadataToken(options.redactionClass, "--redaction-class"),
1023
+ validation_policy_ref: options.validationPolicyRef,
1024
+ evidence_policy_ref: options.evidencePolicyRef,
982
1025
  artifacts: parseCsvList(options.artifacts),
983
1026
  proof_refs: parseCsvList(options.proofRefs),
984
1027
  attestation_refs: parseCsvList(options.attestationRefs),
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.AGENT_ATTRIBUTE_KEY_ORDER = exports.AGENT_FILE_BASENAMES = exports.AGENT_FILE_TYPES = exports.LEGACY_SPEC_BASENAME = exports.CANONICAL_MANIFEST_BASENAME = void 0;
6
+ exports.KNOWN_REDACTION_CLASSES = exports.KNOWN_RECEIPT_KINDS = exports.KNOWN_CONTRACT_PROFILES = exports.AGENT_ATTRIBUTE_KEY_ORDER = exports.AGENT_FILE_BASENAMES = exports.AGENT_FILE_TYPES = exports.LEGACY_SPEC_BASENAME = exports.CANONICAL_MANIFEST_BASENAME = void 0;
7
7
  exports.isAgentFileType = isAgentFileType;
8
8
  exports.isManifestSemanticType = isManifestSemanticType;
9
9
  exports.collectManifestSiblingConflicts = collectManifestSiblingConflicts;
@@ -41,6 +41,9 @@ const MANIFEST_ATTRIBUTE_KEYS = [
41
41
  "role",
42
42
  "runtime_mode",
43
43
  "work_contracts",
44
+ "contract_profile",
45
+ "validation_policy_ref",
46
+ "evidence_policy_ref",
44
47
  "requested_capabilities",
45
48
  "skill_refs",
46
49
  "tool_refs",
@@ -58,6 +61,7 @@ exports.AGENT_ATTRIBUTE_KEY_ORDER = {
58
61
  "version",
59
62
  "agent_id",
60
63
  "kind",
64
+ "contract_profile",
61
65
  "pricing_model",
62
66
  "required_capabilities",
63
67
  "skill_refs",
@@ -79,6 +83,9 @@ exports.AGENT_ATTRIBUTE_KEY_ORDER = {
79
83
  "request_ref",
80
84
  "trigger_ref",
81
85
  "payload_hash",
86
+ "contract_profile",
87
+ "validation_policy_ref",
88
+ "evidence_policy_ref",
82
89
  "input_refs",
83
90
  "queue_refs",
84
91
  "requested_outputs",
@@ -92,6 +99,11 @@ exports.AGENT_ATTRIBUTE_KEY_ORDER = {
92
99
  "outcome",
93
100
  "cost_ref",
94
101
  "redaction_policy",
102
+ "contract_profile",
103
+ "receipt_kind",
104
+ "redaction_class",
105
+ "validation_policy_ref",
106
+ "evidence_policy_ref",
95
107
  "proof_refs",
96
108
  "attestation_refs",
97
109
  "evidence_hashes",
@@ -123,6 +135,9 @@ exports.AGENT_ATTRIBUTE_KEY_ORDER = {
123
135
  const SEMVER_RE = /^\d+\.\d+\.\d+(?:[-+][a-z0-9.-]+)?$/;
124
136
  const LOWER_TOKEN_RE = /^[a-z][a-z0-9_]*(?:-[a-z0-9_]+)*$/;
125
137
  const FIELD_DESCRIPTOR_RE = /^[a-z][a-z0-9_]*:[a-z][a-z0-9_]*(?::(?:required|optional))?$/;
138
+ exports.KNOWN_CONTRACT_PROFILES = new Set(["generic", "omni-room"]);
139
+ exports.KNOWN_RECEIPT_KINDS = new Set(["worker", "final", "cleanup", "audit"]);
140
+ exports.KNOWN_REDACTION_CLASSES = new Set(["public", "internal", "private", "restricted"]);
126
141
  const ROLE_VALUES = new Set([
127
142
  "orchestrator",
128
143
  "subagent",
@@ -233,7 +248,7 @@ function collectManifestSiblingConflicts(filePaths, formatDir) {
233
248
  const conflicts = [];
234
249
  for (const [dirPath, basenames] of Array.from(basenamesByDir.entries()).sort()) {
235
250
  if (basenames.has(exports.CANONICAL_MANIFEST_BASENAME) && basenames.has(exports.LEGACY_SPEC_BASENAME)) {
236
- conflicts.push(`${formatDir(dirPath)}: MANIFEST.md and SPEC.md cannot both exist in the same logical Omni unit; keep MANIFEST.md and remove the legacy SPEC.md alias`);
251
+ conflicts.push(`${formatDir(dirPath)}: MANIFEST.md and SPEC.md cannot both exist in the same logical unit; keep MANIFEST.md and remove the legacy SPEC.md alias`);
237
252
  }
238
253
  }
239
254
  return conflicts;
@@ -390,6 +405,48 @@ function validatePortableOrUriScalar(value, key, filePath) {
390
405
  throw formatError(filePath, `${key} must be a portable id or URI ref`);
391
406
  }
392
407
  }
408
+ function requireContractMetadataToken(value, key, filePath) {
409
+ if (LOWER_TOKEN_RE.test(value)) {
410
+ return;
411
+ }
412
+ const id = key === "receipt_kind"
413
+ ? "receipt-kind.invalid"
414
+ : key === "redaction_class"
415
+ ? "redaction-class.invalid"
416
+ : "contract-profile.invalid";
417
+ throw formatError(filePath, `${id}: ${key} must be lowercase snake/kebab style`);
418
+ }
419
+ function optionalContractMetadataString(frontmatter, key, filePath) {
420
+ const value = frontmatter[key];
421
+ if (value === undefined) {
422
+ return undefined;
423
+ }
424
+ const id = key === "receipt_kind"
425
+ ? "receipt-kind.invalid"
426
+ : key === "redaction_class"
427
+ ? "redaction-class.invalid"
428
+ : "contract-profile.invalid";
429
+ if (typeof value !== "string" || value.trim().length === 0) {
430
+ throw formatError(filePath, `${id}: ${key} must be a non-empty string`);
431
+ }
432
+ return value;
433
+ }
434
+ function validateOptionalContractProfile(frontmatter, filePath) {
435
+ const contractProfile = optionalContractMetadataString(frontmatter, "contract_profile", filePath);
436
+ if (contractProfile) {
437
+ requireContractMetadataToken(contractProfile, "contract_profile", filePath);
438
+ }
439
+ }
440
+ function validateOptionalPolicyRefs(frontmatter, filePath) {
441
+ const validationPolicyRef = optionalRefString(frontmatter, "validation_policy_ref", filePath);
442
+ if (validationPolicyRef) {
443
+ validatePortableOrUriScalar(validationPolicyRef, "validation_policy_ref", filePath);
444
+ }
445
+ const evidencePolicyRef = optionalRefString(frontmatter, "evidence_policy_ref", filePath);
446
+ if (evidencePolicyRef) {
447
+ validatePortableOrUriScalar(evidencePolicyRef, "evidence_policy_ref", filePath);
448
+ }
449
+ }
393
450
  function validateOptionalFieldDescriptors(values, key, filePath) {
394
451
  if (values.length === 0) {
395
452
  return;
@@ -448,6 +505,8 @@ function validateAgentFrontmatter(type, frontmatter, filePath) {
448
505
  requireEnum(runtimeMode, "runtime_mode", RUNTIME_MODE_VALUES, filePath);
449
506
  const updatePolicy = expectString(frontmatter, "update_policy", filePath);
450
507
  requireEnum(updatePolicy, "update_policy", UPDATE_POLICY_VALUES, filePath);
508
+ validateOptionalContractProfile(frontmatter, filePath);
509
+ validateOptionalPolicyRefs(frontmatter, filePath);
451
510
  validateRelativeMarkdownPaths(optionalList(frontmatter, "work_contracts", filePath), "work_contracts", "WORK.md", filePath);
452
511
  validateCapabilities(optionalList(frontmatter, "requested_capabilities", filePath), "requested_capabilities", filePath);
453
512
  validatePortableRefs(optionalList(frontmatter, "skill_refs", filePath), "skill_refs", filePath);
@@ -467,6 +526,7 @@ function validateAgentFrontmatter(type, frontmatter, filePath) {
467
526
  requirePortableIdRef(agentId, "agent_id", filePath);
468
527
  const kind = expectString(frontmatter, "kind", filePath);
469
528
  requireLowerToken(kind, "kind", filePath);
529
+ validateOptionalContractProfile(frontmatter, filePath);
470
530
  const pricingModel = expectString(frontmatter, "pricing_model", filePath);
471
531
  requireEnum(pricingModel, "pricing_model", PRICING_MODEL_VALUES, filePath);
472
532
  const requiredCapabilities = expectList(frontmatter, "required_capabilities", filePath);
@@ -505,6 +565,8 @@ function validateAgentFrontmatter(type, frontmatter, filePath) {
505
565
  validatePortableOrUriScalar(requester, "requester", filePath);
506
566
  const orderStatus = expectString(frontmatter, "order_status", filePath);
507
567
  requireEnum(orderStatus, "order_status", ORDER_STATUS_VALUES, filePath);
568
+ validateOptionalContractProfile(frontmatter, filePath);
569
+ validateOptionalPolicyRefs(frontmatter, filePath);
508
570
  const requestRef = optionalRefString(frontmatter, "request_ref", filePath);
509
571
  if (requestRef) {
510
572
  validatePortableOrUriScalar(requestRef, "request_ref", filePath);
@@ -542,6 +604,16 @@ function validateAgentFrontmatter(type, frontmatter, filePath) {
542
604
  if (redactionPolicy) {
543
605
  requireEnum(redactionPolicy, "redaction_policy", REDACTION_POLICY_VALUES, filePath);
544
606
  }
607
+ validateOptionalContractProfile(frontmatter, filePath);
608
+ const receiptKind = optionalContractMetadataString(frontmatter, "receipt_kind", filePath);
609
+ if (receiptKind) {
610
+ requireContractMetadataToken(receiptKind, "receipt_kind", filePath);
611
+ }
612
+ const redactionClass = optionalContractMetadataString(frontmatter, "redaction_class", filePath);
613
+ if (redactionClass) {
614
+ requireContractMetadataToken(redactionClass, "redaction_class", filePath);
615
+ }
616
+ validateOptionalPolicyRefs(frontmatter, filePath);
545
617
  validatePortableOrUriRefs(optionalList(frontmatter, "proof_refs", filePath), "proof_refs", filePath);
546
618
  validatePortableOrUriRefs(optionalList(frontmatter, "attestation_refs", filePath), "attestation_refs", filePath);
547
619
  validateHashRefs(optionalList(frontmatter, "evidence_hashes", filePath), "evidence_hashes", filePath);
@@ -30,11 +30,16 @@ exports.DEFAULT_FRONTMATTER_KEY_ORDER = [
30
30
  "role",
31
31
  "runtime_mode",
32
32
  "work_contracts",
33
+ "contract_profile",
34
+ "validation_policy_ref",
35
+ "evidence_policy_ref",
33
36
  "requested_capabilities",
34
37
  "resource_profile",
35
38
  "update_policy",
36
39
  "agent_id",
37
40
  "kind",
41
+ "receipt_kind",
42
+ "redaction_class",
38
43
  "pricing_model",
39
44
  "required_capabilities",
40
45
  "inputs",
@@ -236,8 +236,36 @@ function requireTemplateSchema(type, templateSchemas, filePath) {
236
236
  return schema;
237
237
  }
238
238
  const OPTIONAL_COMPAT_TEMPLATE_KEYS = {
239
+ manifest: {
240
+ contract_profile: "scalar",
241
+ validation_policy_ref: "scalar",
242
+ evidence_policy_ref: "scalar",
243
+ profile: "scalar",
244
+ },
239
245
  spec: {
240
246
  spec_kind: "scalar",
247
+ contract_profile: "scalar",
248
+ validation_policy_ref: "scalar",
249
+ evidence_policy_ref: "scalar",
250
+ profile: "scalar",
251
+ },
252
+ work: {
253
+ contract_profile: "scalar",
254
+ profile: "scalar",
255
+ },
256
+ work_order: {
257
+ contract_profile: "scalar",
258
+ validation_policy_ref: "scalar",
259
+ evidence_policy_ref: "scalar",
260
+ profile: "scalar",
261
+ },
262
+ receipt: {
263
+ contract_profile: "scalar",
264
+ receipt_kind: "scalar",
265
+ redaction_class: "scalar",
266
+ validation_policy_ref: "scalar",
267
+ evidence_policy_ref: "scalar",
268
+ profile: "scalar",
241
269
  },
242
270
  };
243
271
  function validateTemplateKeys(frontmatter, schema, filePath) {
@@ -57,6 +57,12 @@ Agent operating prompt:
57
57
  - Use `mdkg archive add/list/show/verify/compress` for committed source and artifact sidecars under `.mdkg/archive`.
58
58
  - Use `mdkg work ...` helpers for semantic mirror contracts, deterministic triggers, work order status, receipt verification, and artifact registration.
59
59
  - Treat work contracts, orders, and receipts as committed semantic mirrors only; never store raw secrets, credentials, live payment state, ledger mutations, or canonical marketplace state in mdkg.
60
+ - Treat optional `contract_profile`, policy refs, receipt kind, and redaction
61
+ class fields as generic semantic mirror metadata. Use `mdkg validate --profile
62
+ omni-room` or `mdkg work validate --profile omni-room` only when a downstream
63
+ profile-specific check is intended; mdkg validates mirrors, while downstream
64
+ runtimes own execution, provider state, billing or ledger state, and final
65
+ receipt authority.
60
66
  - Use `artifact://...` for external/runtime-managed artifacts and `archive://...` for committed mdkg archive sidecars.
61
67
  - Use `mdkg bundle create/list/show/verify` for explicit full `.mdkg` graph snapshot bundles.
62
68
  - Use `mdkg subgraph add/list/verify/sync/materialize` to register child bundle snapshots as read-only planning context, refresh root-owned child bundle snapshots, and optionally generate ignored inspection trees.
@@ -120,10 +120,11 @@ Project database commands:
120
120
  - active `.mdkg/db/runtime/` files and `.mdkg/db` WAL/SHM/journal/lock/temp files are ignored by default
121
121
 
122
122
  Validation commands:
123
- - `mdkg validate [--out <path>] [--json-out <path>] [--quiet] [--changed-only] [--summary] [--limit <n>] [--json]`
123
+ - `mdkg validate [--out <path>] [--json-out <path>] [--quiet] [--changed-only] [--summary] [--limit <n>] [--profile <name>] [--json]`
124
124
  - `--changed-only` filters warning presentation to changed `.mdkg` files while full graph errors still run
125
125
  - `--summary` emits bounded warning samples for agent/CI logs; `--limit <n>` controls the sample size
126
126
  - `--out <path>` writes the compatibility text report; `--json-out <path>` writes a clean full JSON receipt
127
+ - `--profile omni-room` applies explicit contract-profile validation after generic validation; it is separate from `mdkg pack --profile`
127
128
  - JSON receipts include `warning_summary` and `warning_diagnostics` with warning ids, categories, severity, paths, refs, and remediation text
128
129
 
129
130
  Node creation commands:
@@ -146,6 +147,9 @@ Agent workflow file type creation:
146
147
 
147
148
  Agent workflow notes:
148
149
  - `--id <portable-id>` is only for agent workflow file types.
150
+ - `--contract-profile <name>` is supported for MANIFEST, WORK, WORK_ORDER, and RECEIPT.
151
+ - `--validation-policy-ref <ref>` and `--evidence-policy-ref <ref>` are supported for MANIFEST, WORK_ORDER, and RECEIPT.
152
+ - `--receipt-kind <kind>` and `--redaction-class <class>` are supported for RECEIPT.
149
153
  - `manifest` and `work` scaffold as validation-clean standalone docs.
150
154
  - `work_order`, `receipt`, `feedback`, `dispute`, and `proposal` need real refs before strict `mdkg validate` passes.
151
155
  - `goal` nodes capture recursive objective state and required checks, but normal `mdkg next` does not select them.
@@ -285,22 +289,23 @@ Subgraph orchestration:
285
289
  - public/internal subgraphs require public bundle profiles
286
290
 
287
291
  Work semantic mirrors:
288
- - `mdkg work contract new "<title>" --id <work.id> --agent-id <agent.id> --kind <kind> --inputs <...> --outputs <...> [--required-capabilities <...>] [--pricing-model <...>] [--json]`
292
+ - `mdkg work contract new "<title>" --id <work.id> --agent-id <agent.id> --kind <kind> --inputs <...> --outputs <...> [--contract-profile <name>] [--required-capabilities <...>] [--pricing-model <...>] [--json]`
289
293
  - `mdkg work trigger <work-or-capability-ref> [--id <order.id>] [--title "<title>"] [--requester <ref>] [--enqueue <queue>] [--json]`
290
- - `mdkg work order new "<title>" --id <order.id> --work-id <work.id> --requester <ref> [--request-ref <ref>] [--input-refs <...>] [--requested-outputs <...>] [--json]`
294
+ - `mdkg work order new "<title>" --id <order.id> --work-id <work.id> --requester <ref> [--contract-profile <name>] [--validation-policy-ref <ref>] [--evidence-policy-ref <ref>] [--request-ref <ref>] [--input-refs <...>] [--requested-outputs <...>] [--json]`
291
295
  - `mdkg work order status <id-or-qid> [--json]`
292
296
  - `mdkg work order update <id-or-qid> [--status <status>] [--add-input-refs <...>] [--add-artifacts <...>] [--json]`
293
- - `mdkg work receipt new "<title>" --id <receipt.id> --work-order-id <order.id> --outcome success|partial|failure [--receipt-status recorded|verified|rejected|superseded] [--json]`
297
+ - `mdkg work receipt new "<title>" --id <receipt.id> --work-order-id <order.id> --outcome success|partial|failure [--receipt-status recorded|verified|rejected|superseded] [--redaction-policy refs_and_hashes_only|redacted_summary|external_private] [--contract-profile <name>] [--receipt-kind <kind>] [--redaction-class <class>] [--validation-policy-ref <ref>] [--evidence-policy-ref <ref>] [--json]`
294
298
  - `mdkg work receipt verify <id-or-qid> [--json]`
295
299
  - `mdkg work receipt update <id-or-qid> [--receipt-status <status>] [--add-artifacts <...>] [--add-proof-refs <...>] [--add-attestation-refs <...>] [--json]`
296
300
  - `mdkg work artifact add <order-or-receipt-id-or-qid> <file> [--id <archive.id>] [--kind source|artifact] [--json]`
297
- - `mdkg work validate [<id-or-qid>] [--type manifest|spec|work|work_order|receipt|feedback|dispute|proposal] [--json]`
301
+ - `mdkg work validate [<id-or-qid>] [--type manifest|spec|work|work_order|receipt|feedback|dispute|proposal] [--profile <name>] [--json]`
298
302
  - `work trigger` accepts a `WORK.md` ref directly or a `MANIFEST.md` / legacy `SPEC.md` capability ref with exactly one resolvable work contract; it creates a submitted order mirror and never executes work
299
303
  - example: `mdkg work trigger work.example --id order.example-1 --requester user://example --json`
300
304
  - `work trigger --enqueue <queue>` requires a valid project DB plus an explicitly created active queue, creates a submitted order mirror, and enqueues a local delivery message without executing work
301
305
  - `work order status` is read-only and reports deterministic order state plus linked receipts
302
306
  - `work receipt verify` is read-only and reports linkage, evidence, archive ref, hash, outcome, and redaction-policy checks
303
307
  - `work validate` is read-only and reports typed diagnostics for agent workflow mirrors; obvious raw secret, prompt, token, or payload markers are warnings, not hard failures
308
+ - `work validate --profile omni-room` applies explicit contract-profile validation after generic mirror validation; it is separate from `mdkg pack --profile`
304
309
  - work commands mutate mdkg semantic mirror files only; production order, receipt, feedback, dispute, payment, ledger, marketplace inventory, fulfillment, and execution state remains canonical outside mdkg
305
310
  - do not store raw secrets, credentials, live payment state, ledger mutations, or canonical marketplace state in work mirrors
306
311
  - `artifact://...` refs identify external/runtime-managed artifacts; `archive://...` refs identify committed mdkg archive sidecars
@@ -80,8 +80,9 @@ intentionally with `mdkg new task ...` or `mdkg new test ...`.
80
80
  Agent workflow docs can use semantic ids:
81
81
 
82
82
  ```bash
83
- mdkg new manifest "image worker" --id agent.image-worker
84
- mdkg new work "generate image" --id work.generate-image
83
+ mdkg new manifest "image worker" --id agent.image-worker --contract-profile generic
84
+ mdkg new work "generate image" --id work.generate-image --contract-profile generic
85
+ mdkg work validate --profile omni-room --json
85
86
  ```
86
87
 
87
88
  `MANIFEST.md` is optional. Repos without manifest files still validate. When
@@ -92,6 +93,14 @@ release, and `mdkg spec list/show/validate` remains a deprecated alias for
92
93
  read-only discovery surface for skills, manifests, WORK contracts, core docs,
93
94
  and design docs.
94
95
 
96
+ Agent workflow files may include optional generic mirror fields such as
97
+ `contract_profile`, `validation_policy_ref`, `evidence_policy_ref`,
98
+ `receipt_kind`, and `redaction_class`. mdkg validates those mirrors and can run
99
+ profile checks such as `mdkg validate --profile omni-room` and
100
+ `mdkg work validate --profile omni-room`; downstream runtimes still own queue
101
+ execution, room ids, provider state, billing or ledger state, and final receipt
102
+ authority.
103
+
95
104
  Read `AGENT_START.md` first when this repo includes it.
96
105
 
97
106
  ## Pack Profiles
@@ -229,10 +238,10 @@ mdkg archive verify archive://archive.example
229
238
  Use work lifecycle helpers for semantic mirrors only:
230
239
 
231
240
  ```bash
232
- mdkg work contract new "example capability" --id work.example --agent-id agent.example --kind example --inputs prompt:text:required --outputs result:text:required
241
+ mdkg work contract new "example capability" --id work.example --agent-id agent.example --kind example --contract-profile generic --inputs prompt:text:required --outputs result:text:required
233
242
  mdkg work trigger work.example --id order.example-1 --requester user://example
234
243
  mdkg work order status order.example-1 --json
235
- mdkg work receipt new "example receipt" --id receipt.example-1 --work-order-id order.example-1 --outcome success
244
+ mdkg work receipt new "example receipt" --id receipt.example-1 --work-order-id order.example-1 --outcome success --contract-profile generic --receipt-kind worker --redaction-class internal
236
245
  mdkg work receipt verify receipt.example-1 --json
237
246
  ```
238
247
 
@@ -249,7 +258,7 @@ Update and artifact commands accept local ids or local qids; subgraph qids are r
249
258
  `mdkg work trigger` creates a deterministic submitted `WORK_ORDER.md` from a
250
259
  WORK contract or a SPEC with exactly one resolvable work contract. `mdkg work
251
260
  order status` and `mdkg work receipt verify` are read-only review helpers.
252
- `mdkg work validate [<id-or-qid>] [--type spec|work|work_order|receipt|feedback|dispute|proposal] --json`
261
+ `mdkg work validate [<id-or-qid>] [--type manifest|spec|work|work_order|receipt|feedback|dispute|proposal] [--profile omni-room] --json`
253
262
  is a read-only focused validator for agent workflow mirrors with typed diagnostics
254
263
  and raw secret, prompt, token, or payload marker warnings.
255
264
  `mdkg work trigger --enqueue <queue>` optionally writes a local project DB queue