mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-09 11:51:37 -10:00
fix(slice-05): v2 workflow_specs validation + serialization tests (guard B1 repair on 4f50d127) (OPR.0.3.2.5)
Per velocity-guard BLOCKING-CONCERN on4f50d127(qitem-…040fc9ed): the type-extension commit wired workflow_specs into both v1 (LegacyBundleManifest) and v2 (PodBundleManifest) schemas but only added v1-path tests. v2 changes were claimed but not exercised. Fix: add v2-path tests mirroring the 0c13c7c6-skills and 5aec0ef5-plugins v2 test patterns in pod-bundle- assembler.test.ts. Adds 6 v2 tests (test-only, no product source change): - v2: missing workflow_specs block passes validation (backward compat) - v2: workflow_specs as string array passes validation - v2: workflow_specs as non-array rejected - v2: unsafe workflow_specs path rejected (dot-dot traversal) - v2: round-trip preserves workflow_specs through serialize -> parse - v2: round-trip preserves all 3 cross-primitive blocks together (skills + plugins + workflow_specs co-exist cleanly in the same manifest) Discriminator-flip evidence BEFORE this commit: temporarily commented out the validateWorkflowSpecsBlock(m["workflow_specs"], errors) call in validatePodBundleManifest. Result: 2/51 fail in pod-bundle-assembler.test.ts — exactly the v2 non-array-rejected + v2 unsafe-path-rejected tests, which depend on the v2 validator firing. The 4 positive v2 tests (missing / valid / round-trip / co-existence) stay green. This proves the new v2 negative tests SPECIFICALLY catch the validator-wiring regression that 4f50d127's v1-only tests would have silently missed. Restored, 100/100 green across bundle-types + pod-bundle-assembler. Product source byte-identical to4f50d127(only the v2 validator call site was touched and immediately restored; nothing staged for bundle- types.ts in this repair). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
1 parent
4f50d12735
commit
da286abddd
1 file changed
+92
@@ -910,6 +910,98 @@ describe("PodBundleManifest validation", () => {
|
||||
expect(m["skills"]).toEqual(["skills/v2-foo/SKILL.md", "skills/v2-bar/SKILL.md"]);
|
||||
});
|
||||
|
||||
// -- Item 6 workflow_specs block tests for v2 (slice-05 Checkpoint 7.3e) --
|
||||
|
||||
it("v2: missing workflow_specs block passes validation (backward compat)", () => {
|
||||
const raw = {
|
||||
schema_version: 2, name: "no-workflow-specs", version: "1.0",
|
||||
created_at: "2026-05-18T00:00:00Z", rig_spec: "rig.yaml",
|
||||
agents: [{ name: "impl", version: "1.0", path: "agents/impl", original_ref: "local:agents/impl", hash: "abc", import_entries: [] }],
|
||||
};
|
||||
const result = validatePodBundleManifest(raw);
|
||||
expect(result.valid).toBe(true);
|
||||
});
|
||||
|
||||
it("v2: workflow_specs as string array passes validation", () => {
|
||||
const raw = {
|
||||
schema_version: 2, name: "with-workflow-specs", version: "1.0",
|
||||
created_at: "2026-05-18T00:00:00Z", rig_spec: "rig.yaml",
|
||||
agents: [{ name: "impl", version: "1.0", path: "agents/impl", original_ref: "local:agents/impl", hash: "abc", import_entries: [] }],
|
||||
workflow_specs: ["workflows/onboarding.yaml", "workflows/release.yaml"],
|
||||
};
|
||||
const result = validatePodBundleManifest(raw);
|
||||
expect(result.valid).toBe(true);
|
||||
});
|
||||
|
||||
it("v2: workflow_specs as non-array rejected", () => {
|
||||
const raw = {
|
||||
schema_version: 2, name: "bad-workflow-specs", version: "1.0",
|
||||
created_at: "2026-05-18T00:00:00Z", rig_spec: "rig.yaml",
|
||||
agents: [{ name: "impl", version: "1.0", path: "agents/impl", original_ref: "local:agents/impl", hash: "abc", import_entries: [] }],
|
||||
workflow_specs: "workflows/a.yaml",
|
||||
};
|
||||
const result = validatePodBundleManifest(raw);
|
||||
expect(result.valid).toBe(false);
|
||||
expect(result.errors.some((e) => e.includes("workflow_specs must be an array"))).toBe(true);
|
||||
});
|
||||
|
||||
it("v2: unsafe workflow_specs path rejected", () => {
|
||||
const raw = {
|
||||
schema_version: 2, name: "bad-workflow-specs", version: "1.0",
|
||||
created_at: "2026-05-18T00:00:00Z", rig_spec: "rig.yaml",
|
||||
agents: [{ name: "impl", version: "1.0", path: "agents/impl", original_ref: "local:agents/impl", hash: "abc", import_entries: [] }],
|
||||
workflow_specs: ["../escape/spec.yaml"],
|
||||
};
|
||||
const result = validatePodBundleManifest(raw);
|
||||
expect(result.valid).toBe(false);
|
||||
expect(result.errors.some((e) => e.includes("workflow_specs[0]") && e.includes("not safe"))).toBe(true);
|
||||
});
|
||||
|
||||
it("v2: round-trip preserves workflow_specs through serialize -> parse", () => {
|
||||
const manifest: PodBundleManifest = {
|
||||
schemaVersion: 2, name: "rt-workflow-specs-v2", version: "2.0",
|
||||
createdAt: "2026-05-18T00:00:00Z", rigSpec: "rig.yaml",
|
||||
agents: [{
|
||||
name: "impl", version: "1.0", path: "agents/impl",
|
||||
originalRef: "local:agents/impl", hash: "def",
|
||||
importEntries: [],
|
||||
}],
|
||||
workflowSpecs: ["workflows/v2-onboarding.yaml", "workflows/v2-release.yaml"],
|
||||
};
|
||||
const yaml = serializePodBundleManifest(manifest);
|
||||
expect(yaml).toContain("workflow_specs:");
|
||||
expect(yaml).toContain("workflows/v2-onboarding.yaml");
|
||||
const parsed = parsePodBundleManifest(yaml);
|
||||
const validation = validatePodBundleManifest(parsed);
|
||||
expect(validation.valid).toBe(true);
|
||||
const m = parsed as Record<string, unknown>;
|
||||
expect(m["workflow_specs"]).toEqual(["workflows/v2-onboarding.yaml", "workflows/v2-release.yaml"]);
|
||||
});
|
||||
|
||||
it("v2: round-trip preserves all 3 cross-primitive blocks together (skills + plugins + workflow_specs)", () => {
|
||||
const manifest: PodBundleManifest = {
|
||||
schemaVersion: 2, name: "rt-all-three", version: "2.0",
|
||||
createdAt: "2026-05-18T00:00:00Z", rigSpec: "rig.yaml",
|
||||
agents: [{
|
||||
name: "impl", version: "1.0", path: "agents/impl",
|
||||
originalRef: "local:agents/impl", hash: "def",
|
||||
importEntries: [],
|
||||
}],
|
||||
skills: ["skills/co-test/SKILL.md"],
|
||||
plugins: [{ id: "co-plugin", source: { kind: "local", path: "plugins/co-plugin" } }],
|
||||
workflowSpecs: ["workflows/co-flow.yaml"],
|
||||
};
|
||||
const yaml = serializePodBundleManifest(manifest);
|
||||
const parsed = parsePodBundleManifest(yaml);
|
||||
const validation = validatePodBundleManifest(parsed);
|
||||
expect(validation.valid).toBe(true);
|
||||
const m = parsed as Record<string, unknown>;
|
||||
expect(m["skills"]).toEqual(["skills/co-test/SKILL.md"]);
|
||||
expect(m["workflow_specs"]).toEqual(["workflows/co-flow.yaml"]);
|
||||
const ps = m["plugins"] as Array<Record<string, unknown>>;
|
||||
expect(ps[0]!["id"]).toBe("co-plugin");
|
||||
});
|
||||
|
||||
it("v2: round-trip preserves compatibility through serialize -> parse", () => {
|
||||
const manifest: PodBundleManifest = {
|
||||
schemaVersion: 2, name: "rt-compat", version: "2.0",
|
||||
|
||||
Reference in new issue
Block a user