feat(harness): implement CEL-guarded overlays (ADR 0088) - #6285
Conversation
|
🤖 Finished Review · ✅ Success · Started 11:57 AM UTC · Completed 12:13 PM UTC Commit: |
Site previewPreview: https://76520e74-site.fullsend-ai.workers.dev Commit: |
ReviewFindingsMedium
Low
Previous runReviewFindingsMedium
Low
Previous run (2)ReviewFindingsMedium
Low
Previous run (3)ReviewFindingsMedium
Low
Next steps:
Previous run (4)ReviewFindingsHigh
Medium
Low
Next steps:
Previous run (5)ReviewFindingsHigh
Medium
Low
Next steps:
Previous run (6)ReviewFindingsHigh
Medium
Low
Info
Next steps:
Previous run (7)ReviewFindingsHigh
Medium
Low
Next steps:
Previous run (8)ReviewFindingsMedium
Low
Previous run (9)ReviewFindingsMedium
Low
Next steps:
Previous run (10)ReviewFindingsMedium
Low
Previous run (11)ReviewFindingsLow
Previous run (12)ReviewFindingsMedium
Low
Labels: PR modifies Go harness code and implements a new feature (CEL-guarded overlays) Previous run (13)ReviewFindingsMedium
Low
Labels: PR modifies Go harness code and implements a new feature Previous run (14)ReviewFindingsLow
|
|
🤖 Finished Fix · ✅ Success · Started 12:14 PM UTC · Completed 12:25 PM UTC Commit: |
🔧 Fix agent — iteration 1 (bot-triggered)Addressed 4 of 6 review findings with code and doc fixes. The high-severity finding (missing URL-base resolution for overlay entries) was the most significant — added overlay-iteration loops to all 5 resolveBase* functions. Disagreed with 2 low-severity findings: production caller wiring is acknowledged as acceptable follow-up work, and ADR 0088 reference resolves when the PR stack merges. Fixed (4):
Disagreed (2):
Tests: passedNext steps:
|
Add overlay-iteration loops to resolveBaseScripts, resolveBaseResources, resolveBaseHostFiles, resolveBaseProfiles, and resolveBaseProviders, paralleling the existing forge-iteration loops. Without this, overlay entries inherited from URL-sourced base harnesses would have unresolved relative paths for scripts, skills, host_files, providers, and profiles. Also: annotate ADR 0045 with cross-reference to ADR 0088 (forge deprecation), clarify overlay merge semantics in user docs, and enhance validateOverlayForgeConfig doc comment to explain the ForgeConfig embedding relationship. Addresses review feedback on #6285
|
🤖 Finished Review · ✅ Success · Started 12:26 PM UTC · Completed 12:42 PM UTC Commit: |
Superseded by updated review
Add overlay-iteration loops to resolveBaseScripts, resolveBaseResources, resolveBaseHostFiles, resolveBaseProfiles, and resolveBaseProviders, paralleling the existing forge-iteration loops. Without this, overlay entries inherited from URL-sourced base harnesses would have unresolved relative paths for scripts, skills, host_files, providers, and profiles. Also: annotate ADR 0045 with cross-reference to ADR 0088 (forge deprecation), clarify overlay merge semantics in user docs, and enhance validateOverlayForgeConfig doc comment to explain the ForgeConfig embedding relationship. Addresses review feedback on #6285
4007729 to
6ffc734
Compare
|
🤖 Review · Commit: |
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
|
🤖 Review · Commit: |
PR Summary by QodoImplement CEL-guarded harness overlays (ADR 0088) with first-match resolution
AI Description
Diagram
High-Level Assessment
Files changed (12)
|
|
🤖 Review · ❌ Terminated · Started 9:03 PM UTC · Ended 9:23 PM UTC Commit: |
Add an `overlays:` list field to the harness schema. Each entry has a `when:` CEL expression (same environment as `trigger:`) and the same override fields as `ForgeConfig`. At resolution time, all entries whose `when` evaluates to true are merged into the harness in declaration order using mergeForgeConfig semantics. Key changes: - OverlayEntry struct (When + inline ForgeConfig) in forge.go - validateOverlays: CEL compilation, field validation, mutual exclusion with forge - ResolveOverlays: evaluate when expressions, merge matches, nil out - LoadWithOpts/LoadWithBase pipeline: validateOverlays + ResolveOverlays inserted after their forge counterparts - mergeBaseIntoChild: overlay concatenation (base first, child appended) - Lint: forge deprecation warning recommending overlays - User docs: bring-your-own-agent guide updated to show overlays syntax forge: and overlays: cannot coexist in the same harness. forge: is deprecated but continues to work unchanged. Closes #2264 Closes #5989 Assisted-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Ralph Bean <rbean@redhat.com>
Add overlay-iteration loops to resolveBaseScripts, resolveBaseResources, resolveBaseHostFiles, resolveBaseProfiles, and resolveBaseProviders, paralleling the existing forge-iteration loops. Without this, overlay entries inherited from URL-sourced base harnesses would have unresolved relative paths for scripts, skills, host_files, providers, and profiles. Also: annotate ADR 0045 with cross-reference to ADR 0088 (forge deprecation), clarify overlay merge semantics in user docs, and enhance validateOverlayForgeConfig doc comment to explain the ForgeConfig embedding relationship. Addresses review feedback on #6285
Update overlay resolution to match revised ADR 0088: - Switch from merge-all to first-match-wins semantics: the first overlay entry whose `when` evaluates to true is merged; remaining entries are skipped. - Expand the overlay CEL environment with `runtime.forge` (effective forge platform) and `config` (per-repo config from config.yaml) alongside the existing `event` variable. - Add `Config map[string]any` to LoadOpts and ComposeOpts; wire ForgePlatform and Config through to ResolveOverlays. - Update docs to reflect first-match-wins and runtime.forge usage. Assisted-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Ralph Bean <rbean@redhat.com>
- Fix misleading comment in mergeBaseIntoChild: overlay concatenation uses first-match-wins (base entries take precedence), not last-entry-wins as the comment incorrectly stated - Add openshell to list of overlay-applicable fields in user docs - Document base-first overlay precedence in user guide - Add forge deprecation cross-reference in docs/architecture.md - Add forge deprecation annotation in ADR 0055 Addresses review feedback on #6285
Add --event-file flag to fullsend run for normalized event JSON input and wire ComposeOpts.Event and ComposeOpts.Config into the run path so ResolveOverlays can evaluate overlay when expressions at runtime. - Load normalized event from --event-file, parse via normevent.ParseJSON, convert to map[string]any via ToMap(), pass to ComposeOpts.Event - Build config map from per-repo config reader (forge, tracker, runtime, roles fields) and pass to ComposeOpts.Config - Add configMapForOverlays helper with tests - Update all runAgent test callers for the new eventFile parameter Addresses review feedback on #6285
…event Fix ResolveOverlays to evaluate overlays even when event is nil, allowing overlays conditioned only on runtime.forge or config to match in CLI paths (run, lock) that don't have event context. When event is nil, pass an empty map to CEL instead of short-circuiting. Wire ComposeOpts.Config in lock.go and enumerate.go so overlay when expressions can reference config.* fields. Also add documentation callout in harness-composition.md about overlay precedence exception (base-first, not child-overrides-base). Changes: - ResolveOverlays: use empty map when event is nil instead of early return - lock.go: wire Config via configMapForOverlays - enumerate.go: add buildConfigMap helper and wire Config - Update tests (TestResolveOverlays_NilEventNoop, TestLoadWithOpts_OverlayNoEvent) to use runtime.forge conditions instead of event-dependent conditions - Add overlay precedence note in docs/contributing/harness-composition.md Addresses review feedback on PR #6285. Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com> Signed-off-by: Ralph Bean <rbean@redhat.com>
Change ResolveOverlays to log-and-continue on CEL evaluation errors instead of aborting overlay resolution. This matches the MatchHarnesses pattern in harnessdispatch/enumerate.go and fixes the documented more-specific-first overlay pattern: when a specific overlay (e.g., event.source.system == "jira" && runtime.forge == "github") errors on key access because the event map is empty, the broader fallback overlay (e.g., runtime.forge == "github") is now evaluated instead of the entire resolution failing. Addresses review feedback on #6285
…plication Extract configMapForOverlays (internal/cli/run.go) and buildConfigMap (internal/harnessdispatch/enumerate.go) into a single exported harness.BuildConfigMap function accepting config.ConfigReader. Both call sites now use the shared function, ensuring overlay CEL resolution sees the same config shape regardless of the call path. This also fixes the interface mismatch: configMapForOverlays accepted ConfigWriter but only read from it. BuildConfigMap correctly accepts ConfigReader (the narrowest sufficient interface). Tests moved from run_test.go to forge_test.go alongside the function. Addresses review feedback on #6285
Per ADR 0088, runtime.forge should have the following precedence: 1. --forge flag 2. config.forge (from config.yaml) 3. CI environment variables (GITHUB_ACTIONS, GITLAB_CI) Previously, detectForgePlatform() only checked (1) and (3), skipping config.forge entirely. This commit threads the config through as a parameter and adds the config.forge check between flag and env. The call in runAgent() is moved to after orgCfg is loaded so that config.forge is available for consultation. The reconcilestatus.go call site passes nil since no config is available in that context. Added three test cases to verify the precedence chain: - TestDetectForgePlatform_ConfigForge: config.forge consulted when no flag/env - TestDetectForgePlatform_FlagOverridesConfig: flag takes precedence over config - TestDetectForgePlatform_ConfigOverridesEnv: config takes precedence over env Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com> Signed-off-by: Ralph Bean <rbean@redhat.com>
When a base harness has overlays with relative resource paths, compose.go records lock dependencies with paths like overlays[0].pre_script, overlays[0].skills[0], overlays[0].providers[0], etc. Previously, resolveFromLock only had cases for forge.<platform>.* paths to avoid duplication. Overlay paths fell through to the default case and were incorrectly appended as skills. This commit adds overlays[N].* counterparts to every forge.* case: - In the mutation switch: handle overlays[N].skills[M], overlays[N].pre_script, overlays[N].post_script, overlays[N].policy, overlays[N].validation_loop.*, overlays[N].providers[M], and overlays[N].openshell.profiles[M] - In isTreeLockField: recognize overlays[N].skills[M] as a tree field - In isScriptLockField: recognize overlays[N].pre_script, overlays[N].post_script, and overlays[N].validation_loop.script as script fields - In provider/profile parsing: recognize overlays[N].providers[M] and overlays[N].openshell.profiles[M] for proper parsing Added comprehensive tests: - TestResolveFromLock_OverlayScopedSkillNoMutation: ensures overlay skills don't duplicate into h.Skills - TestResolveFromLock_OverlayScriptNoMutation: ensures overlay scripts don't become skills - TestResolveFromLock_OverlayProviderParsed: ensures overlay providers are parsed correctly - TestResolveFromLock_OverlayProfileParsed: ensures overlay profiles are parsed correctly All existing tests continue to pass. Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com> Signed-off-by: Ralph Bean <rbean@redhat.com>
Problem: The lint check for deprecated forge: field runs after ResolveForge has already cleared h.Forge = nil, so it never fires in CI (where forge platform is always set). Solution: Add a runtime-only hadForgeBeforeResolve field to the Harness struct (yaml:"-") that LoadWithOpts sets before calling ResolveForge. Lint() checks this field instead of h.Forge so the deprecation warning is emitted even when the forge platform is set. Fixes #6285 (Issue 3) Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com> Signed-off-by: Ralph Bean <rbean@redhat.com>
Problem: BuildConfigMap only exposes 4 keys (forge, tracker, runtime, roles) but ADR 0088 says "full per-repo config". This was overly restrictive for overlay when expressions. Solution: Expand BuildConfigMap to expose all safe per-repo config fields via the PerRepoConfigReader interface. Sensitive fields (mint_url, inference provider details) remain excluded. Added comprehensive test coverage for the extended field set. Fields now exposed: - version, kill_switch (operational) - agents, allowed_remote_resources (policy) - create_issues, status_notifications (behavior config) Fixes #6285 (Issue 4) Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com> Signed-off-by: Ralph Bean <rbean@redhat.com>
Problem: Guide examples like "event.source.system == 'jira'" error with "no such key: source" when event is empty. has() exists but isn't documented. Several comments and ADR text are stale. Solution: - Document has(event.source) pattern in bring-your-own-agent.md and harness-composition.md - Update harness-fields.md to reflect current overlay resolution (nil event → empty map substitution, not no-op) - Fix stale comment in compose.go (Event field docs) - Add note to ADR 0088 pointing to harness-fields.md as living reference for current semantics Fixes #6285 (Issues 5, 6) Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com> Signed-off-by: Ralph Bean <rbean@redhat.com>
…erlay validation Address low-priority review feedback from PR #6285: - Issue 8: Add comment explaining why validateOverlayForgeConfig references "ForgeConfig" in the function name rather than "OverlayEntry". The name is semantically accurate (validates ForgeConfig fields) and remains clear with the expanded documentation. - Issue 9: Add comment explaining why the forge/overlays mutual exclusion error includes remediation advice unlike other validation errors. This is appropriate as a one-time migration message guiding users from the deprecated forge feature to overlays (ADR 0088). Both changes add explanatory comments rather than altering behavior, keeping the code semantically accurate while documenting the design choices. Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com> Signed-off-by: Ralph Bean <rbean@redhat.com>
… docs, improve coverage - Validate config.forge in detectForgePlatform: typos in config.forge now return an error instead of silently shadowing CI env vars - Update harness-fields.md: remove "planned"/"not yet implemented" markers for overlays (now implemented) - Add tests for validateOverlayForgeConfig (31.6% → 97.4%) - Add tests for EvaluateOverlay (68% → 88%) - Add tests for overlay URL-base composition (scripts, policy, skills, providers, host_files, profiles, validation_loop) - Add tests for forge deprecation warning through LoadWithOpts - Add test for config.forge validation in detectForgePlatform Addresses review feedback on #6285 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove extra empty string argument from runAgent test calls that was accidentally added during conflict resolution. The function signature has 3 string parameters after noPostScript (debug, forgeFlag, eventFile), not 4. Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com> Signed-off-by: Ralph Bean <rbean@redhat.com>
Add missing runOverrideFlags{} argument to the status notifier test case
that was missed by the earlier fix.
Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
|
🤖 Finished Review · ✅ Success · Started 7:56 PM UTC · Completed 8:14 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $11.11 |
|
🤖 Finished Retro · ✅ Success · Started 8:53 PM UTC · Completed 9:07 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $4.58 |
Retro: PR #6285 — CEL-guarded overlays (ADR 0088)Analyzed the full lifecycle of this human-authored feature PR (ralphbean, Aug 17–24). The PR added Review qualityHuman reviewer (waynesun09) dramatically outperformed the review agent. A single human pass found 6 comments (100% actionable, 2 HIGH-severity correctness bugs) that the review agent missed across 8+ review passes:
Both bugs were ADR-implementation mismatches that required reading the spec and verifying the code matched. The review agent never performed this analysis despite the PR title explicitly referencing ADR 0088. The review agent's severity calibration was also off: it rated "no production caller wires Event" as LOW for 7 consecutive runs, despite it meaning the PR's core feature was a no-op in production. The author had to manually escalate it. Review agent strengths: Reliably caught documentation staleness (planned-vs-implemented markers, missing CLI flags, stale pipeline diagrams) and code duplication. These mechanical checks are where the bot adds consistent value. Rework and token cost14 review iterations with ~44% repetitive findings. Findings like Fix agentPerformed well — 6/8 iterations succeeded, correctly implementing fixes including non-trivial refactors (extracting Evidence supporting existing issues
Autonomy readinessThe review agent is not ready for autonomous approval on large feature PRs implementing ADRs/specifications. It reliably handles documentation and style checks but misses semantic correctness bugs requiring spec-compliance analysis. Human review remains essential for this class of change. Proposals filed
|
Summary
Implements ADR 0088 (CEL-guarded overlays in the harness schema).
overlays:list field toHarness— each entry has awhen:CEL expression and the same override fields asForgeConfigResolveOverlaysevaluateswhenexpressions and merges the first matching entry usingmergeForgeConfigsemantics; remaining entries are skippedevent(normevent map),runtime.forge(effective forge platform), andconfig(per-repo config from config.yaml)validateOverlays()compileswhenexpressions against the expanded overlay CEL environment and validates override fields; rejectsforge:+overlays:coexistenceLoadWithOpts/LoadWithBasegainConfig map[string]anyand wireForgePlatform+Configthrough to overlay resolutionmergeBaseIntoChildconcatenates overlay lists (base first, child appended)Lint()emits a deprecation warning whenforge:is presentbring-your-own-agent.md) updated with first-match-wins semantics,runtime.forgeexamples, and cross-concern combined-entry patternStacked on #6237 (ADR 0088)
Test plan
validateOverlays(empty when, non-bool CEL, valid CEL, runtime.forge CEL, config variable CEL, URL script, mutual exclusion)ResolveOverlays— first-match-wins (first match applied, later matches skipped),runtime.forgeconditioning,configvariable, combinedwhenexpressions, no match, nil eventLoadWithOptsandLoadWithBasewith overlay + event data, runtime.forge, and configinternal/harness,internal/cli,internal/harnessdispatch)🤖 Generated with Claude Code