Skip to content

chore(dev→main): routine PR cadence for CodeRabbit review#4

Merged
Snider merged 6 commits intomainfrom
dev
Apr 27, 2026
Merged

chore(dev→main): routine PR cadence for CodeRabbit review#4
Snider merged 6 commits intomainfrom
dev

Conversation

@Snider
Copy link
Copy Markdown
Contributor

@Snider Snider commented Apr 27, 2026

Routine dev→main PR opened by Hephaestus PR-cadence task.

This PR exists to:

  1. Trigger CodeRabbit auto-review of accumulated dev work
  2. Surface any blocking review feedback before merge
  3. Keep main current with dev once approved

ahead_by: 4 commit(s) (per gh api compare)

If CodeRabbit clears with no blocking comments, this PR is eligible for gh pr merge --merge (real merge commit, no force-push). Conflicts and review feedback should be addressed on the dev branch before merge.

Co-authored-by: Hephaestus hephaestus@cladius

Summary by CodeRabbit

Release Notes

  • New Features

    • Added asynchronous task execution with job tracking and detached task launching.
    • Introduced template expression evaluation with comprehensive filter pipeline support (string transformations, type coercions, regex operations, base64 encoding/decoding).
    • Enhanced condition evaluation with binary operators (==, !=, <, >, <=, >=, in, not in, contains).
    • Added role support with tasks, handlers, defaults, and variables.
    • Implemented variables file parsing with glob expansion and merging.
    • Enabled host-scoped variables in inventory configuration.
  • Bug Fixes

    • Eliminated duplicate handler execution.
  • Chores

    • Updated module dependencies and package structure.
    • Refined shell initialisation behaviour for improved consistency.

Snider and others added 4 commits April 14, 2026 15:46
5.4-mini pass. Build + tests clean first try.

- parser.go: +274 lines covering playbook/inventory surface
- types.go: +10 lines of new DTOs
- executor.go, local_client.go: aligning call sites
- parser_test.go: +73 lines coverage

Co-Authored-By: Virgil <virgil@lethean.io>
- go.mod: module dappco.re/go/core/ansible → dappco.re/go/ansible
- go.mod requires: dappco.re/go/core/{io,log} → dappco.re/go/{io,log}
- Removed stale forge.lthn.ai/core/go-log indirect (maps to dappco.re/go/log)
- 7 *.go files updated with import rewrites + CLI self-import

Closes tasks.lthn.sh/view.php?id=421

Co-authored-by: Codex <noreply@openai.com>
- Bump dappco.re/go/* deps to v0.8.0-alpha.1 in go.mod (any forge.lthn.ai/core/* paths migrated to canonical dappco.re/go/* form)
- Add tests/cli/ansible/Taskfile.yaml AX-10 scaffold (build/vet/test under default deps), per RFC-CORE-008-AGENT-EXPERIENCE.md §10

Co-Authored-By: Athena <athena@lthn.ai>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 27, 2026

Warning

Rate limit exceeded

@Snider has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 12 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 33729c4b-4063-4c14-952c-199e84577da3

📥 Commits

Reviewing files that changed from the base of the PR and between d2d0e94 and b73c9ef.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (11)
  • async_features.go
  • executor.go
  • executor_extra_test.go
  • go.mod
  • local_client.go
  • local_client_test.go
  • modules_infra_test.go
  • parser.go
  • parser_test.go
  • template_features.go
  • types.go
📝 Walkthrough

Walkthrough

This pull request introduces async execution capabilities for tasks with optional polling, adds comprehensive template expression evaluation and filtering, enhances the parser with configurable storage mediums and new high-level APIs, refactors condition evaluation logic, and reorganises the module structure by consolidating dependencies into standalone packages. It also introduces a new Role type and extends task/inventory structures to support async operations and host-scoped variables.

Changes

Cohort / File(s) Summary
Async Execution Support
async_features.go, executor.go (async control flow section), types.go (Task fields)
Adds async job ID generation, detached task launching with timeout derivation from Async field, deep-cloning of executor/task/play/host structures to prevent mutation, and task-level Async and Poll fields to control async behaviour. Prevents duplicate handler execution by tracking executed handler names.
Expression & Condition Evaluation
template_features.go, executor.go (condition/expression evaluation sections)
Introduces new template_features.go with expression resolution (supporting lookup(), field access, and fallback), and a comprehensive filter pipeline (default, casing, trim, splitting, joining, type coercions, min/max, length, regex_replace, base64 encode/decode). Refactors evalCondition to support binary operators (==, !=, <, >, <=, >=, in, not in, contains) with typed comparison logic and updates operand lookup ordering.
Parser Enhancements & Storage Medium
parser.go, parser_test.go
Adds configurable storage medium support (SetMedium), new high-level parsing APIs (ParseTasksFromDir, ParseVarsFiles, ParseRoles), medium-aware file operations, path-based role loaders, and improved GetHostVars to merge per-host inventory variables. Tests verify async/poll task parsing, vars file globbing/merging, and role directory loading.
Type System Extensions
types.go
Introduces new Role type with tasks, defaults, vars, and handlers. Extends Task with Async and Poll fields. Extends Inventory with HostVars field (per-host variable mappings) and population logic during YAML unmarshalling.
Module Path & Dependency Updates
go.mod, cmd/ansible/ansible.go, modules.go, ssh.go, test_primitives_test.go
Updates module identity to dappco.re/go/ansible and migrates imports from dappco.re/go/core/* submodules to standalone dappco.re/go/* packages (io v0.8.0-alpha.1, log v0.8.0-alpha.1). Removes transitive dependency on forge.lthn.ai/core/go-log.
Shell & Client Updates
local_client.go
Modifies bash invocation to include --noprofile and --norc flags alongside -lc for altered shell initialisation behaviour.
Testing & Infrastructure
executor_extra_test.go, tests/cli/ansible/Taskfile.yaml
Adds executor tests for condition evaluation, role membership, and filter application across multiple input types. Introduces new Taskfile with build, vet, and test task orchestration for Go module validation.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.12% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title describes a routine merge cadence operation, which aligns with the actual objective of merging dev into main for CodeRabbit review.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@Snider
Copy link
Copy Markdown
Contributor Author

Snider commented Apr 27, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 27, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
local_client.go (1)

84-159: ⚠️ Potential issue | 🟠 Major

Error handling violates coding guidelines.

Multiple functions return errors without wrapping them using coreerr.E() from the go-log package:

  • Line 87: return err from io.ReadAll
  • Line 91: return err from os.MkdirAll
  • Line 93: return os.WriteFile(...) (error not wrapped)
  • Line 97: return os.ReadFile(remote) (error not wrapped)
  • Line 108: return false, err from os.Stat
  • Line 117: return nil, err from os.Stat
  • Line 139: return "", "", -1, stdinErr from cmd.StdinPipe
  • Line 158: return stdout, stderr, -1, err from cmd.Run

All errors in production code must be wrapped with coreerr.E(scope, message, err) to provide proper error context and tracing.

As per coding guidelines: all errors in production code must use coreerr.E(scope, message, err) from go-log, never fmt.Errorf.

Example fix for Upload method
 func (c *localClient) Upload(_ context.Context, localReader io.Reader, remote string, mode os.FileMode) error {
 	content, err := io.ReadAll(localReader)
 	if err != nil {
-		return err
+		return coreerr.E("local_client.upload", "failed to read upload content", err)
 	}
 
 	if err := os.MkdirAll(filepath.Dir(remote), 0o755); err != nil {
-		return err
+		return coreerr.E("local_client.upload", "failed to create directory", err)
 	}
-	return os.WriteFile(remote, content, mode)
+	if err := os.WriteFile(remote, content, mode); err != nil {
+		return coreerr.E("local_client.upload", "failed to write file", err)
+	}
+	return nil
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@local_client.go` around lines 84 - 159, Wrap all returned errors in the shown
functions with coreerr.E to add scope and context: in Upload, wrap errors from
io.ReadAll, os.MkdirAll and os.WriteFile using coreerr.E("localClient",
"read/upload file", err) (or similar messages per failure); in Download wrap
os.ReadFile errors with coreerr.E("localClient", "read remote file", err); in
FileExists and Stat wrap os.Stat errors with coreerr.E("localClient", "stat
path", err) and return the wrapped error; in runLocalShell wrap the stdin pipe
error and the final cmd.Run error with coreerr.E("localClient", "run local
shell", err) (or distinct messages like "open stdin" and "execute command") so
every error returned uses coreerr.E(scope, message, err) instead of returning
raw err.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@async_features.go`:
- Around line 23-29: The current code in launchDetachedAsyncTask creates a timed
context with context.WithTimeout and calls defer cancel() in the parent
function, which cancels the async context before the launched goroutine starts;
fix by creating the timeout context inside the goroutine (or move the
cancel/defer into the goroutine) so the goroutine owns the context lifetime and
calls cancel() when it finishes; update both places where WithTimeout is used
(the block around asyncCtx, cancel := context.WithTimeout(...) and the second
occurrence at lines 38-40) to ensure cancel() is not deferred in the parent but
invoked by the goroutine that uses asyncCtx.
- Around line 48-61: The cloned Executor currently reuses the parent clients map
via cloneClientMap(e.clients), which shares sshExecutorClient instances and can
leak per-task become/sudo state; change the clone logic in the Executor creation
so that clients is initialized to a fresh map and each entry is created as a new
sshExecutorClient instance (copying only safe immutable connection/config fields
from the original client) instead of copying the existing client objects; update
or replace cloneClientMap usage to a function that returns a new map of newly
constructed sshExecutorClient objects (preserving connection config but not any
task-level state) so async clones do not share client state with the parent.

In `@executor.go`:
- Around line 2870-2874: The current guard logic treats unresolved operands as
satisfying the comparison by returning (true, true); change it so that if either
e.resolveConditionOperandValue(left, ...) or
e.resolveConditionOperandValue(right, ...) returns !OK, the comparison fails
closed by returning (false, true) instead. Update the if !leftOK || !rightOK
block in the comparison code path to return the non-satisfied result (false,
true) so parsed operators don't erroneously evaluate as true when operands are
missing.
- Around line 2901-2959: splitBinaryCondition currently requires
isConditionBoundary around the operator for all ops, which prevents matching
symbolic operators like <=, !=, >, etc.; modify the logic so only word-like
operators (e.g., "in", "not in", "contains") require surrounding token
boundaries. Inside splitBinaryCondition (before the checks that use
isConditionBoundary for cond[i-1] and cond[end]), compute a flag (e.g.,
isWordOp) that is true when op contains any ASCII letters or underscore and
false for purely symbolic operators, then only perform the left/right boundary
checks if isWordOp is true; keep the existing trimming and empty operand checks
and return behavior unchanged for other cases.

In `@go.mod`:
- Around line 7-8: The go.mod now references modules dappco.re/go/io and
dappco.re/go/log but go.sum lacks their checksum entries; run `go mod tidy` (or
`go get dappco.re/go/io@v0.8.0-alpha.1 dappco.re/go/log@v0.8.0-alpha.1` then `go
mod tidy`) to fetch modules and update go.sum so the build/typecheck succeeds,
then commit the updated go.sum alongside the modified go.mod.

In `@parser.go`:
- Around line 22-25: Parser.medium is currently unsynchronized and can be
written by SetMedium while being read by readFile, exists, isDir, and listDir,
causing data races; either make the medium immutable after construction (remove
SetMedium) or protect access with a concurrency primitive. Fix by adding a
sync.Mutex or sync.RWMutex (or an atomic.Value) to Parser and use it in
SetMedium to guard writes and in readFile, exists, isDir, and listDir to guard
reads (or store the medium once during construction and remove SetMedium to
enforce immutability); update the Parser struct to include the mutex/atomic and
wrap all accesses to Parser.medium accordingly to eliminate the race.
- Around line 386-396: Parser.expandFilePattern currently uses filepath.Glob
which only works on the local filesystem; when a non-local coreio.Medium is set
(via SetMedium) wildcard patterns in ParseVarsFiles bypass the medium. Update
expandFilePattern to detect wildcard patterns and if p.medium is non-local
either (a) call a globbing/listing method on the medium (e.g. a hypothetical
p.medium.Glob or p.medium.ReadDir/Walk to match pattern) and return those
matches, or (b) reject wildcard patterns by returning a clear error when
p.medium is not the local filesystem; keep the existing filepath.Glob path only
when the medium is nil/local, and ensure subsequent reads still use p.readFile
so non-local media are consistently used.

In `@template_features.go`:
- Around line 44-55: The code only checks registered results when expr contains
a dot, causing bare names like "{{ cmd_result }}" to fall through; update the
resolution logic in the template lookup to consult e.getRegisteredVar for the
bare expr before calling lookupExprValue: after the dotted-path branch, call
e.getRegisteredVar(host, expr) and if non-nil return that whole result (true) so
whole-result templating and filters on registered vars are preserved; ensure
this check happens prior to invoking lookupExprValue(expr, host, task).

---

Outside diff comments:
In `@local_client.go`:
- Around line 84-159: Wrap all returned errors in the shown functions with
coreerr.E to add scope and context: in Upload, wrap errors from io.ReadAll,
os.MkdirAll and os.WriteFile using coreerr.E("localClient", "read/upload file",
err) (or similar messages per failure); in Download wrap os.ReadFile errors with
coreerr.E("localClient", "read remote file", err); in FileExists and Stat wrap
os.Stat errors with coreerr.E("localClient", "stat path", err) and return the
wrapped error; in runLocalShell wrap the stdin pipe error and the final cmd.Run
error with coreerr.E("localClient", "run local shell", err) (or distinct
messages like "open stdin" and "execute command") so every error returned uses
coreerr.E(scope, message, err) instead of returning raw err.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 77ee45dc-122a-4755-be5d-382549cb064f

📥 Commits

Reviewing files that changed from the base of the PR and between 14863ba and d2d0e94.

📒 Files selected for processing (14)
  • async_features.go
  • cmd/ansible/ansible.go
  • executor.go
  • executor_extra_test.go
  • go.mod
  • local_client.go
  • modules.go
  • parser.go
  • parser_test.go
  • ssh.go
  • template_features.go
  • test_primitives_test.go
  • tests/cli/ansible/Taskfile.yaml
  • types.go

Comment thread async_features.go Outdated
Comment thread async_features.go
Comment thread executor.go
Comment thread executor.go
Comment thread executor.go
Comment thread go.mod
Comment thread parser.go
Comment thread parser.go
Comment thread template_features.go
Snider and others added 2 commits April 27, 2026 15:06
12 files modified, +265/-41. All actionable findings dispositioned.

Code fixes:
- async_features.go + executor.go: async timeout context lifecycle
  (was cancelled before goroutine use)
- async_features.go: detached async clone no longer reuses parent
  client cache
- parser.go: Parser.medium access now synchronised
- parser.go: wildcard vars files honour non-local medium routing
- executor.go: bare registered result lookup regression closed
- local_client.go: errors are wrapped instead of raw
- template_features.go: missing condition operands no longer
  evaluate to true; symbolic condition operators now require spaces
- go.mod + go.sum: dappco.re/go/io + log dependency download +
  checksum entries

Doc fixes:
- AX-2 docstrings on exported funcs to clear CodeRabbit coverage
  warning (templateValueGreater, decodeInventoryHostVarsValue, etc.)

Disposition replies (no code change):
- SonarCloud PR/branch issues: API returned 0 unresolved for
  dAppCore_go-ansible PR 4 / dev — RESOLVED-COMMENT
- GHAS/Dependabot/secret scanning: 0 open alerts, secret scanning
  disabled, code-scanning API unavailable to token — RESOLVED-COMMENT

Verification: gofmt -l clean, git diff --check clean. GOWORK=off
+ explicit GOPATH/GOMODCACHE/GOCACHE go vet + go test -count=1 ./...
clean. No golangci-lint config in repo.

Closes findings on #4

Co-authored-by: Codex <noreply@openai.com>
Brings exported-surface docstring coverage on the codex-pushed PR #4
delta (commit d7d6e33) from 80% to 100%.

- executor.go: environmentSSHClient.Run + RunScript
- local_client.go: BecomeState, SetBecome, Close, Run, RunScript, Upload,
  Download, FileExists, Stat (9 methods)

Pure docs. No behaviour change. gofmt-clean.

Co-authored-by: Hephaestus <hephaestus@cladius>
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot
7.6% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@github-advanced-security
Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@Snider Snider merged commit 712d4b5 into main Apr 27, 2026
6 of 9 checks passed
Snider added a commit that referenced this pull request Apr 27, 2026
Round 2 verification on PR #4. CodeRabbit re-flagged previously-fixed
items; verified each is still present from d7d6e33:
- async_features.go: timeout context lifecycle (line 25)
- async_features.go: cached clients recreated not shared (line 79)
- executor.go: unresolved operands fail closed (line 2883)
- executor.go: symbolic operators with spaces (line 2914)
- parser.go: sync.RWMutex on medium access (line 24)
- parser.go: non-local wildcard vars rejected with coreerr.E (line 421)
- template_features.go: bare registered-result lookup (line 55)
- local_client.go: errors wrapped with coreerr.E (line 111)
- go.mod / go.sum: replacements produce replacement-path sums

Only diff this pass: gofmt on cmd/ansible/ansible.go (single line).

Verification: gofmt -l clean, GOWORK=off go vet + go test -count=1 ./...
pass with explicit cache paths.

Co-authored-by: Codex <noreply@openai.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants