@namewta/speculo 0.8.9 → 0.8.11

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.
Files changed (64) hide show
  1. package/dist/src/structured.js +186 -0
  2. package/dist/src/structured.js.map +1 -1
  3. package/package.json +1 -1
  4. package/template/canonical/canonical-specdev-goal-plan.md +14 -4
  5. package/template/canonical/canonical-specdev-orchestrate-implementation.md +27 -7
  6. package/template/canonical/canonical-specdev-tickets.md +5 -2
  7. package/template/commands/archive-and-consolidate.md +9 -0
  8. package/template/commands/status.md +3 -3
  9. package/template/workflows/ops/A-archive-and-learn/A-archive-and-learn.md +64 -0
  10. package/template/workflows/ops/A-archive-and-learn/promotion-plan-template.md +41 -0
  11. package/template/workflows/ops/A-archive-and-learn/retrospective-template.md +53 -0
  12. package/template/workflows/ops/E-execute-and-stabilize/E-execute-and-stabilize.md +75 -0
  13. package/template/workflows/ops/E-execute-and-stabilize/attempt-summary-template.md +44 -0
  14. package/template/workflows/ops/E-execute-and-stabilize/diagnosis-template.md +24 -0
  15. package/template/workflows/ops/E-execute-and-stabilize/handoff-template.md +32 -0
  16. package/template/workflows/ops/E-execute-and-stabilize/rollback-template.md +26 -0
  17. package/template/workflows/ops/E-execute-and-stabilize/verification-state-template.json +30 -0
  18. package/template/workflows/ops/E-execute-and-stabilize/verification-template.md +55 -0
  19. package/template/workflows/ops/I-intake-and-assess/I-intake-and-assess.md +66 -0
  20. package/template/workflows/ops/I-intake-and-assess/change-status-template.json +26 -0
  21. package/template/workflows/ops/I-intake-and-assess/collector-catalog.md +28 -0
  22. package/template/workflows/ops/I-intake-and-assess/deployment-dossier-template.md +62 -0
  23. package/template/workflows/ops/I-intake-and-assess/global-change-status-template.json +26 -0
  24. package/template/workflows/ops/I-intake-and-assess/project-detection.md +38 -0
  25. package/template/workflows/ops/I-intake-and-assess/request-template.md +44 -0
  26. package/template/workflows/ops/I-intake-and-assess/system-report-template.md +34 -0
  27. package/template/workflows/ops/I-intake-and-assess/target-profile-template.json +24 -0
  28. package/template/workflows/ops/INDEX.md +30 -0
  29. package/template/workflows/ops/P-plan-and-approve/P-plan-and-approve.md +65 -0
  30. package/template/workflows/ops/P-plan-and-approve/plan-review-template.md +78 -0
  31. package/template/workflows/ops/README.md +120 -0
  32. package/template/workflows/ops/_state/archive/.gitkeep +1 -0
  33. package/template/workflows/ops/_state/changes/.gitkeep +1 -0
  34. package/template/workflows/ops/_state/status.json +6 -0
  35. package/template/workflows/ops/common/rules/artifact-contract.md +37 -0
  36. package/template/workflows/ops/common/rules/closure-and-learning.md +25 -0
  37. package/template/workflows/ops/common/rules/evidence-and-redaction.md +22 -0
  38. package/template/workflows/ops/common/rules/execution-loop.md +27 -0
  39. package/template/workflows/ops/common/rules/path-and-scope-contract.md +21 -0
  40. package/template/workflows/ops/common/rules/plan-and-approval.md +25 -0
  41. package/template/workflows/ops/common/rules/project-and-change-scope.md +20 -0
  42. package/template/workflows/ops/common/rules/target-profile-and-release-gates.md +44 -0
  43. package/template/workflows/ops/common/schemas/approval.schema.json +28 -0
  44. package/template/workflows/ops/common/schemas/attempt.schema.json +42 -0
  45. package/template/workflows/ops/common/schemas/change-status.schema.json +43 -0
  46. package/template/workflows/ops/common/schemas/deployment-model.schema.json +26 -0
  47. package/template/workflows/ops/common/schemas/implementation-plan.schema.json +221 -0
  48. package/template/workflows/ops/common/schemas/inventory-snapshot.schema.json +31 -0
  49. package/template/workflows/ops/common/schemas/journal-event.schema.json +22 -0
  50. package/template/workflows/ops/common/schemas/project.schema.json +19 -0
  51. package/template/workflows/ops/common/schemas/promotion-approval.schema.json +20 -0
  52. package/template/workflows/ops/common/schemas/promotion-manifest.schema.json +22 -0
  53. package/template/workflows/ops/common/schemas/status.schema.json +30 -0
  54. package/template/workflows/ops/common/schemas/target-profile.schema.json +32 -0
  55. package/template/workflows/ops/common/schemas/verification-state.schema.json +30 -0
  56. package/template/workflows/ops/common/tools/close-change.mjs +177 -0
  57. package/template/workflows/ops/common/tools/validate-ops.mjs +992 -0
  58. package/template/workflows/ops/runtime-contract.json +14 -0
  59. package/template/workflows/specdev/I-implement/I-implement.md +6 -3
  60. package/template/workflows/specdev/I-implement/evidence-template.md +13 -0
  61. package/template/workflows/specdev/O-orchestrate-implementation/execution-loop.md +3 -2
  62. package/template/workflows/specdev/P-goal-plan/completion-control.md +3 -0
  63. package/template/workflows/specdev/P-goal-plan/lead-orchestration.md +6 -2
  64. package/template/workflows/specdev/common/skills/dev-worktree/references/finalize.md +5 -2
@@ -0,0 +1,25 @@
1
+ # 计划与批准合同
2
+
3
+ ## Ready 门
4
+
5
+ P 只有在 request 明确、快照新鲜度被接受、deployment model 与 target profile v1 无 blocking unknown、source revision/target fingerprint/profile digest 可重建、deployment root 和整体控制面身份已确认、健康与恢复可判定时生成 Ready plan。重新规划还必须绑定触发 attempt 和 diagnosis。
6
+
7
+ 计划深度随事故半径增长:本地可丢弃环境可 lite;共享非生产默认 standard;生产、全局配置、数据、流量、权限、密钥或不可逆动作必须 deep。
8
+
9
+ ## 结构化操作与版本
10
+
11
+ 每个 plan-NNN.json 使用 schema v3,并绑定 target profile path/digest。它包含 required Gate DAG、batch `gate_ids`、`after_batches`、不可变 artifact requirements、逐项 verification contract、data protection、staging/activation/previous refs 和恢复策略。每个 operation 包含 id、batch、kind、target、cwd、preconditions、write set、external mutation、preview、apply、postconditions、rollback、risk、privilege 和 evidence。无法使用 typed adapter 时,以 custom-command 显示完整 shell、command、引用方式和风险。
12
+
13
+ plan-NNN 永不覆盖。修订版记录 supersedes_plan_path;由失败 attempt 触发时再记录 triggered_by_attempt,执行前按用户审核意见修订时允许后者为 null。修订版完整重述当前待执行状态;旧已执行 batch 只由 attempt evidence 表示,不能自动成为新 plan 的批准。旧 plan v2 只读且不可重新批准;继续 mutation 必须生成 v3,不能推导缺失的 identity、Gate 或数据保护结论。
14
+
15
+ ## Gate、候选与数据保护
16
+
17
+ 每个 required Gate 和 verification id 只有一个 owner。batch `gate_ids` 表示启动前置 Gate,可为空;Gate `after_batches` 表示生成 Gate 结论前必须完成的批次;两者与各自依赖合成的 DAG 必须可拓扑排序。不可变候选使用 SHA-256、image digest 或 provider immutable id;可变 tag 仅作显示。适用时计划包含授权 root 内独立 staging、目标端摘要重验、原子 activation、previous ref 和保留策略。
18
+
19
+ 每个 data mutation 映射 data-protection 条目。production 不接受 waiver;local waiver 仅在用户对精确对象明确批准并记录 decision locator、exact scope、对象身份、零冲突 preflight 和 forward-only 恢复边界时有效。backup 不只证明存在,还要规划可读性、restore ref 与恢复验证。cleanup 使用独立 batch。
20
+
21
+ ## 批量批准
22
+
23
+ P 一次展示 target/profile identity、完整待执行 batch、Gate/依赖、候选摘要、数据保护、差异、外部 mutation、全局环境 key/scope/source/impact/rollback、风险、验证、恢复、cleanup 和期限。用户可以一条回复批准多个 ids,不逐命令确认。approval 绑定 scope/project/change、plan SHA-256、source、target、batches、条件和期限,不保存密钥。
24
+
25
+ profile/计划内容、固定点、identity、权限、external mutation、数据保护或期限漂移时,不执行受影响 batch;保留旧工件,将 approval invalidated,并返回 I/P 生成新版本。用户最初要求部署不等于对后续修复、回滚、数据恢复、cleanup、归档或知识改写的授权。
@@ -0,0 +1,20 @@
1
+ # Project 与 Change Scope
2
+
3
+ ## Scope
4
+
5
+ - `global`:宿主机、共享环境、Docker/集群控制面基线或跨项目运维规则;change 位于根 changes/archive。
6
+ - `project`:指定应用、仓库或可独立部署组件;change 和永久知识位于 `projects/{project_id}`。
7
+
8
+ 项目工作不得使用 global scope 来绕过项目历史;project change 可在 A 阶段把确实跨项目的候选提升到全局知识。
9
+
10
+ ## 项目身份
11
+
12
+ `project_id` 是 lowercase kebab 稳定目录 id,不等同于可变 display name。识别顺序:用户显式 id、已登记无凭据 VCS identity、monorepo component identity、workspace/package identity、用户确认 alias。目录 basename 只能提出候选。
13
+
14
+ VCS identity 必须移除 scheme userinfo、token、query 和 fragment;无法可靠脱敏时存储 hash 与人类可识别 label,不保存原值。identity 冲突或多个项目命中时一次展示并请求选择,不自动合并历史。
15
+
16
+ ## Change 选择
17
+
18
+ 全局索引 entry 统一为 `{scope, project_id, change}`。tuple 唯一;change 名只需在同一 scope root 内唯一。新 change 先写完整目录和 `.status.json`,再加入 active;失败时不留下索引悬挂。
19
+
20
+ 归档 change 不恢复写入。follow-up 继承 project/global scope,但 request 明确记录来源 archive locator 和仍适用的事实;不得复制旧批准作为当前授权。
@@ -0,0 +1,44 @@
1
+ # Target Profile、发布 Gate 与数据保护
2
+
3
+ ## 三层证据
4
+
5
+ I 拥有 `deployment/target-profile.json`,记录非敏感期望、现场身份和授权边界;P 拥有绑定 profile 摘要的 plan,记录候选、Gate、数据保护和恢复策略;E 在每个 attempt 中拥有 `verification-state.json`,记录实测身份、Gate、构件、探针、稳定窗口和恢复资产。Markdown 只投影这些结构化事实。
6
+
7
+ profile、plan 或 verification state 禁止保存 secret 值。只保存 key、provider/受控 locator、版本、是否必须存在和 presence 证据。需要生成私密配置时,输出必须位于 deployment root 或外部受控位置,权限和未回显证明进入 attempt;私密文件本身不得复制进 Ops state 或 HANDOFF。
8
+
9
+ ## Operation Mode 与接管
10
+
11
+ - `audit`:只读盘点;不批准 mutation。
12
+ - `takeover`:已存在持久目录、控制面资源、数据、路由或服务时的默认模式;保持现场身份和命名。
13
+ - `fresh`:只在证据证明目标为空、归属清楚且 protected/unknown targets 为空时允许。
14
+ - `release`:向已确认目标发布不可变候选。
15
+ - `upgrade`:从已确认现役版本迁移到新候选。
16
+ - `rollback`:恢复已验证且兼容的目标或反向操作。
17
+
18
+ 现场为 `present` 或 `unknown` 时不得选择 fresh。差异分类为 `ownership-conflict` 或 `unknown` 时 profile 必须 blocked;不得通过改用 global scope、改名或宽泛命令绕过。
19
+
20
+ ## 控制面身份
21
+
22
+ 身份由一组不可拆分 assertions 表示。每项声明 provider、key、`exact | ordered-list | set | digest` 比较、非敏感 expected 和 evidence。Compose 的 project、有序 files、env file、services、labels/image identity 和 bind host 应分别进入 assertions;目录名、容器名前缀或单一文件不能独立证明身份。
23
+
24
+ mutation 模式要求整体 `identity_confirmed=true` 和确认 evidence。E 在第一条 mutation 前重采集 actual 并逐 assertion 比较;任何漂移使 approval 失效并返回 I/P。
25
+
26
+ ## 构件与提升
27
+
28
+ 发布构件使用不可变 SHA-256 或 provider 的不可变 digest/image ID。适用时计划必须声明授权 root 下的独立 staging、activation target 和 previous ref;传输后在目标端重算摘要,再原子切换活动指针。未验证候选不得进入 active 位置,可变 tag 不构成不可变身份。
29
+
30
+ 失败候选、previous release、rollback material 和脱敏日志默认保留。cleanup 不继承部署批准,只能由新的 plan batch 执行。
31
+
32
+ ## 数据保护
33
+
34
+ 每个 data mutation 映射到一个 `data_protection` 条目。默认策略为 verified backup:记录受控 evidence locator、摘要、可读性、restore ref 和恢复验证。production 不接受 waiver。
35
+
36
+ 只有 local 环境且用户针对精确对象明确批准时允许 waiver;必须同时记录 decision locator、exact scope、对象身份确认、零冲突 preflight 和 `forward-only` 恢复边界。执行器事务能力未知、备份不可读或恢复路径未验证时,在第一条持久写入前停止。
37
+
38
+ ## Gate 与验证
39
+
40
+ plan 的 required Gate 与 batch 形成组合 DAG:batch `gate_ids` 是启动该批次前必须 passed 的 Gate,可为空;Gate `after_batches` 是计算该 Gate 前必须完成的批次。E 只能在 batch 的全部 gate ids 已 passed 后开始该 batch;Gate failed/blocked 后不得执行新的 operation、提升活动指针或删除候选,后续 Gate只能标记 skipped。
41
+
42
+ 每个 required verification id 必须由 Gate 引用并在 verification state 有唯一结果。HTTP 探针独立声明允许状态、允许业务码和认证要求;稳定窗口声明 interval、timeout 和连续成功数。瞬时失败可以记录,但连续成功不足或超时仍失败。多实例收敛通过显式 convergence group 比较不可变构件和运行配置摘要。
43
+
44
+ 成功、回滚或作为 completed 依据的 abandoned attempt 必须生成 verification state 和无密钥 handoff,并证明 required Gates、数据保护、恢复资产和接受的残余风险。
@@ -0,0 +1,28 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:speculo:ops:approval:v2",
4
+ "title": "Ops Plan Approval",
5
+ "type": "object",
6
+ "required": ["schema_version", "artifact", "scope", "project_id", "approval_id", "change", "plan_path", "plan_digest", "source_revision", "target_fingerprint", "approved_batches", "excluded_batches", "confirmed_global_environment_keys", "conditions", "decision", "decision_summary", "decided_at", "expires_at"],
7
+ "properties": {
8
+ "schema_version": {"const": 2},
9
+ "artifact": {"const": "ops-plan-approval"},
10
+ "scope": {"enum": ["global", "project"]},
11
+ "project_id": {"type": ["string", "null"], "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"},
12
+ "approval_id": {"type": "string", "pattern": "^APPROVAL-[0-9]{3}$"},
13
+ "change": {"type": "string"},
14
+ "plan_path": {"type": "string", "pattern": "^plan/plan-[0-9]{3}\\.json$"},
15
+ "plan_digest": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
16
+ "source_revision": {"type": "string", "minLength": 1},
17
+ "target_fingerprint": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
18
+ "approved_batches": {"type": "array", "items": {"type": "string", "pattern": "^B[0-9]{2}$"}, "minItems": 1, "uniqueItems": true},
19
+ "excluded_batches": {"type": "array", "items": {"type": "string", "pattern": "^B[0-9]{2}$"}, "uniqueItems": true},
20
+ "confirmed_global_environment_keys": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
21
+ "conditions": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
22
+ "decision": {"const": "approved"},
23
+ "decision_summary": {"type": "string", "minLength": 1},
24
+ "decided_at": {"type": "string", "format": "date-time"},
25
+ "expires_at": {"type": "string", "format": "date-time"}
26
+ },
27
+ "additionalProperties": false
28
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:speculo:ops:attempt:v2",
4
+ "title": "Ops Execution Attempt",
5
+ "type": "object",
6
+ "required": ["schema_version", "artifact", "scope", "project_id", "change", "attempt_id", "kind", "triggered_by_attempt", "plan_path", "plan_digest", "approval_path", "target_profile_path", "target_profile_digest", "started_at", "ended_at", "result", "executed_batches", "failed_operation", "mutation_performed", "journal_path", "diagnosis_path", "verification_path", "verification_state_path", "verification_state_digest", "handoff_path", "retained_artifacts", "blockers"],
7
+ "properties": {
8
+ "schema_version": {"const": 2},
9
+ "artifact": {"const": "ops-execution-attempt"},
10
+ "scope": {"enum": ["global", "project"]},
11
+ "project_id": {"type": ["string", "null"], "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"},
12
+ "change": {"type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9]+(?:-[a-z0-9]+)*$"},
13
+ "attempt_id": {"type": "string", "pattern": "^ATTEMPT-[0-9]{3}$"},
14
+ "kind": {"enum": ["deploy", "remediation", "rollback", "verification-only"]},
15
+ "triggered_by_attempt": {"type": ["string", "null"], "pattern": "^ATTEMPT-[0-9]{3}$"},
16
+ "plan_path": {"anyOf": [{"type": "null"}, {"type": "string", "pattern": "^plan/plan-[0-9]{3}\\.json$"}]},
17
+ "plan_digest": {"type": ["string", "null"], "pattern": "^[a-f0-9]{64}$"},
18
+ "approval_path": {"anyOf": [{"type": "null"}, {"type": "string", "pattern": "^plan/approval-[0-9]{3}\\.json$"}]},
19
+ "target_profile_path": {"type": ["string", "null"], "pattern": "^deployment/target-profile\\.json$"},
20
+ "target_profile_digest": {"type": ["string", "null"], "pattern": "^[a-f0-9]{64}$"},
21
+ "started_at": {"type": "string", "format": "date-time"},
22
+ "ended_at": {"type": ["string", "null"], "format": "date-time"},
23
+ "result": {"enum": ["running", "succeeded", "failed", "blocked", "rolled_back", "abandoned"]},
24
+ "executed_batches": {"type": "array", "items": {"type": "string", "pattern": "^B[0-9]{2}$"}, "uniqueItems": true},
25
+ "failed_operation": {"type": ["string", "null"], "pattern": "^OP[0-9]{3}$"},
26
+ "mutation_performed": {"type": "boolean"},
27
+ "journal_path": {"type": "string", "pattern": "^execution/attempts/ATTEMPT-[0-9]{3}/journal\\.jsonl$"},
28
+ "diagnosis_path": {"type": ["string", "null"], "pattern": "^execution/attempts/ATTEMPT-[0-9]{3}/diagnosis\\.md$"},
29
+ "verification_path": {"type": ["string", "null"], "pattern": "^execution/attempts/ATTEMPT-[0-9]{3}/verification\\.md$"},
30
+ "verification_state_path": {"type": ["string", "null"], "pattern": "^execution/attempts/ATTEMPT-[0-9]{3}/verification-state\\.json$"},
31
+ "verification_state_digest": {"type": ["string", "null"], "pattern": "^[a-f0-9]{64}$"},
32
+ "handoff_path": {"type": ["string", "null"], "pattern": "^execution/attempts/ATTEMPT-[0-9]{3}/HANDOFF\\.md$"},
33
+ "retained_artifacts": {"type": "array", "items": {"type": "object", "required": ["id", "kind", "locator", "reason"], "properties": {"id": {"type": "string", "minLength": 1}, "kind": {"type": "string", "minLength": 1}, "locator": {"type": "string", "minLength": 1}, "reason": {"type": "string", "minLength": 1}}, "additionalProperties": false}},
34
+ "blockers": {"type": "array", "items": {"type": "string", "minLength": 1}, "uniqueItems": true}
35
+ },
36
+ "allOf": [
37
+ {"if": {"properties": {"scope": {"const": "global"}}}, "then": {"properties": {"project_id": {"const": null}}}},
38
+ {"if": {"properties": {"scope": {"const": "project"}}}, "then": {"properties": {"project_id": {"type": "string"}}}},
39
+ {"if": {"properties": {"kind": {"not": {"const": "verification-only"}}}}, "then": {"properties": {"plan_path": {"type": "string"}, "plan_digest": {"type": "string"}, "approval_path": {"type": "string"}, "target_profile_path": {"type": "string"}, "target_profile_digest": {"type": "string"}}}}
40
+ ],
41
+ "additionalProperties": false
42
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:speculo:ops:change-status:v2",
4
+ "title": "Ops Change Status",
5
+ "type": "object",
6
+ "required": [
7
+ "schema_version", "artifact", "scope", "project_id", "change", "change_status", "phase",
8
+ "current_work", "works_run", "created_at", "updated_at", "completed_at", "archived_at",
9
+ "archive_path", "source_revision", "target_fingerprint", "plan_path", "plan_digest",
10
+ "approval_path", "approval_status", "approved_batches", "latest_attempt_id", "outcome", "blockers"
11
+ ],
12
+ "properties": {
13
+ "schema_version": {"const": 2},
14
+ "artifact": {"const": "ops-change-status"},
15
+ "scope": {"enum": ["global", "project"]},
16
+ "project_id": {"type": ["string", "null"], "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"},
17
+ "change": {"type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9]+(?:-[a-z0-9]+)*$"},
18
+ "change_status": {"enum": ["active", "blocked", "completed", "archived"]},
19
+ "phase": {"enum": ["intake", "assessment", "planning", "awaiting_approval", "approved", "executing", "diagnosing", "stabilizing", "ready_to_archive", "archived"]},
20
+ "current_work": {"anyOf": [{"type": "null"}, {"enum": ["ops/archive-and-learn", "ops/execute-and-stabilize", "ops/intake-and-assess", "ops/plan-and-approve"]}]},
21
+ "works_run": {"type": "array", "items": {"enum": ["ops/archive-and-learn", "ops/execute-and-stabilize", "ops/intake-and-assess", "ops/plan-and-approve"]}, "uniqueItems": true},
22
+ "created_at": {"type": "string", "format": "date-time"},
23
+ "updated_at": {"type": "string", "format": "date-time"},
24
+ "completed_at": {"type": ["string", "null"], "format": "date-time"},
25
+ "archived_at": {"type": ["string", "null"], "format": "date-time"},
26
+ "archive_path": {"type": ["string", "null"]},
27
+ "source_revision": {"type": ["string", "null"], "minLength": 1},
28
+ "target_fingerprint": {"type": ["string", "null"], "pattern": "^[a-f0-9]{64}$"},
29
+ "plan_path": {"anyOf": [{"type": "null"}, {"type": "string", "pattern": "^plan/plan-[0-9]{3}\\.json$"}]},
30
+ "plan_digest": {"type": ["string", "null"], "pattern": "^[a-f0-9]{64}$"},
31
+ "approval_path": {"anyOf": [{"type": "null"}, {"type": "string", "pattern": "^plan/approval-[0-9]{3}\\.json$"}]},
32
+ "approval_status": {"enum": ["not_requested", "pending", "approved", "invalidated"]},
33
+ "approved_batches": {"type": "array", "items": {"type": "string", "pattern": "^B[0-9]{2}$"}, "uniqueItems": true},
34
+ "latest_attempt_id": {"type": ["string", "null"], "pattern": "^ATTEMPT-[0-9]{3}$"},
35
+ "outcome": {"enum": ["pending", "succeeded", "rolled_back", "abandoned"]},
36
+ "blockers": {"type": "array", "items": {"type": "string", "minLength": 1}, "uniqueItems": true}
37
+ },
38
+ "allOf": [
39
+ {"if": {"properties": {"scope": {"const": "global"}}}, "then": {"properties": {"project_id": {"const": null}}}},
40
+ {"if": {"properties": {"scope": {"const": "project"}}}, "then": {"properties": {"project_id": {"type": "string"}}}}
41
+ ],
42
+ "additionalProperties": false
43
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:speculo:ops:deployment-model:v2",
4
+ "title": "Ops Deployment Model",
5
+ "type": "object",
6
+ "required": ["schema_version", "artifact", "scope", "project_id", "change", "generated_at", "project", "components", "dependencies", "configuration", "data", "deployment_options", "selected_method", "health_checks", "unknowns", "readiness"],
7
+ "properties": {
8
+ "schema_version": {"const": 2},
9
+ "artifact": {"const": "ops-deployment-model"},
10
+ "scope": {"enum": ["global", "project"]},
11
+ "project_id": {"type": ["string", "null"], "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"},
12
+ "change": {"type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9]+(?:-[a-z0-9]+)*$"},
13
+ "generated_at": {"type": "string", "format": "date-time"},
14
+ "project": {"anyOf": [{"type": "null"}, {"type": "object", "required": ["source_root", "source_revision", "languages", "build", "start"], "properties": {"source_root": {"type": "string", "minLength": 1}, "source_revision": {"type": "string", "minLength": 1}, "languages": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "build": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "start": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}}, "additionalProperties": false}]},
15
+ "components": {"type": "array", "items": {"type": "object", "required": ["id", "kind", "source"], "properties": {"id": {"type": "string"}, "kind": {"type": "string"}, "source": {"type": "string"}}, "additionalProperties": false}},
16
+ "dependencies": {"type": "array", "items": {"type": "object", "required": ["id", "kind", "required", "source", "target_disposition"], "properties": {"id": {"type": "string"}, "kind": {"type": "string"}, "required": {"type": "boolean"}, "source": {"type": "string"}, "target_disposition": {"enum": ["existing", "provision", "external", "unresolved"]}}, "additionalProperties": false}},
17
+ "configuration": {"type": "array", "items": {"type": "object", "required": ["key", "scope", "required", "secret", "source_ref"], "properties": {"key": {"type": "string", "minLength": 1}, "scope": {"enum": ["process", "service", "project", "container", "cluster", "global"]}, "required": {"type": "boolean"}, "secret": {"type": "boolean"}, "source_ref": {"type": ["string", "null"]}}, "additionalProperties": false}},
18
+ "data": {"type": "array", "items": {"type": "object", "required": ["id", "kind", "persistence", "migration"], "properties": {"id": {"type": "string"}, "kind": {"type": "string"}, "persistence": {"type": "string"}, "migration": {"type": ["string", "null"]}}, "additionalProperties": false}},
19
+ "deployment_options": {"type": "array", "items": {"type": "object", "required": ["id", "method", "evidence", "tradeoffs"], "properties": {"id": {"type": "string"}, "method": {"type": "string"}, "evidence": {"type": "array", "items": {"type": "string"}}, "tradeoffs": {"type": "array", "items": {"type": "string"}}}, "additionalProperties": false}},
20
+ "selected_method": {"type": ["string", "null"]},
21
+ "health_checks": {"type": "array", "items": {"type": "object", "required": ["id", "kind", "target", "success"], "properties": {"id": {"type": "string"}, "kind": {"type": "string"}, "target": {"type": "string"}, "success": {"type": "string"}}, "additionalProperties": false}},
22
+ "unknowns": {"type": "array", "items": {"type": "object", "required": ["id", "question", "blocking", "owner"], "properties": {"id": {"type": "string"}, "question": {"type": "string"}, "blocking": {"type": "boolean"}, "owner": {"type": "string"}}, "additionalProperties": false}},
23
+ "readiness": {"enum": ["blocked", "ready"]}
24
+ },
25
+ "additionalProperties": false
26
+ }
@@ -0,0 +1,221 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:speculo:ops:implementation-plan:v3",
4
+ "title": "Ops Implementation Plan",
5
+ "type": "object",
6
+ "required": ["schema_version", "artifact", "scope", "project_id", "plan_id", "change", "created_at", "supersedes_plan_path", "triggered_by_attempt", "depth", "status", "input_bindings", "scope_definition", "batches", "external_mutations", "global_environment_changes", "operations", "artifact_requirements", "gates", "data_protection", "retention_policy", "verification", "rollback_strategy", "blockers"],
7
+ "properties": {
8
+ "schema_version": {"const": 3},
9
+ "artifact": {"const": "ops-implementation-plan"},
10
+ "scope": {"enum": ["global", "project"]},
11
+ "project_id": {"type": ["string", "null"], "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"},
12
+ "plan_id": {"type": "string", "pattern": "^PLAN-[0-9]{3}$"},
13
+ "change": {"type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9]+(?:-[a-z0-9]+)*$"},
14
+ "created_at": {"type": "string", "format": "date-time"},
15
+ "supersedes_plan_path": {"anyOf": [{"type": "null"}, {"type": "string", "pattern": "^plan/plan-[0-9]{3}\\.json$"}]},
16
+ "triggered_by_attempt": {"type": ["string", "null"], "pattern": "^ATTEMPT-[0-9]{3}$"},
17
+ "depth": {"enum": ["lite", "standard", "deep"]},
18
+ "status": {"enum": ["blocked", "ready"]},
19
+ "input_bindings": {
20
+ "type": "object",
21
+ "required": ["request_path", "snapshot_path", "snapshot_digest", "deployment_model_path", "deployment_model_digest", "target_profile_path", "target_profile_digest", "source_revision", "target_fingerprint"],
22
+ "properties": {
23
+ "request_path": {"type": "string", "minLength": 1},
24
+ "snapshot_path": {"type": "string", "minLength": 1},
25
+ "snapshot_digest": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
26
+ "deployment_model_path": {"type": "string", "minLength": 1},
27
+ "deployment_model_digest": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
28
+ "target_profile_path": {"type": "string", "pattern": "^deployment/target-profile\\.json$"},
29
+ "target_profile_digest": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
30
+ "source_revision": {"type": "string", "minLength": 1},
31
+ "target_fingerprint": {"type": "string", "pattern": "^[a-f0-9]{64}$"}
32
+ },
33
+ "additionalProperties": false
34
+ },
35
+ "scope_definition": {
36
+ "type": "object",
37
+ "required": ["source_root", "deployment_root", "read_roots", "write_roots", "forbidden_roots"],
38
+ "properties": {
39
+ "source_root": {"type": "string", "minLength": 1},
40
+ "deployment_root": {"type": "string", "minLength": 1},
41
+ "read_roots": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
42
+ "write_roots": {"type": "array", "items": {"type": "string"}, "minItems": 1, "uniqueItems": true},
43
+ "forbidden_roots": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}
44
+ },
45
+ "additionalProperties": false
46
+ },
47
+ "batches": {
48
+ "type": "array",
49
+ "items": {
50
+ "type": "object",
51
+ "required": ["id", "title", "risk", "requires_confirmation", "depends_on", "operation_ids", "gate_ids"],
52
+ "properties": {
53
+ "id": {"type": "string", "pattern": "^B[0-9]{2}$"},
54
+ "title": {"type": "string", "minLength": 1},
55
+ "risk": {"enum": ["low", "medium", "high", "critical"]},
56
+ "requires_confirmation": {"const": true},
57
+ "depends_on": {"type": "array", "items": {"type": "string", "pattern": "^B[0-9]{2}$"}, "uniqueItems": true},
58
+ "operation_ids": {"type": "array", "items": {"type": "string", "pattern": "^OP[0-9]{3}$"}, "minItems": 1, "uniqueItems": true},
59
+ "gate_ids": {"type": "array", "items": {"type": "string", "pattern": "^G[0-9]{2}$"}, "uniqueItems": true}
60
+ },
61
+ "additionalProperties": false
62
+ }
63
+ },
64
+ "external_mutations": {
65
+ "type": "array",
66
+ "items": {
67
+ "type": "object",
68
+ "required": ["id", "provider", "target", "current_state", "desired_state", "privilege", "blast_radius", "preview", "rollback", "batch_id"],
69
+ "properties": {
70
+ "id": {"type": "string", "pattern": "^EM[0-9]{3}$"},
71
+ "provider": {"type": "string"},
72
+ "target": {"type": "string"},
73
+ "current_state": {"type": "string"},
74
+ "desired_state": {"type": "string"},
75
+ "privilege": {"type": "string"},
76
+ "blast_radius": {"type": "string"},
77
+ "preview": {"type": "string"},
78
+ "rollback": {"type": "string"},
79
+ "batch_id": {"type": "string", "pattern": "^B[0-9]{2}$"}
80
+ },
81
+ "additionalProperties": false
82
+ }
83
+ },
84
+ "global_environment_changes": {
85
+ "type": "array",
86
+ "items": {
87
+ "type": "object",
88
+ "required": ["key", "target_scope", "value_source_ref", "impact", "rollback", "batch_id"],
89
+ "properties": {
90
+ "key": {"type": "string"},
91
+ "target_scope": {"type": "string"},
92
+ "value_source_ref": {"type": "string"},
93
+ "impact": {"type": "string"},
94
+ "rollback": {"type": "string"},
95
+ "batch_id": {"type": "string", "pattern": "^B[0-9]{2}$"}
96
+ },
97
+ "additionalProperties": false
98
+ }
99
+ },
100
+ "operations": {
101
+ "type": "array",
102
+ "items": {
103
+ "type": "object",
104
+ "required": ["id", "batch_id", "kind", "target", "working_directory", "preconditions", "write_set", "external_mutation_id", "preview", "apply", "postconditions", "rollback", "risk", "required_privilege", "evidence_path"],
105
+ "properties": {
106
+ "id": {"type": "string", "pattern": "^OP[0-9]{3}$"},
107
+ "batch_id": {"type": "string", "pattern": "^B[0-9]{2}$"},
108
+ "kind": {"enum": ["filesystem", "docker-compose", "systemd", "kubernetes", "helm", "terraform", "ansible", "database", "custom-command"]},
109
+ "target": {"type": "string"},
110
+ "working_directory": {"type": "string"},
111
+ "preconditions": {"type": "array", "items": {"type": "string"}, "minItems": 1},
112
+ "write_set": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
113
+ "external_mutation_id": {"type": ["string", "null"], "pattern": "^EM[0-9]{3}$"},
114
+ "preview": {"type": "object", "required": ["supported", "command", "limitations"], "properties": {"supported": {"type": "boolean"}, "command": {"type": ["string", "null"]}, "limitations": {"type": "array", "items": {"type": "string"}}}, "additionalProperties": false},
115
+ "apply": {"type": "object", "required": ["shell", "command"], "properties": {"shell": {"type": "string"}, "command": {"type": "string"}}, "additionalProperties": false},
116
+ "postconditions": {"type": "array", "items": {"type": "string"}, "minItems": 1},
117
+ "rollback": {"type": "object", "required": ["mode", "trigger", "command"], "properties": {"mode": {"enum": ["automatic", "manual", "not-available"]}, "trigger": {"type": "string"}, "command": {"type": ["string", "null"]}}, "additionalProperties": false},
118
+ "risk": {"enum": ["low", "medium", "high", "critical"]},
119
+ "required_privilege": {"type": "string"},
120
+ "evidence_path": {"type": "string"}
121
+ },
122
+ "additionalProperties": false
123
+ }
124
+ },
125
+ "artifact_requirements": {
126
+ "type": "array",
127
+ "items": {
128
+ "type": "object",
129
+ "required": ["id", "kind", "source_ref", "immutable_digest", "staging_ref", "activation_target", "previous_ref", "required"],
130
+ "properties": {
131
+ "id": {"type": "string", "pattern": "^AR[0-9]{3}$"},
132
+ "kind": {"enum": ["file", "archive", "container-image", "static-bundle", "other"]},
133
+ "source_ref": {"type": "string", "minLength": 1},
134
+ "immutable_digest": {"type": "string", "pattern": "^(?:sha256:)?[a-f0-9]{64}$"},
135
+ "staging_ref": {"type": ["string", "null"]},
136
+ "activation_target": {"type": "string", "minLength": 1},
137
+ "previous_ref": {"type": ["string", "null"]},
138
+ "required": {"type": "boolean"}
139
+ },
140
+ "additionalProperties": false
141
+ }
142
+ },
143
+ "gates": {
144
+ "type": "array",
145
+ "items": {
146
+ "type": "object",
147
+ "required": ["id", "title", "depends_on", "after_batches", "verification_ids", "required"],
148
+ "properties": {
149
+ "id": {"type": "string", "pattern": "^G[0-9]{2}$"},
150
+ "title": {"type": "string", "minLength": 1},
151
+ "depends_on": {"type": "array", "items": {"type": "string", "pattern": "^G[0-9]{2}$"}, "uniqueItems": true},
152
+ "after_batches": {"type": "array", "items": {"type": "string", "pattern": "^B[0-9]{2}$"}, "minItems": 1, "uniqueItems": true},
153
+ "verification_ids": {"type": "array", "items": {"type": "string", "pattern": "^VR[0-9]{3}$"}, "minItems": 1, "uniqueItems": true},
154
+ "required": {"type": "boolean"}
155
+ },
156
+ "additionalProperties": false
157
+ },
158
+ "minItems": 1
159
+ },
160
+ "data_protection": {
161
+ "type": "array",
162
+ "items": {
163
+ "type": "object",
164
+ "required": ["id", "resource", "operation_ids", "strategy", "backup", "waiver"],
165
+ "properties": {
166
+ "id": {"type": "string", "pattern": "^DP[0-9]{3}$"},
167
+ "resource": {"type": "string", "minLength": 1},
168
+ "operation_ids": {"type": "array", "items": {"type": "string", "pattern": "^OP[0-9]{3}$"}, "uniqueItems": true},
169
+ "strategy": {"enum": ["verified-backup", "waiver", "not-required"]},
170
+ "backup": {
171
+ "anyOf": [
172
+ {"type": "null"},
173
+ {"type": "object", "required": ["evidence_path", "digest", "readability_verified", "restore_ref", "restore_verified"], "properties": {"evidence_path": {"type": "string", "minLength": 1}, "digest": {"type": "string", "pattern": "^[a-f0-9]{64}$"}, "readability_verified": {"const": true}, "restore_ref": {"type": "string", "minLength": 1}, "restore_verified": {"type": "boolean"}}, "additionalProperties": false}
174
+ ]
175
+ },
176
+ "waiver": {
177
+ "anyOf": [
178
+ {"type": "null"},
179
+ {"type": "object", "required": ["decision_ref", "exact_scope", "object_identity_verified", "conflicts_absent", "recovery_mode"], "properties": {"decision_ref": {"type": "string", "minLength": 1}, "exact_scope": {"type": "string", "minLength": 1}, "object_identity_verified": {"const": true}, "conflicts_absent": {"const": true}, "recovery_mode": {"const": "forward-only"}}, "additionalProperties": false}
180
+ ]
181
+ }
182
+ },
183
+ "additionalProperties": false
184
+ }
185
+ },
186
+ "retention_policy": {
187
+ "type": "object",
188
+ "required": ["retain_failed_candidates", "retain_previous_release", "cleanup_requires_separate_approval"],
189
+ "properties": {
190
+ "retain_failed_candidates": {"const": true},
191
+ "retain_previous_release": {"const": true},
192
+ "cleanup_requires_separate_approval": {"const": true}
193
+ },
194
+ "additionalProperties": false
195
+ },
196
+ "verification": {
197
+ "type": "array",
198
+ "items": {
199
+ "type": "object",
200
+ "required": ["id", "kind", "target", "required", "expected", "stability", "convergence_group"],
201
+ "properties": {
202
+ "id": {"type": "string", "pattern": "^VR[0-9]{3}$"},
203
+ "kind": {"enum": ["http", "tcp", "command", "service", "artifact", "data", "custom"]},
204
+ "target": {"type": "string", "minLength": 1},
205
+ "required": {"type": "boolean"},
206
+ "expected": {"type": "object", "required": ["description", "http_statuses", "business_codes", "authenticated"], "properties": {"description": {"type": "string", "minLength": 1}, "http_statuses": {"type": "array", "items": {"type": "integer", "minimum": 100, "maximum": 599}, "uniqueItems": true}, "business_codes": {"type": "array", "items": {"type": ["integer", "string"]}, "uniqueItems": true}, "authenticated": {"type": ["boolean", "null"]}}, "additionalProperties": false},
207
+ "stability": {"anyOf": [{"type": "null"}, {"type": "object", "required": ["interval_seconds", "timeout_seconds", "required_consecutive_successes"], "properties": {"interval_seconds": {"type": "number", "exclusiveMinimum": 0}, "timeout_seconds": {"type": "number", "exclusiveMinimum": 0}, "required_consecutive_successes": {"type": "integer", "minimum": 1}}, "additionalProperties": false}]},
208
+ "convergence_group": {"type": ["string", "null"]}
209
+ },
210
+ "additionalProperties": false
211
+ }
212
+ },
213
+ "rollback_strategy": {"type": "string", "minLength": 1},
214
+ "blockers": {"type": "array", "items": {"type": "string", "minLength": 1}, "uniqueItems": true}
215
+ },
216
+ "allOf": [
217
+ {"if": {"properties": {"scope": {"const": "global"}}}, "then": {"properties": {"project_id": {"const": null}}}},
218
+ {"if": {"properties": {"scope": {"const": "project"}}}, "then": {"properties": {"project_id": {"type": "string"}}}}
219
+ ],
220
+ "additionalProperties": false
221
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:speculo:ops:inventory-snapshot:v2",
4
+ "title": "Ops Inventory Snapshot",
5
+ "type": "object",
6
+ "required": ["schema_version", "artifact", "scope", "project_id", "snapshot_id", "change", "captured_at", "scope_definition", "target_fingerprint", "collectors", "gaps", "redactions"],
7
+ "properties": {
8
+ "schema_version": {"const": 2},
9
+ "artifact": {"const": "ops-inventory-snapshot"},
10
+ "scope": {"enum": ["global", "project"]},
11
+ "project_id": {"type": ["string", "null"], "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"},
12
+ "snapshot_id": {"type": "string", "pattern": "^[0-9]{8}T[0-9]{6}Z(?:-[0-9]{2})?$"},
13
+ "change": {"type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9]+(?:-[a-z0-9]+)*$"},
14
+ "captured_at": {"type": "string", "format": "date-time"},
15
+ "scope_definition": {
16
+ "type": "object",
17
+ "required": ["levels", "targets", "approved_deep_roots"],
18
+ "properties": {
19
+ "levels": {"type": "array", "items": {"enum": ["L0", "L1", "L2"]}, "uniqueItems": true, "minItems": 1},
20
+ "targets": {"type": "array", "items": {"type": "string", "minLength": 1}, "minItems": 1, "uniqueItems": true},
21
+ "approved_deep_roots": {"type": "array", "items": {"type": "string", "minLength": 1}, "uniqueItems": true}
22
+ },
23
+ "additionalProperties": false
24
+ },
25
+ "target_fingerprint": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
26
+ "collectors": {"type": "array", "items": {"type": "object", "required": ["id", "category", "tool", "target", "status", "facts", "evidence", "error"], "properties": {"id": {"type": "string"}, "category": {"enum": ["host", "filesystem", "runtime", "service", "network", "container", "cluster", "project-location"]}, "tool": {"type": "string"}, "target": {"type": "string"}, "status": {"enum": ["observed", "partial", "unavailable", "denied", "failed"]}, "facts": {"type": "object", "additionalProperties": true}, "evidence": {"type": "array", "items": {"type": "string"}}, "error": {"type": ["string", "null"]}}, "additionalProperties": false}, "minItems": 1},
27
+ "gaps": {"type": "array", "items": {"type": "string", "minLength": 1}, "uniqueItems": true},
28
+ "redactions": {"type": "array", "items": {"type": "string", "minLength": 1}, "uniqueItems": true}
29
+ },
30
+ "additionalProperties": false
31
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:speculo:ops:journal-event:v1",
4
+ "title": "Ops Attempt Journal Event",
5
+ "type": "object",
6
+ "required": ["schema_version", "artifact", "sequence", "at", "event", "batch_id", "gate_id", "operation_id", "result", "summary", "output_digest", "evidence"],
7
+ "properties": {
8
+ "schema_version": {"const": 1},
9
+ "artifact": {"const": "ops-journal-event"},
10
+ "sequence": {"type": "integer", "minimum": 1},
11
+ "at": {"type": "string", "format": "date-time"},
12
+ "event": {"enum": ["attempt-start", "gate-result", "operation-intent", "operation-result", "postcondition-result", "attempt-end"]},
13
+ "batch_id": {"type": ["string", "null"], "pattern": "^B[0-9]{2}$"},
14
+ "gate_id": {"type": ["string", "null"], "pattern": "^G[0-9]{2}$"},
15
+ "operation_id": {"type": ["string", "null"], "pattern": "^OP[0-9]{3}$"},
16
+ "result": {"enum": ["pending", "passed", "failed", "blocked", "skipped"]},
17
+ "summary": {"type": "string", "minLength": 1},
18
+ "output_digest": {"type": ["string", "null"], "pattern": "^[a-f0-9]{64}$"},
19
+ "evidence": {"type": "array", "items": {"type": "string", "minLength": 1}, "uniqueItems": true}
20
+ },
21
+ "additionalProperties": false
22
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:speculo:ops:project:v1",
4
+ "title": "Ops Project Identity",
5
+ "type": "object",
6
+ "required": ["schema_version", "artifact", "project_id", "display_name", "aliases", "identities", "source_hints", "created_at", "updated_at"],
7
+ "properties": {
8
+ "schema_version": {"const": 1},
9
+ "artifact": {"const": "ops-project"},
10
+ "project_id": {"type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"},
11
+ "display_name": {"type": "string", "minLength": 1},
12
+ "aliases": {"type": "array", "items": {"type": "string", "minLength": 1}, "uniqueItems": true},
13
+ "identities": {"type": "array", "items": {"type": "object", "required": ["kind", "value", "source"], "properties": {"kind": {"enum": ["vcs", "monorepo-component", "workspace", "package", "user-confirmed", "digest"]}, "value": {"type": "string", "minLength": 1}, "source": {"type": "string", "minLength": 1}}, "additionalProperties": false}, "minItems": 1},
14
+ "source_hints": {"type": "array", "items": {"type": "string", "minLength": 1}, "uniqueItems": true},
15
+ "created_at": {"type": "string", "format": "date-time"},
16
+ "updated_at": {"type": "string", "format": "date-time"}
17
+ },
18
+ "additionalProperties": false
19
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:speculo:ops:promotion-approval:v1",
4
+ "title": "Ops Promotion Approval",
5
+ "type": "object",
6
+ "required": ["schema_version", "artifact", "scope", "project_id", "change", "manifest_path", "manifest_digest", "decision", "decision_summary", "decided_at"],
7
+ "properties": {
8
+ "schema_version": {"const": 1},
9
+ "artifact": {"const": "ops-promotion-approval"},
10
+ "scope": {"enum": ["global", "project"]},
11
+ "project_id": {"type": ["string", "null"], "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"},
12
+ "change": {"type": "string"},
13
+ "manifest_path": {"const": "promotion/manifest.json"},
14
+ "manifest_digest": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
15
+ "decision": {"const": "approved"},
16
+ "decision_summary": {"type": "string", "minLength": 1},
17
+ "decided_at": {"type": "string", "format": "date-time"}
18
+ },
19
+ "additionalProperties": false
20
+ }