Skip to content

feat(cel): custom data transformation functions (#14)#21

Merged
michaelmcnees merged 21 commits intomainfrom
feat/data-transformation-cel
Mar 25, 2026
Merged

feat(cel): custom data transformation functions (#14)#21
michaelmcnees merged 21 commits intomainfrom
feat/data-transformation-cel

Conversation

@michaelmcnees
Copy link
Copy Markdown
Collaborator

@michaelmcnees michaelmcnees commented Mar 25, 2026

Summary

  • Adds 15 custom CEL functions for data transformation in workflows
  • Documents already-working macros (.map(), .filter(), .exists(), .all(), .exists_one()) that were never documented
  • Creates data transformation patterns guide with CEL-only, AI-powered, and hybrid examples
  • Adds example workflows demonstrating both approaches

Closes #14

Custom Functions

Category Functions
String toLower(), toUpper(), trim(), replace(old, new), split(delim)
Type coercion parseInt(s), parseFloat(s), toString(v)
Object construction obj(k1, v1, k2, v2, ...)
Collections flatten(list)
Utility default(value, fallback) — handles null, error, and unknown
JSON jsonEncode(value), jsonDecode(string)
Date/time parseTimestamp(string) (6 formats), formatTimestamp(ts, layout)

Key design decisions

  • Functions registered via cel.Lib interface in internal/cel/functions.go
  • parseTimestamp named to avoid collision with CEL's built-in timestamp()
  • parseTimestamp tries 6 date formats: RFC3339, RFC3339Nano, bare ISO datetime, date-only, US date, named month
  • obj() uses fixed-arity overloads (2/4/6/8/10 args) since cel-go doesn't support true variadic
  • default() handles null, error, and unknown values

Documentation

  • Updated concepts/expressions.md with all functions and macros
  • New getting-started/data-transformations.md with 3 patterns (CEL-only, AI-powered, hybrid)
  • New example workflows: data-transform-api-to-db.yaml, ai-data-enrichment.yaml

Test plan

  • 39 CEL tests passing (15 existing + 7 macro + 17 function tests)
  • go vet ./internal/cel/ clean
  • Site builds successfully
  • CodeRabbit review findings addressed (null handling, date formats, docs fixes)

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added CEL functions for strings (toLower, toUpper, trim, replace, split), type coercion (parseInt, parseFloat, toString), collections (default, flatten, obj), JSON (jsonEncode, jsonDecode), and timestamps (parseTimestamp, formatTimestamp)
    • Added list macros for data transformation: map, filter, exists, all, exists_one
    • Included example workflows for AI-powered data enrichment and API-to-database transformations
  • Documentation

    • New guide on data transformation patterns and comprehensive CEL expression reference

michaelmcnees and others added 14 commits March 24, 2026 20:21
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
)

12-task plan: macro tests, string/type/collection/JSON/time functions,
obj() construction, docs updates, and example workflows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Registers custom string member overloads via cel.Lib and wires them
into NewEvaluator through a central customFunctions() registration point.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers three patterns — CEL-only structural transforms, AI-powered semantic
transforms, and hybrid workflows that combine both. Includes a decision guide
and a quick-reference table of all available CEL extension functions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ests (#14)

- default(): fall through to rhs when lhs is null (types.NullValue)
- parseTimestamp(): try RFC3339Nano, bare ISO date, US date, and named-month formats before erroring
- expressions.md: remove undeclared `now` variable from two examples
- data-transformations.md: add missing exists_one row to list-macros table
- data-transform-api-to-db.yaml: simplify to single-user fetch+insert (fixes broken batch param shape)
- functions_test.go: add null/fallback tests for default(), edge-case tests for flatten(), and new format tests for parseTimestamp()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 25, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 48b89085-4762-415d-b015-6772540d3cae

📥 Commits

Reviewing files that changed from the base of the PR and between 9a70a0e and c4788c3.

📒 Files selected for processing (6)
  • docs/superpowers/plans/2026-03-24-init-connection-recovery.md
  • docs/superpowers/specs/2026-03-24-init-connection-recovery-design.md
  • internal/cel/functions.go
  • internal/cel/functions_test.go
  • internal/cel/macros_test.go
  • site/src/content/docs/getting-started/data-transformations.md

📝 Walkthrough

Walkthrough

The PR adds comprehensive CEL custom functions for string manipulation, type coercion, collections, JSON encoding/decoding, and timestamp parsing/formatting. It includes workflow examples demonstrating AI-powered and standard data transformation patterns, extensive test coverage, and documentation. Previous planning documents for connection recovery were removed.

Changes

Cohort / File(s) Summary
CEL Functions Implementation
internal/cel/cel.go, internal/cel/functions.go
Integrated custom CEL function libraries (string, type coercion, collection, JSON, time) into the evaluator environment. Functions include toLower/toUpper/trim/replace/split, parseInt/parseFloat/toString, default/flatten/obj, jsonEncode/jsonDecode, and parseTimestamp/formatTimestamp with full error handling and type normalization.
CEL Functions Testing
internal/cel/functions_test.go, internal/cel/macros_test.go
Comprehensive test suites validating CEL function behavior, type conversions, error cases, JSON number normalization, and list macros (.map(), .filter(), .exists(), .all(), .exists_one()) with chaining and custom function integration.
Workflow Examples
examples/ai-data-enrichment.yaml, examples/data-transform-api-to-db.yaml
Added workflow examples demonstrating data transformation patterns: API-to-AI-to-database (with conditional routing on priority), and API-to-database with field transformation via CEL functions.
Documentation - Concepts
site/src/content/docs/concepts/expressions.md
Added reference documentation for new CEL functions, macros, and utility helpers grouped by category (lists, strings, object construction, type coercion, JSON, time) with YAML usage examples.
Documentation - Guides
site/src/content/docs/getting-started/data-transformations.md
New guide introducing data transformation patterns: structural transforms using CEL, AI-powered transforms with schema validation, and hybrid transforms combining both approaches.
Removed Planning Documents
docs/superpowers/plans/2026-03-24-init-connection-recovery.md, docs/superpowers/specs/2026-03-24-init-connection-recovery-design.md
Deleted implementation plan and design specification for connection recovery feature (1,155 lines total).

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Possibly Related PRs

Poem

🐰 With whiskers twitching, letters dance,
toLower, toUpper in bold expanse!
JSON and timestamps we parse with glee,
Data transforms flow, wild and free!
CEL functions hop through workflows new,
From APIs to databases—whoosh, we flew!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary change: adding 15 custom CEL functions for data transformation and documenting previously undocumented macros.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/data-transformation-cel

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

Copy link
Copy Markdown
Contributor

@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: 12

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/superpowers/plans/2026-03-24-data-transformation.md`:
- Line 15: The task headings use H3 (e.g., "### Task 1: Test and document
built-in macros") which skips from the document H1 and breaks the outline;
change those "### Task ..." headings to H2 ("## Task ...") and update any
subsequent task sections that repeat the H3 pattern so all task headings
consistently use "##" to satisfy markdownlint and preserve proper heading
hierarchy.
- Around line 689-742: The plan's
collectionFunctions()/collectionLib.CompileOptions() currently describes a
single variadic obj overload (nil arg types), but cel-go requires fixed-arity
overloads; update collectionLib.CompileOptions() to register individual
cel.Function overloads for obj with explicit arg type slices for 2, 4, 6, 8 and
10 parameters (e.g., []ref.Type{cel.DynType, cel.DynType} etc.), reusing the
same binding logic (validate even arg count and string keys, call refToNative on
values) in each overload's FunctionBinding, and ensure customFunctions() still
includes collectionFunctions(); keep error messages and behavior identical.
- Around line 1018-1130: The plan implements a function named timestamp which
conflicts with CEL's built-in timestamp(string); rename the custom symbol to
parseTimestamp everywhere: update the cel.Function call and overload name in
timeLib (e.g., change "timestamp"/"timestamp_string" to
"parseTimestamp"/"parseTimestamp_string"), update any binding logic references
in timeFunctions/timeLib and the formatTimestamp overload that expects the
parsed timestamp (keep formatTimestamp name), and update tests
(TestFunc_Timestamp -> TestFunc_ParseTimestamp or at least change the
expressions to use parseTimestamp(...)) and any references in customFunctions
where timeFunctions is added; ensure imports (time) remain and bindings still
return types.Timestamp for the parse function.

In `@docs/superpowers/specs/2026-03-24-data-transformation-design.md`:
- Around line 70-75: The spec uses the wrong function name: replace
`timestamp(string)` with `parseTimestamp(string)` in the table and any examples
so it matches the implementation and other docs (e.g., expressions.md and
functions_test.go); update the example line to
`parseTimestamp("2026-03-24T19:00:00Z")` and ensure the description still notes
Go time layout strings and the `formatTimestamp(ts, layout)` example remains
unchanged.
- Around line 87-99: The code example for NewEvaluator is inaccurate: instead of
inlining customFunctions()... into cel.NewEnv, the real implementation builds a
slice of cel.EnvOption first (opts := []cel.EnvOption{...}) and then appends the
custom functions via opts = append(opts, customFunctions()...) before calling
cel.NewEnv(opts...). Update the snippet to show the opts variable being
initialized with the cel.Variable(...) entries, appending customFunctions() to
opts, and then passing opts to cel.NewEnv so it matches the actual
implementation (referencing NewEvaluator, opts, customFunctions, and
cel.NewEnv).

In `@examples/ai-data-enrichment.yaml`:
- Around line 57-65: The YAML for the postgres/query step uses the wrong key
name — replace the second params: block (the list of query arguments under the
INSERT INTO urgent_tickets ... statement) with args: so the connector receives
arguments under args; keep the same templated values (e.g., "{{
steps.classify.output.json.priority }}", "{{
jsonEncode(steps.classify.output.json.products) }}", "{{
steps['fetch-tickets'].output.body }}") and only change the key name from params
to args to match the postgres/query contract.

In `@examples/data-transform-api-to-db.yaml`:
- Around line 2-6: Update the description to match the actual CEL usage or add
an `obj()` example in the workflow: either remove `obj()` (and possibly `string
functions`) from the front-matter description and state that it demonstrates
`toLower()` only, or modify the CEL transform expressions to include a concrete
`obj()` call (e.g., using `obj(someJson).field` in the transformation) alongside
`toLower()` so the description "Demonstrates obj(), toLower(), and string
functions" is accurate; adjust the wording around `string functions` too if you
only keep `toLower()`.

In `@internal/cel/functions_test.go`:
- Around line 201-242: Add a test in TestFunc_Obj to cover the spec case where
obj() is called with a non-string key and should return an error: update the
tests slice in TestFunc_Obj (the table used by NewEvaluator() and eval.Eval with
newTestContext()) to include a case like name: "non_string_key", expr: `obj(1,
"value")` (or similar non-string key), and set wantErr: true so the test asserts
an error is returned; keep the rest of the test harness (Eval call and
assertions) unchanged so the new case follows the same require.Error check.

In `@internal/cel/functions.go`:
- Around line 270-281: The jsonDecode overload currently unmarshals into an any
which turns all JSON numbers into float64 and loses integer precision; fix
jsonDecode by using json.Decoder with UseNumber() to parse numbers as
json.Number and then implement and call a helper (e.g., normalizeJSONNumbers) to
walk the parsed structure and convert json.Number values into appropriate
numeric types (int64 when the string represents an integer within range,
otherwise float64 or a high-precision representation), returning the normalized
value to types.DefaultTypeAdapter.NativeToValue; ensure the helper handles
nested maps and slices and is referenced from the UnaryBinding in the
cel.Function("jsonDecode") overload.
- Around line 157-168: The overload "default_any_any" is currently strict so CEL
will propagate error/unknown before the BinaryBinding runs, making the lhs
checks ineffective; change the overload to be non-strict by adding the CEL
non-strict option to the overload declaration (e.g., use cel.NonStrict or the
library's non-strict overload option) for the Overload("default_any_any", ...)
so the BinaryBinding for default handles error/unknown lhs and returns rhs when
lhs is error/unknown/null.

In `@site/src/content/docs/concepts/expressions.md`:
- Around line 490-503: Update the `obj()` documentation to state that, due to
cel-go fixed-arity overloads, `obj()` supports only up to 5 key-value pairs
(i.e., 2/4/6/8/10 arguments) rather than an unlimited number; mention callers
will get an error if they pass more than 10 arguments and suggest using nested
objects or helper functions for larger maps. Reference the `obj()` expression
name in the text so readers can find the exact function.

In `@site/src/content/docs/getting-started/data-transformations.md`:
- Around line 266-274: The table lists string transformations as global
functions (toLower(s), toUpper(s), trim(s), replace(s, old, new), split(s, sep))
but the implementation and expressions.md use string methods; update each table
row to show method syntax (e.g., s.toLower() / "hello".toLower(), s.toUpper() /
"HELLO".toUpper(), s.trim() / " a ".trim(), s.replace(old, new) /
"a-b".replace("-", "_"), s.split(sep) / "a,b".split(",")) and adjust the
Description column if needed so the table matches the actual method-based API in
expressions.md and the codebase.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: eba3db8a-214b-484b-9f64-8bca2bcbd3a9

📥 Commits

Reviewing files that changed from the base of the PR and between 236e14f and ee7f873.

📒 Files selected for processing (10)
  • docs/superpowers/plans/2026-03-24-data-transformation.md
  • docs/superpowers/specs/2026-03-24-data-transformation-design.md
  • examples/ai-data-enrichment.yaml
  • examples/data-transform-api-to-db.yaml
  • internal/cel/cel.go
  • internal/cel/functions.go
  • internal/cel/functions_test.go
  • internal/cel/macros_test.go
  • site/src/content/docs/concepts/expressions.md
  • site/src/content/docs/getting-started/data-transformations.md

…, docs accuracy

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Mar 25, 2026

Deploying mantle with  Cloudflare Pages  Cloudflare Pages

Latest commit: ed5845c
Status: ✅  Deploy successful!
Preview URL: https://6c4d32f5.mantle-cxy.pages.dev
Branch Preview URL: https://feat-data-transformation-cel.mantle-cxy.pages.dev

View logs

Copy link
Copy Markdown
Contributor

@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: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/superpowers/plans/2026-03-24-data-transformation.md`:
- Around line 821-831: The plan still contains older, pre-fix implementations
for cel.Function("default"), jsonDecode(), and parseTimestamp(); update the
document snippets to match the shipped implementations in
internal/cel/functions.go: replace the strict/null-blind default overload with
the current null-coalescing/error-aware implementation used in production, swap
the jsonDecode example that uses json.Unmarshal with the actual decoder
wrapper/panic-safe implementation, and expand parseTimestamp examples to include
all accepted formats (not only RFC3339) as in the shipped parseTimestamp
function; reference the function names cel.Function("default"), jsonDecode, and
parseTimestamp when making these replacements so the docs reflect the real code.
- Around line 1279-1282: The Postgres examples use a nested params key and a
single $1,$2,$3 tuple while mapping produces multiple rows; update both Task 11
snippets to use the connector's args parameter (not nested params) and supply an
array-of-rows that matches the three bindings per row: set args to something
like steps['fetch-users'].output.json.map(u => [u.username.toLower(),
u.email.toLower(), u.address.city]) so the postgres/query call receives args
(array of arrays) and the INSERT statement (INSERT INTO users (username, email,
city) VALUES ($1, $2, $3)) will be executed with each inner array as bindings;
apply the same change to the other occurrence referenced (lines ~1345-1353).

In `@internal/cel/functions_test.go`:
- Around line 252-264: Add a regression test to TestFunc_Default to exercise the
non-strict missing-key overload of default(): add a test case entry in the tests
slice (inside TestFunc_Default) that uses an unresolved/missing path expression
such as `default(steps.fetch.output.missing, "fallback")` and expects
"fallback"; ensure the new case name clearly indicates it's the
missing-field/non-strict overload so it will catch regressions to default's
missing-key behavior.

In `@internal/cel/functions.go`:
- Around line 277-283: After decoding into result with dec.Decode, ensure there
is no trailing JSON by checking the decoder for extra data (e.g. use dec.More()
or attempt to read the next token and expect io.EOF); if extra data exists
return an error (using types.NewErr) so inputs like "{} {}" are rejected. Update
the block around dec, result, normalizeJSONNumbers and
types.DefaultTypeAdapter.NativeToValue to perform this EOF/trailing-data check
and return an explicit jsonDecode error when trailing content is found.

In `@site/src/content/docs/getting-started/data-transformations.md`:
- Around line 87-92: The examples for the ai/completion step are inconsistent:
the snippet for the classify step places credential under params while the
runnable example (examples/ai-data-enrichment.yaml) uses a step-level
credential; update the classify step (and the other similar snippet referenced)
to move credential out of params to the same step-level field (e.g., keep name:
classify and action: ai/completion, remove credential from params and add
credential: openai at the step root) so all ai/completion examples use the same
shape and avoid confusing copy/paste users.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: be4230a6-821d-46ba-b484-f68d4fc1eb73

📥 Commits

Reviewing files that changed from the base of the PR and between ee7f873 and 3473d2a.

📒 Files selected for processing (8)
  • docs/superpowers/plans/2026-03-24-data-transformation.md
  • docs/superpowers/specs/2026-03-24-data-transformation-design.md
  • examples/ai-data-enrichment.yaml
  • examples/data-transform-api-to-db.yaml
  • internal/cel/functions.go
  • internal/cel/functions_test.go
  • site/src/content/docs/concepts/expressions.md
  • site/src/content/docs/getting-started/data-transformations.md

…cs accuracy

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@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: 2

♻️ Duplicate comments (3)
internal/cel/functions.go (1)

277-287: ⚠️ Potential issue | 🟠 Major

dec.More() is still too weak for top-level trailing-data checks.

After a successful top-level Decode, Decoder.More() only peeks for ] / }. Inputs like jsonDecode("{}]") or jsonDecode("1}") can still slip through as success. Use a real EOF check (Token() or a second Decode() expecting io.EOF) and add one of those cases to the regression suite.

📝 Suggested fix
 import (
 	"encoding/json"
 	"fmt"
+	"io"
 	"strconv"
 	"strings"
 	"time"
@@
-					// Reject trailing data (e.g., "{} {}")
-					if dec.More() {
-						return types.NewErr("jsonDecode: unexpected trailing data after JSON value")
-					}
+					if _, err := dec.Token(); err != io.EOF {
+						if err != nil {
+							return types.NewErr("jsonDecode: %v", err)
+						}
+						return types.NewErr("jsonDecode: unexpected trailing data after JSON value")
+					}
 					return types.DefaultTypeAdapter.NativeToValue(normalizeJSONNumbers(result))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/cel/functions.go` around lines 277 - 287, The current JSON
trailing-data check using dec.More() is insufficient; after Decode succeeds you
must verify there is no non-whitespace trailing data by ensuring the decoder
hits EOF (e.g., call dec.Token() and allow only io.EOF or perform a second
dec.Decode expecting io.EOF) instead of relying on dec.More(); update the code
around the JSON decoding block (the decoder created in the
json.NewDecoder(strings.NewReader(s)) path that returns
types.DefaultTypeAdapter.NativeToValue(normalizeJSONNumbers(result))) to perform
the EOF check and return a jsonDecode-style error when extra data is found, and
add regression tests for cases like "{}]" and "1}" to the test suite.
docs/superpowers/plans/2026-03-24-data-transformation.md (1)

1073-1076: ⚠️ Potential issue | 🟡 Minor

There are still stale timestamp() references in the plan.

Line 1076 says the tests fail because the runtime registers parseTimestamp(), even though the test expressions above already call parseTimestamp(). Line 1206 still tells docs writers to publish timestamp(string). Leaving both behind reintroduces the old name the implementation intentionally avoided to prevent a collision with CEL's built-in timestamp().

📝 Suggested edits
-Expected: FAIL (functions registered as `parseTimestamp`, not `timestamp`)
+Expected: FAIL (functions not registered yet)
...
-- `timestamp(string)`, `formatTimestamp(ts, layout)` with Go layout reference
+- `parseTimestamp(string)`, `formatTimestamp(ts, layout)` with Go layout reference

Also applies to: 1205-1206

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/superpowers/plans/2026-03-24-data-transformation.md` around lines 1073 -
1076, Remove the stale references to timestamp() in the plan and update the
narrative to consistently use the runtime-registered parseTimestamp() name:
change the test expectation text around TestFunc_Timestamp and
TestFunc_FormatTimestamp to state that functions are registered as
parseTimestamp (not timestamp), and update any guidance to docs writers
(previously instructing to publish timestamp(string)) to instead instruct not to
publish the old timestamp name and to use parseTimestamp(string) to avoid
colliding with CEL's built-in timestamp(); ensure all occurrences of
"timestamp()" in the document are replaced or clarified to "parseTimestamp()" so
the plan and publishing guidance are consistent.
site/src/content/docs/getting-started/data-transformations.md (1)

135-138: ⚠️ Potential issue | 🟠 Major

Don't promise engine-side schema validation.

Line 137 attributes schema validation to Mantle itself, but the code only forwards output_schema to the provider. OpenAI gets native enforcement, while Bedrock only gets a prompt instruction, so “schema-validated object” is not a universal engine guarantee.

📝 Suggested wording
-- `output_schema` tells the AI connector to return structured JSON matching the schema, not free-form text. The engine validates the response against the schema before making it available as `output.json`.
-- The `if` field on `store-escalation` is a bare CEL expression that reads from the AI step's structured output. CEL works on the schema-validated object directly.
+- `output_schema` tells the AI connector to request structured JSON instead of free-form text. Provider behavior varies: some providers enforce the schema natively, while others treat it as guidance only.
+- The `if` field on `store-escalation` is a bare CEL expression that reads from the AI step's parsed JSON output.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/superpowers/plans/2026-03-24-data-transformation.md`:
- Around line 1386-1389: Update the final validation checklist to include
running golangci-lint in addition to the existing "Step 2: Run go vet" item:
replace or augment the "Run: `go vet ./internal/cel/` Expected: clean" entry
with an added step to run `golangci-lint run ./...` (or the repository's
configured golangci-lint command) and mark the expected result as clean so local
validation matches CI linting requirements.

In `@site/src/content/docs/getting-started/data-transformations.md`:
- Around line 295-300: Update the documentation entry for parseTimestamp(s) to
reflect the actual accepted input formats: note that internal/cel/functions.go
accepts RFC3339 and RFC3339Nano, bare ISO datetimes, date-only strings, US-style
dates, and named-month formats (not just RFC3339). Edit the table row for
`parseTimestamp(s)` so the Description lists these formats concisely and
references the parsing behavior in internal/cel/functions.go (e.g., “Accepts
RFC3339/RFC3339Nano, bare ISO datetimes, date-only strings, US dates, and
named-month formats”).

---

Duplicate comments:
In `@docs/superpowers/plans/2026-03-24-data-transformation.md`:
- Around line 1073-1076: Remove the stale references to timestamp() in the plan
and update the narrative to consistently use the runtime-registered
parseTimestamp() name: change the test expectation text around
TestFunc_Timestamp and TestFunc_FormatTimestamp to state that functions are
registered as parseTimestamp (not timestamp), and update any guidance to docs
writers (previously instructing to publish timestamp(string)) to instead
instruct not to publish the old timestamp name and to use parseTimestamp(string)
to avoid colliding with CEL's built-in timestamp(); ensure all occurrences of
"timestamp()" in the document are replaced or clarified to "parseTimestamp()" so
the plan and publishing guidance are consistent.

In `@internal/cel/functions.go`:
- Around line 277-287: The current JSON trailing-data check using dec.More() is
insufficient; after Decode succeeds you must verify there is no non-whitespace
trailing data by ensuring the decoder hits EOF (e.g., call dec.Token() and allow
only io.EOF or perform a second dec.Decode expecting io.EOF) instead of relying
on dec.More(); update the code around the JSON decoding block (the decoder
created in the json.NewDecoder(strings.NewReader(s)) path that returns
types.DefaultTypeAdapter.NativeToValue(normalizeJSONNumbers(result))) to perform
the EOF check and return a jsonDecode-style error when extra data is found, and
add regression tests for cases like "{}]" and "1}" to the test suite.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ab28b0a4-5a72-4679-8f98-697538f3b6cf

📥 Commits

Reviewing files that changed from the base of the PR and between 3473d2a and 2d826be.

📒 Files selected for processing (4)
  • docs/superpowers/plans/2026-03-24-data-transformation.md
  • internal/cel/functions.go
  • internal/cel/functions_test.go
  • site/src/content/docs/getting-started/data-transformations.md

…leanup

- jsonDecode: replace dec.More() with dec.Decode(&trailing) EOF check
  to catch all trailing data (e.g., "{}]", "1}")
- Add trailing_bracket and trailing_brace regression tests
- Update parseTimestamp description in data-transformations.md to list
  all accepted formats
- Plan: rename TestFunc_Timestamp to TestFunc_ParseTimestamp, add
  golangci-lint to final validation checklist

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@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: 2

♻️ Duplicate comments (5)
docs/superpowers/plans/2026-03-24-data-transformation.md (3)

1205-1207: ⚠️ Potential issue | 🟠 Major

Task 9 still asks the writer to document timestamp().

The custom helper shipped in this PR is parseTimestamp(). Leaving timestamp(string) here points the docs back at CEL’s built-in parser instead of the extension you actually add.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/superpowers/plans/2026-03-24-data-transformation.md` around lines 1205 -
1207, Replace the incorrect reference to the built-in timestamp(string) with the
actual custom helper parseTimestamp(string) in the Date/Time Functions docs (and
any places Task 9 references timestamp()); update the entry to list
parseTimestamp(string) and describe its behavior, parameters and that it
supersedes CEL’s built-in parser, and keep formatTimestamp(ts, layout) with the
Go layout reference unchanged so readers use parseTimestamp() not timestamp().

704-723: ⚠️ Potential issue | 🟠 Major

The obj() task still shows the old variadic overload.

The note above correctly says the implementation uses fixed 2/4/6/8/10-argument overloads, but this snippet still registers a single nil-typed overload. Following it will diverge from internal/cel/functions.go.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/superpowers/plans/2026-03-24-data-transformation.md` around lines 704 -
723, The doc still shows a single variadic overload for cel.Function("obj") but
the implementation uses explicit fixed-arity overloads; replace the nil
(variadic) overload with explicit cel.Overload entries for the supported arities
(2,4,6,8,10) each returning cel.DynType and using the same FunctionBinding logic
(validate even args, check keys are types.String, call refToNative for values,
return types.DefaultTypeAdapter.NativeToValue(m), and produce errors via
types.NewErr). Ensure each overload signature matches the fixed number of
ref.Val parameters so the docs mirror internal/cel/functions.go.

978-985: ⚠️ Potential issue | 🟠 Major

Keep the jsonDecode() plan snippet in sync with the trailing-data fix.

This example returns after the first Decode, so inputs like "{} {}" are effectively documented as valid again. The shipped code now performs a second decode and expects io.EOF.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/superpowers/plans/2026-03-24-data-transformation.md` around lines 978 -
985, The jsonDecode plan currently returns immediately after the first
dec.Decode(&result), which allows trailing JSON to be accepted; after
successfully decoding into result in the jsonDecode snippet, perform a second
decode into a temporary variable (e.g., var extra any) and ensure the decoder
returns io.EOF; if the second Decode returns nil or any error other than io.EOF,
return a types.NewErr indicating trailing data (or the decode error) instead of
returning the parsed value, then continue to return
types.DefaultTypeAdapter.NativeToValue(normalizeJSONNumbers(result)) upon EOF.
Make sure to reference the json decoder (dec), result variable,
normalizeJSONNumbers, and types.DefaultTypeAdapter.NativeToValue in the fix and
handle io.EOF comparison.
site/src/content/docs/getting-started/data-transformations.md (1)

274-279: ⚠️ Potential issue | 🟡 Minor

Expose obj()'s 5-pair limit in the reference table.

obj(key, val, ...) reads like a true variadic helper, but internal/cel/functions.go only registers 2/4/6/8/10-argument overloads. Without the cap here, readers can copy an expression that fails in the evaluator once they go past five pairs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@site/src/content/docs/getting-started/data-transformations.md` around lines
274 - 279, The table entry for obj(key, val, ...) is misleadingly variadic;
update the docs to state the actual limit (up to 5 key/value pairs) or list
supported overloads explicitly. Mention the specific helper name obj() and
reference the overload registrations in internal/cel/functions.go
(2/4/6/8/10-argument overloads) so readers know the evaluator caps at five
pairs. Keep the description short and replace or augment "obj(key, val, ...)"
with "obj(key, val, ... up to 5 pairs)" or enumerated signatures.
internal/cel/functions.go (1)

353-362: ⚠️ Potential issue | 🟠 Major

jsonDecode() still rounds integers that don't fit in int64.

A pure integer like 9223372036854775808 falls through to Float64() here, so the decoded value loses bits silently. Preserve out-of-range integer tokens exactly instead of coercing them to float64, and add a regression test for that path.

Preserve out-of-range integers exactly
 func normalizeJSONNumbers(v any) any {
 	switch val := v.(type) {
 	case json.Number:
-		if i, err := val.Int64(); err == nil {
+		s := val.String()
+		if !strings.ContainsAny(s, ".eE") {
+			if i, err := val.Int64(); err == nil {
+				return i
+			}
+			return s
+		}
+		if f, err := val.Float64(); err == nil {
+			return f
+		}
+		return s
-			return i
-		}
-		if f, err := val.Float64(); err == nil {
-			return f
-		}
-		return val.String()
 	case map[string]any:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/cel/functions.go` around lines 353 - 362, normalizeJSONNumbers
currently falls back to Float64 when Int64 fails, which silently loses precision
for integers > int64; change the logic in normalizeJSONNumbers (and update
jsonDecode tests) so that if json.Number.String() contains no decimal point or
exponent and val.Int64() returns an error, you preserve the exact token by
returning val.String() (not val.Float64()); only use Float64 for numbers that
include a decimal point or an exponent. Add a regression test that jsonDecode
returns the exact string for a large integer like "9223372036854775808" (and
assert it is not converted to a float).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/superpowers/plans/2026-03-24-data-transformation.md`:
- Around line 419-424: Remove the unused local `result` and its population loop
from the split() snippet: delete the lines that create `result :=
make([]ref.Val, len(parts))` and the for-loop that assigns `result[i] =
types.String(p)`, leaving the function to directly return
types.DefaultTypeAdapter.NativeToValue(parts); this removes the dead-variable
compile error when copying into functions.go while keeping the intended return
value.

In `@internal/cel/functions_test.go`:
- Around line 312-324: The test "empty list" currently allows
flatten(inputs.empty) to return nil; change it to require a list instead: after
calling eval.Eval(`flatten(inputs.empty)`, ctx) assert no error, then assert
that result is a []any (use a type assertion on result and require.True or
require.IsType) and finally assert that the resulting slice is empty; reference
the test block named "empty list", the call to eval.Eval and the flattened
function `flatten(inputs.empty)` so the assertion enforces a non-nil []any
return value.

---

Duplicate comments:
In `@docs/superpowers/plans/2026-03-24-data-transformation.md`:
- Around line 1205-1207: Replace the incorrect reference to the built-in
timestamp(string) with the actual custom helper parseTimestamp(string) in the
Date/Time Functions docs (and any places Task 9 references timestamp()); update
the entry to list parseTimestamp(string) and describe its behavior, parameters
and that it supersedes CEL’s built-in parser, and keep formatTimestamp(ts,
layout) with the Go layout reference unchanged so readers use parseTimestamp()
not timestamp().
- Around line 704-723: The doc still shows a single variadic overload for
cel.Function("obj") but the implementation uses explicit fixed-arity overloads;
replace the nil (variadic) overload with explicit cel.Overload entries for the
supported arities (2,4,6,8,10) each returning cel.DynType and using the same
FunctionBinding logic (validate even args, check keys are types.String, call
refToNative for values, return types.DefaultTypeAdapter.NativeToValue(m), and
produce errors via types.NewErr). Ensure each overload signature matches the
fixed number of ref.Val parameters so the docs mirror internal/cel/functions.go.
- Around line 978-985: The jsonDecode plan currently returns immediately after
the first dec.Decode(&result), which allows trailing JSON to be accepted; after
successfully decoding into result in the jsonDecode snippet, perform a second
decode into a temporary variable (e.g., var extra any) and ensure the decoder
returns io.EOF; if the second Decode returns nil or any error other than io.EOF,
return a types.NewErr indicating trailing data (or the decode error) instead of
returning the parsed value, then continue to return
types.DefaultTypeAdapter.NativeToValue(normalizeJSONNumbers(result)) upon EOF.
Make sure to reference the json decoder (dec), result variable,
normalizeJSONNumbers, and types.DefaultTypeAdapter.NativeToValue in the fix and
handle io.EOF comparison.

In `@internal/cel/functions.go`:
- Around line 353-362: normalizeJSONNumbers currently falls back to Float64 when
Int64 fails, which silently loses precision for integers > int64; change the
logic in normalizeJSONNumbers (and update jsonDecode tests) so that if
json.Number.String() contains no decimal point or exponent and val.Int64()
returns an error, you preserve the exact token by returning val.String() (not
val.Float64()); only use Float64 for numbers that include a decimal point or an
exponent. Add a regression test that jsonDecode returns the exact string for a
large integer like "9223372036854775808" (and assert it is not converted to a
float).

In `@site/src/content/docs/getting-started/data-transformations.md`:
- Around line 274-279: The table entry for obj(key, val, ...) is misleadingly
variadic; update the docs to state the actual limit (up to 5 key/value pairs) or
list supported overloads explicitly. Mention the specific helper name obj() and
reference the overload registrations in internal/cel/functions.go
(2/4/6/8/10-argument overloads) so readers know the evaluator caps at five
pairs. Keep the description short and replace or augment "obj(key, val, ...)"
with "obj(key, val, ... up to 5 pairs)" or enumerated signatures.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e0e79ecd-e6f2-4ca1-88b0-0b06aad16b87

📥 Commits

Reviewing files that changed from the base of the PR and between 2d826be and 9a70a0e.

📒 Files selected for processing (4)
  • docs/superpowers/plans/2026-03-24-data-transformation.md
  • internal/cel/functions.go
  • internal/cel/functions_test.go
  • site/src/content/docs/getting-started/data-transformations.md

- normalizeJSONNumbers: only use float64 for decimals/exponents, preserve
  overflow integers as strings to avoid silent precision loss
- flatten: initialize result as make([]any, 0) so empty lists return
  non-nil empty slice
- Add large_integer_preserved test for jsonDecode
- Plan: remove dead split() variable, replace variadic obj() with
  fixed-arity overloads, add jsonDecode EOF check, fix timestamp refs
- data-transformations.md: note obj() 5-pair limit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/superpowers/plans/2026-03-24-data-transformation.md`:
- Around line 1148-1151: The test command uses the wrong selector and will skip
the ParseTimestamp test; update the test run invocation to include the correct
test name TestFunc_ParseTimestamp (replace or augment the current selector
TestFunc_Timestamp|TestFunc_FormatTimestamp with TestFunc_ParseTimestamp, e.g.
TestFunc_ParseTimestamp|TestFunc_Timestamp|TestFunc_FormatTimestamp) so the
parse test defined as TestFunc_ParseTimestamp is executed when running go test
./internal/cel/ -run "...".

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 59b8c48a-f403-4859-9620-5fe78aebc70a

📥 Commits

Reviewing files that changed from the base of the PR and between 9a70a0e and ed5845c.

📒 Files selected for processing (4)
  • docs/superpowers/plans/2026-03-24-data-transformation.md
  • internal/cel/functions.go
  • internal/cel/functions_test.go
  • site/src/content/docs/getting-started/data-transformations.md

michaelmcnees and others added 2 commits March 24, 2026 22:20
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These internal process docs (specs, plans) are already in .gitignore
but were force-added during development. Files remain on disk but are
no longer tracked by git.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 25, 2026

Note

Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 25, 2026

✅ Unit tests committed locally. Commit: c4788c31f5b89c3293aea9971b90f9978a522959

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 25, 2026

Note

Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 25, 2026

✅ Created PR with unit tests: #26

@michaelmcnees michaelmcnees merged commit 21cdd42 into main Mar 25, 2026
6 checks passed
michaelmcnees added a commit that referenced this pull request Mar 25, 2026
* docs: add spec for data transformation CEL functions (#14)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add implementation plan for data transformation CEL functions (#14)

12-task plan: macro tests, string/type/collection/JSON/time functions,
obj() construction, docs updates, and example workflows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(cel): add coverage for built-in map/filter/exists/all macros

* feat(cel): add toLower, toUpper, trim string functions

Registers custom string member overloads via cel.Lib and wires them
into NewEvaluator through a central customFunctions() registration point.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(cel): add replace and split string functions

* feat(cel): add parseInt, parseFloat, toString type coercion functions

* feat(cel): add obj() map construction function

* feat(cel): add default() null coalescing and flatten() functions

* feat(cel): add jsonEncode and jsonDecode functions

* feat(cel): add timestamp and formatTimestamp date/time functions

* docs: add custom CEL functions and macros to expressions reference (#14)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add data transformation and AI enrichment example workflows (#14)

* docs: add data transformation patterns guide (#14)

Covers three patterns — CEL-only structural transforms, AI-powered semantic
transforms, and hybrid workflows that combine both. Includes a decision guide
and a quick-reference table of all available CEL extension functions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address CodeRabbit review — null handling, date formats, docs, tests (#14)

- default(): fall through to rhs when lhs is null (types.NullValue)
- parseTimestamp(): try RFC3339Nano, bare ISO date, US date, and named-month formats before erroring
- expressions.md: remove undeclared `now` variable from two examples
- data-transformations.md: add missing exists_one row to list-macros table
- data-transform-api-to-db.yaml: simplify to single-user fetch+insert (fixes broken batch param shape)
- functions_test.go: add null/fallback tests for default(), edge-case tests for flatten(), and new format tests for parseTimestamp()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address PR #21 review — non-strict default, jsonDecode precision, docs accuracy

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR #21 review round 3 — test coverage, trailing JSON, docs accuracy

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR #21 review round 4 — EOF trailing check, docs, plan cleanup

- jsonDecode: replace dec.More() with dec.Decode(&trailing) EOF check
  to catch all trailing data (e.g., "{}]", "1}")
- Add trailing_bracket and trailing_brace regression tests
- Update parseTimestamp description in data-transformations.md to list
  all accepted formats
- Plan: rename TestFunc_Timestamp to TestFunc_ParseTimestamp, add
  golangci-lint to final validation checklist

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR #21 review round 5 — precision, flatten, plan accuracy

- normalizeJSONNumbers: only use float64 for decimals/exponents, preserve
  overflow integers as strings to avoid silent precision loss
- flatten: initialize result as make([]any, 0) so empty lists return
  non-nil empty slice
- Add large_integer_preserved test for jsonDecode
- Plan: remove dead split() variable, replace variadic obj() with
  fixed-arity overloads, add jsonDecode EOF check, fix timestamp refs
- data-transformations.md: note obj() 5-pair limit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(plan): correct test selector to TestFunc_ParseTimestamp

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove force-added superpowers docs from tracking

These internal process docs (specs, plans) are already in .gitignore
but were force-added during development. Files remain on disk but are
no longer tracked by git.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* 📝 CodeRabbit Chat: Add generated unit tests

* feat(site): add Docker connector to homepage, fix tablet layout issues

- Add Docker connector card to connectors grid, update count to 8/10
- Update hero section connector count
- Add Docker to built-in connector list in plugins guide
- Fix bottom nav visible on tablets where top nav already shows (lg:hidden → md:hidden)
- Fix nav link overlap at tablet sizes (gap-8 → gap-4 lg:gap-8)
- Delay Get Started button to md breakpoint with tighter padding
- Sync bottom padding and sidebar offset to md breakpoint

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add execution_artifacts table (migration 013)

* feat: add ArtifactDecl and RegistryCredential to Step struct

- Add ArtifactDecl type to declare files produced by a step
- Add ArtifactRef type for runtime representation in CEL expressions
- Add registry_credential field to Step for container registry auth
- Add artifacts field to Step for artifact declarations
- Add test case for parsing artifacts and registry credentials

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add docker credential type (all fields optional)

* feat: validate artifact declarations (unique names, required fields)

* feat: add TmpStorage interface with filesystem backend and artifact context helpers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add artifacts variable to CEL environment

Add support for accessing artifact metadata in CEL expressions:
- Add Artifacts field to Context struct for {name, url, size}
- Register artifacts variable in CEL environment as a map
- Initialize artifacts to empty map if nil during evaluation
- Add test for accessing artifact URL and size properties

Workflow authors can now reference artifacts via artifacts['name'].url
in step parameters, enabling dynamic artifact references.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add artifact metadata store with Postgres backend

Implements Store with Create, GetByName, ListByExecution, DeleteByExecution,
and ListExpired operations backed by the execution_artifacts table (migration 013).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add docker/run param parsing and types

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add artifact reaper for TTL-based cleanup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: add Docker volume backup and data transform example workflows

Refs #12, refs #13

* docs: add docker/run connector reference documentation

* feat: implement docker/run connector with Moby client

Add Execute method to DockerRunConnector using the Moby Go client,
supporting image pull policies, stdin, env vars, volume mounts,
artifacts dir bind-mount, resource limits, context cancellation,
and stdout/stderr capture with 10MB limit per stream.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: wire artifact lifecycle and registry credential into engine

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address CodeRabbit review findings

- Handle filepath.Abs errors in FilesystemTmpStorage.DeleteByPrefix
- Fix artifact reaper over-counting on partial file deletion failure
- Extract loadArtifactsIntoCELContext helper, log errors instead of silencing
- Return error on invalid memory limit in docker/run
- Log stdcopy.StdCopy errors in docker/run
- Dynamic credential type list in GetType error message
- Add missing error checks in tests
- Fix idiomatic boolean check in docker_test.go

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address CodeRabbit review findings (site/examples)

- Fix Hero.astro connector count to match Connectors.astro (8 not 10)
- Make volume backup notification conditions mutually exclusive

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: update example workflow count in Hero (17 → 22)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add Docker workflows guide, artifacts concept page, and config/credential docs

- Add getting-started/docker-workflows.md with examples for stdin, env,
  resource limits, exit code branching, private images, volume mounts
- Add concepts/artifacts.md explaining the artifact system, tmp storage,
  CEL access, retention, and scope
- Add docker and aws credential types to secrets-guide.md
- Add tmp storage config fields to configuration.md (config table + env vars)
- Add Data Transformations, Docker Workflows, and Artifacts to docs nav

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address CI failures — deprecated ImageInspectWithRaw, gosec G118

- Replace deprecated ImageInspectWithRaw with ImageInspect
- Add #nosec G118 for intentional context.Background() in stop goroutine

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add Go doc comments to exported symbols for docstring coverage

Add missing doc comments to Store.Create, Store.GetByName, Store.ListByExecution,
FilesystemTmpStorage.Put, FilesystemTmpStorage.DeleteByPrefix, and
DockerRunConnector.Execute to reach 80% docstring coverage on PR-modified files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: move #nosec G118 annotation to goroutine line where gosec flags it

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address CodeRabbit review round 2

Artifact system:
- Use DB clock (NOW() - interval) in ListExpired instead of app clock
- Return generated ID/CreatedAt from Create via RETURNING clause
- Add DeleteByID for per-artifact reaper cleanup
- Reaper now deletes per-artifact instead of per-execution batch
- Add path traversal protection to FilesystemTmpStorage.Put
- Use filepath.Rel for stronger containment check in DeleteByPrefix
- Add CHECK (size >= 0) to migration, remove redundant index
- Fix missing error checks in tests
- Fail in CI instead of skip when Postgres container fails

Docker connector:
- Wire TLS credentials (ca_cert, client_cert, client_key) into client
- Wire registry_credential into pullImage for private registry auth
- Check io.Copy drain error after ImagePull
- Reject negative memory values in parseMemoryString
- Validate mount source/target are non-empty
- Log stdin pipe errors
- Check errCh after status in ContainerWait select

Engine:
- Refresh artifacts in CEL context after each step completes
- Clean up orphaned blob when ArtifactStore.Create fails
- Emit audit event (artifact.persisted) after artifact storage

Examples:
- Fix postgres output field reference (output.json → output.rows)
- Note continue_on_error limitation in volume backup (see #29)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address CodeRabbit review round 3

- Add TmpStorage.Delete for single-file removal by URL
- Clean up partial file in Put on io.Copy failure
- Reaper uses Delete(url) instead of DeleteByPrefix
- Stop mutating caller's params map in docker/run Execute
- Use strconv.ParseInt for stricter memory value parsing
- Reject artifact bind mounts for remote Docker daemons
- Full rollback of persisted artifacts on partial failure in engine

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address CodeRabbit review round 4

- Make TmpStorage.Delete idempotent (treat ErrNotExist as success)
- Fail fast when step declares artifacts but tmp storage not configured
- Use cli.DaemonHost() for remote daemon detection (handles DOCKER_HOST env)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address CodeRabbit review round 5

- Reject symlinks and non-regular files in TmpStorage.Put
- Validate cpus param is non-negative in docker/run
- Add timeout context and error logging to container cleanup/stop
- Create fresh artifacts scratch dir per retry attempt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.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.

[Feature] Data Transformation Step

1 participant