IMPL 0002: Custom Properties Implementation Plan
Status: Completed Author: Donald Gifford Date: 2026-03-01
Detailed implementation plan derived from DESIGN-0001. Each phase is independently verifiable. Phases must be completed in order -- later phases depend on earlier ones.
Design Decisions
- YAML library: Use
gopkg.in/yaml.v3as a direct dependency. - GHA workflow token: Uses
${{ secrets.GITHUB_TOKEN }}(standard GitHub Actions token), not a custom org secret. - Stale branch cleanup: Follows the same
findOurPRpattern as the file-rules engine.
Phase 1: Catalog Parser
Package: internal/catalog
- Parse
catalog-info.yamlusing Go struct tags for type-safe YAML unmarshaling. Entity,Metadata,Specstructs withyamltags.Propertiesstruct withOwner,Component,JiraProject,JiraLabel.Parse(content string) *Propertiesfunction with defaults for invalid/missing input.- Table-driven tests: 9 cases covering all edge conditions.
Phase 2: GitHub Client Extensions
Package: internal/github
CustomPropertyValuetype.GetFileContent(ctx, owner, repo, path)-- returns decoded file content.GetCustomPropertyValues(ctx, owner, repo)-- reads current properties.SetCustomPropertyValues(ctx, owner, repo, properties)-- writes properties (api mode only).- httptest-based tests for all three methods.
- Mock client updated with new methods for checker tests.
Phase 3: Configuration
Package: internal/config
CustomPropertiesMode stringfield.- Env var:
CUSTOM_PROPERTIES_MODE. - Valid values:
""(disabled),"github-action","api". - Validation rejects invalid values at startup.
Phase 4: Metrics
Package: internal/metrics
Four new Prometheus counters:
repo_guardian_properties_checked_totalrepo_guardian_properties_prs_created_totalrepo_guardian_properties_set_totalrepo_guardian_properties_already_correct_total
Phase 5: Templates
Two embedded templates:
set-custom-properties.tmpl-- GHA workflow forgithub-actionmode.catalog-info.tmpl-- default Backstage entity forapimode.
Phase 6: Properties Checker
File: internal/checker/properties.go
Core custom properties logic:
CheckCustomProperties(ctx, client, owner, repo, defaultBranch, openPRs)- Common steps: read catalog-info.yaml, parse with
catalog.Parse(), read current properties, diff desired vs current. github-actionmode: render GHA workflow template, create PR.apimode: set properties directly, create catalog-info PR if file missing.- Stale branch cleanup following
findOurPRpattern. - Helper functions:
findPropertiesPR,diffProperties,renderTemplate,buildPropertiesPRBody.
Phase 7: Engine Integration & Wiring
- Add
customPropertiesMode stringtoEnginestruct. - Update
NewEnginesignature. - Call
CheckCustomPropertiesinCheckRepowhen mode is non-empty. - Thread
cfg.CustomPropertiesModefrommain.go.
Phase 8: Tests
File: internal/checker/properties_test.go
- 9
github-actionmode tests (catalog-info present, missing, unparseable, wrong kind, already correct, partial annotations, existing PR, dry-run, stale branch cleanup). - 6
apimode tests (catalog-info present, missing, already correct, existing PR, stale branch, dry-run). - 1 disabled mode test.
- 4 helper tests.
Phase 9: Documentation & Observability Updates
- Updated
SUMMARY.md,ONE_PAGER.mdwith Wiz/custom properties coverage. - Grafana dashboard panels for new metrics.
- Prometheus alert for PR burst detection.
CLAUDE.mdarchitecture section updated.
File Change Summary
| Phase | New Files | Modified Files |
|---|---|---|
| 1 | internal/catalog/catalog.go, catalog_test.go |
go.mod, go.sum |
| 2 | -- | internal/github/github.go, client.go, client_test.go, checker/engine_test.go |
| 3 | -- | internal/config/config.go, config_test.go |
| 4 | -- | internal/metrics/metrics.go |
| 5 | templates/set-custom-properties.tmpl, catalog-info.tmpl |
-- |
| 6 | internal/checker/properties.go |
-- |
| 7 | -- | internal/checker/engine.go, cmd/repo-guardian/main.go |
| 8 | internal/checker/properties_test.go |
-- |
| 9 | -- | docs/SUMMARY.md, ONE_PAGER.md, contrib/grafana/..., contrib/prometheus/..., CLAUDE.md |