Implementation Plan: CascadeGuard CLI Improvements

Overview

Implement config inheritance via .cascadeguard.yaml defaults, update validation to use merged defaults, add images generate and ci generate CLI commands, and update the open-secure-images config file. All changes are in Python, targeting repos/cascadeguard/app/app.py, repos/cascadeguard/app/generate_ci.py, and repos/cascadeguard-open-secure-images/.cascadeguard.yaml.

Tasks

  • 1. Implement merge_defaults() and update load_config() in app.py

    • 1.1 Add merge_defaults(images, config) function to app.py

      • Implement shallow merge of defaults.registry, defaults.repository, and defaults.local.dir from config onto each image dict
      • Return a new list without mutating the original image list or its dicts
      • If config has no defaults section, return shallow copies of originals unchanged
      • Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9
    • 1.2 Update load_config() in app.py to handle malformed YAML

      • Add error handling for YAML parse errors (print to stderr, return exit code 1)
      • Add check that root value is a dict (print to stderr, return exit code 1)
      • Silently ignore unknown keys for forward compatibility
      • Requirements: 1.1, 1.2, 1.3, 1.4, 1.5
    • [ ]* 1.3 Write property tests for merge_defaults()

      • Property 1: Default inheritance — if image lacks a key and config defaults provide it, merged image has the default value
      • Validates: Requirements 2.1, 2.2, 2.3
      • Property 2: Override precedence — if image already has a key, merged image retains the image’s original value
      • Validates: Requirements 2.4, 2.5, 2.6
      • Property 3: Non-mutation — calling merge_defaults does not modify the original image list or its dicts
      • Validates: Requirement 2.7
      • Property 4: Length preservation — merge_defaults returns a list of the same length as input
      • Validates: Requirement 2.8
      • Property 5: No-defaults backward compatibility — with no defaults section, returns copies equivalent to originals
      • Validates: Requirements 2.9, 7.1
  • 2. Update cmd_validate() to use config inheritance

    • 2.1 Modify cmd_validate() in app.py to load config and merge defaults before validation

      • Call load_config() with images_yaml.parent as repo root
      • Call merge_defaults() on loaded images before checking required fields
      • For enabled images: require name, registry, and dockerfile (not repository)
      • For disabled images (enabled: false): require only name
      • Include hint in error messages that missing fields can be set in .cascadeguard.yaml defaults
      • Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8
    • [ ]* 2.2 Write property tests for validation correctness

      • Property 6: Disabled image leniency — disabled images with name always pass validation regardless of other missing fields
      • Validates: Requirement 3.4
      • Property 7: Missing name always fails — any image without name fails validation
      • Validates: Requirement 3.5
      • Property 8: Validation correctness for enabled images — enabled images pass iff they have name, registry, and dockerfile after merge
      • Validates: Requirements 3.2, 3.3, 3.6, 3.7
  • 3. Checkpoint - Ensure all tests pass

    • Ensure all tests pass, ask the user if questions arise.
  • 4. Add images generate CLI command and handler

    • 4.1 Register generate subcommand under images in build_parser()

      • Add --output-dir (default .), --cache-dir (default None) flags
      • The --images-yaml flag is already on the images parent parser
      • Requirements: 6.1, 6.3, 6.5
    • 4.2 Implement cmd_images_generate(args) handler in app.py

      • Import generate_state.load_images_yaml, generate_state.generate_state_for_image, generate_state._generate_build_workflow
      • Load config, merge defaults, iterate images, call generate functions
      • Return 0 on success, 1 on failure; print summary of generated files
      • Requirements: 4.1, 4.2, 4.3, 4.4, 4.5
    • 4.3 Wire cmd_images_generate into the cmd_images dispatcher

      • Add "generate": cmd_images_generate to the dispatch dict in cmd_images()
      • Requirements: 4.1, 6.1
    • [ ]* 4.4 Write unit tests for images generate command

      • Test argument parsing for images generate --output-dir /tmp --cache-dir /tmp/cache
      • Test handler with mocked generate_state functions
      • Requirements: 4.1, 4.2, 4.3, 4.4, 4.5
  • 5. Add ci generate CLI command and handler

    • 5.1 Register ci top-level command with generate subcommand in build_parser()

      • Add --images-yaml (default images.yaml), --output-dir (default .), --platform (default None), --dry-run flags
      • Requirements: 6.2, 6.4, 6.6
    • 5.2 Implement cmd_ci_generate(args) handler in app.py

      • Import generate_ci.generate_ci
      • Load config, merge defaults, call generate_ci() with resolved platform
      • Return 0 on success, 1 on failure
      • Requirements: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8
    • 5.3 Add cmd_ci dispatcher and wire into main() commands dict

      • Add "ci": cmd_ci to the top-level commands dict in main()
      • Requirements: 5.1, 6.2
    • [ ]* 5.4 Write unit tests for ci generate command

      • Test argument parsing for ci generate --platform github --dry-run
      • Test handler with mocked generate_ci module
      • Requirements: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8
  • 6. Checkpoint - Ensure all tests pass

    • Ensure all tests pass, ask the user if questions arise.
  • 7. Update .cascadeguard.yaml in open-secure-images and update generate_ci.py

    • 7.1 Add defaults section to repos/cascadeguard-open-secure-images/.cascadeguard.yaml

      • Add defaults.registry: ghcr.io/cascadeguard and defaults.local.dir: images
      • Preserve existing ci: platform: github section
      • Requirements: 1.1, 2.1, 2.3
    • 7.2 Update generate_ci.py to use merged defaults

      • Import and call merge_defaults (or inline equivalent) so that generate_ci resolves image fields from config defaults before generating workflows
      • Ensure images without explicit registry get the default from .cascadeguard.yaml
      • Requirements: 5.1, 7.1, 7.2
    • [ ]* 7.3 Write unit tests verifying backward compatibility

      • Test that CLI behaves identically when no defaults section exists in config
      • Test that existing commands are unaffected by the new code
      • Requirements: 7.1, 7.2, 7.3
  • 8. Final checkpoint - Ensure all tests pass

    • Ensure all tests pass, ask the user if questions arise.

Notes

  • Tasks marked with * are optional and can be skipped for faster MVP
  • Each task references specific requirements for traceability
  • Checkpoints ensure incremental validation
  • Property tests validate universal correctness properties from the design document (Properties 1-8)
  • The design uses Python throughout, so all implementation is in Python
  • generate_state.py and generate_ci.py are imported as modules by the new CLI handlers — no duplication of logic