Skip to content

✨ Parameter delivery, substitution, and execution controls - #169

Merged
djzager merged 4 commits into
konveyor:mainfrom
djzager:impl/115-116-crd-params-execution
Aug 26, 2026
Merged

djzager merged 4 commits into
konveyor:mainfrom
djzager:impl/115-116-crd-params-execution

Conversation

@djzager

@djzager djzager commented Aug 20, 2026

Copy link
Copy Markdown
Member

Implements ADRs 0009/0011 on the controller, with amendments captured in a new ADR 0018.

Closes #115
Closes #116
Closes #119
Closes #87

What's here

Params (#115/#116)

  • Rename AgentParam/AgentRunParam/AgentParamTypeParam/ParamValue/ParamType (Go only; CRD field names unchanged).
  • Deliver typed params via /run/konveyor/params.json (workflow/agent/execution sections), mounted read-only; remove the old KONVEYOR_PARAM_* env vars.
  • $(scope.name) substitution across the agent prompt, run instructions, workflow guide, and stage instructions. Unresolved refs and bad type coercion fail the run terminally with InvalidParams.
  • Workflow-level params on AgentWorkflow; resolved workflow params stamped onto stage AgentRuns.

Execution controls (#115)

  • ExecutionLimits (maxTurns, maxCost) are Agent defaults, overridable per stage; ExecutionSpec (mode + limits) lives on AgentRun and stages. The Agent cannot declare mode (execution-time concern — ADR 0011/0018). Single resolveExecution rule.

Terminal model (#119)

  • AgentRun gains a Knative/Tekton-style Succeeded condition (Unknown while running, True/False terminal) and opaque terminationData read from the pod termination message.
  • Harness exit 0/1/2Succeeded / Failed / LimitReached.
  • Ready is removed from AgentRun; ACPReady (serving) and phase (coarse mirror) are unchanged.

Workflow snapshot (#87)

  • AgentWorkflowRun snapshots the full stage definitions, guide, and params at initialization and executes from the snapshot, so a mid-run workflow edit cannot change stages already planned.

ADR 0018

Amends ADR 0011 (execution controls) and ADR 0009 (parameter delivery) — three places where the merged text didn't survive contact with the controller: stage limits had nowhere to land, exit-2 → Succeeded contradicted workflow sequencing, and a workflow guide has no single agent scope. Builds on the pod-read plumbing merged in #160.

Test plan

  • make test — envtest suite green (unit coverage 78.4%), including terminal-outcome exit-code mapping and the three-section params.json.
  • make lint — 0 issues.
  • make generate manifests — CRDs regenerated and committed.

@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 64c8f5d2-cdec-494b-bfe5-433c2d145540


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@ibolton336 ibolton336 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

One inline pointer; not blocking.

if pod == nil {
return nil
}
for _, cs := range pod.Status.ContainerStatuses {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

agentContainerTermination only looks at containerStatuses; once #157 lands, the skill-loader's failure message sits in initContainerStatuses and the run ends as a bare PodFailed — filed as #171 rather than widening this PR.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch, and agreed on keeping it out of this PR. Now that #157 has merged (rebased in here), the init-container gap is live: agentContainerTermination only reads containerStatuses, so a skill-loader failure lands as a generic Succeeded=False, reason=Failed at best, and a never-starting pod can miss a terminal signal entirely. Tracked in #171 — I'll widen the pod-read there (init container statuses + a distinct reason) rather than expanding scope here.

@djzager
djzager force-pushed the impl/115-116-crd-params-execution branch 3 times, most recently from 6d75bb3 to e4c20d0 Compare August 24, 2026 17:14
savitharaghunathan added a commit to savitharaghunathan/agentic-controller that referenced this pull request Aug 24, 2026
Replace the hard-coded KONVEYOR_PARAM_MAX_TURNS env read with a generic
loader of the controller's three-section /run/konveyor/params.json
(ADR 0009, delivered by PR konveyor#169), and append a "## Parameters" section
to the agent prompt so workflow/agent values are visible with zero
skill involvement.

Closes konveyor#117

Assisted-By: Claude Code <noreply@anthropic.com>
Signed-off-by: Savitha Raghunathan <saveetha13@gmail.com>
@djzager
djzager requested a review from ibolton336 August 25, 2026 19:05
Comment thread internal/controller/params.go
Comment thread internal/controller/agentrun_controller.go Outdated
@djzager
djzager force-pushed the impl/115-116-crd-params-execution branch from e4c20d0 to 93b6c4d Compare August 26, 2026 01:59
savitharaghunathan added a commit to savitharaghunathan/agentic-controller that referenced this pull request Aug 26, 2026
Replace the hard-coded KONVEYOR_PARAM_MAX_TURNS env read with a generic
loader of the controller's three-section /run/konveyor/params.json
(ADR 0009, delivered by PR konveyor#169), and append a "## Parameters" section
to the agent prompt so workflow/agent values are visible with zero
skill involvement.

Closes konveyor#117

Assisted-By: Claude Code <noreply@anthropic.com>
Signed-off-by: Savitha Raghunathan <saveetha13@gmail.com>
savitharaghunathan added a commit to savitharaghunathan/agentic-controller that referenced this pull request Aug 26, 2026
Replace the hard-coded KONVEYOR_PARAM_MAX_TURNS env read with a generic
loader of the controller's three-section /run/konveyor/params.json
(ADR 0009, delivered by PR konveyor#169), and append a "## Parameters" section
to the agent prompt so workflow/agent values are visible with zero
skill involvement.

Closes konveyor#117

Assisted-By: Claude Code <noreply@anthropic.com>
Signed-off-by: Savitha Raghunathan <saveetha13@gmail.com>

@fabianvf fabianvf left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Read through this. Mostly questions.

The harness side is the part I couldn't follow. harness/internal/config/config.go:105 is still the only setter for cfg.MaxTurns and it reads KONVEYOR_PARAM_MAX_TURNS, but I went looking for where params.json gets consumed and came up empty:

$ grep -rn 'params.json\|/run/konveyor' harness/
(nothing)

So I can't work out how execution.maxTurns reaches the harness now, or whether the max_turns param in hack/harness-test/workflow-resources.yaml still does anything. Same for exit 2: os.Exit(1) in harness/cmd/migration-harness/main.go:48 is the only non-zero exit I can find, and the maxTurns path goes through acp/session.go:271 as an error, which I'd expect to land as exit 1. Nothing writes /dev/termination-log either. @djzager is the harness half intentional as-is?

Couple of doc leftovers outside the diff: skills/verify/SKILL.md lines 59 and 84 still tell the model to read KONVEYOR_PARAM_MAX_FIX_ITERATIONS, and harness/README.md:69 still documents KONVEYOR_PARAM_MAX_TURNS. The AgentRun godoc at api/v1alpha1/agentrun_types.go:209 also still says params are injected as env vars, which ships as the CRD description.

Rest is inline.

if !ok {
value = p.Default
}
if value == "" {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

value == "" skips the param in strs as well as coerced, so $(agent.x) on a declared param with no supplied value and no default becomes an unresolved reference and fails the run. Makes a referenced param required whatever its required flag says.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch — that conflated "declared but unset" with "undeclared." Fixed: an unset declared param now resolves to an empty string in $(agent.x) (still omitted from params.json), so only genuinely undeclared references fail. Required params are already caught earlier in validateParams, so anything reaching here is optional.

Comment thread internal/controller/params.go Outdated
if value == "" {
continue
}
strs[p.Name] = value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is the difference between the two scopes here deliberate? strs gets the raw supplied string, but the workflow scope goes through stringifyJSON on the coerced value at line 170, so a boolean supplied as T looks like it renders T in $(agent.flag) and true in $(workflow.flag). params_test.go:78 asserts the agent-side behavior, so I assume one of the two is the one you want.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Deliberate now, not before: both scopes render the canonical coerced value through one shared stringifyJSON rule, so a value renders identically whichever scope references it — a boolean is true/false regardless of the input spelling (T/yes/1), and numbers normalize the same way. Extended stringifyJSON to cover the agent scope's int64/float64 alongside the workflow scope's json.Number, and updated params_test.go to assert the canonical form (Ttrue).

if err := r.validateParams(&run, &agent); err != nil {
// Validate params against Agent declarations. The resolved params and
// substitution scopes are reused by createSandbox below.
params, scopes, err := r.validateParams(&run, &agent)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should this move inside the if run.Status.SandboxName == "" block? Now that validateParams does coercion and substitution it runs on every reconcile of a live run, and findRunsForAgent enqueues non-terminal runs on any Agent write. So if someone edits the Agent's prompt while a run is executing I think it flips to Failed/InvalidParams with the pod still up. Or is re-validating mid-run deliberate?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Not deliberate — moved pre-creation validation inside the SandboxName == "" branch, so a live run is never re-validated against a since-edited Agent (the pod already captured its rendered prompt/params at creation, so re-validating could only spuriously fail it). Also moved validateGateway for the same reason.

Worth separating: this stops a running stage from breaking, but full immunity of not-yet-created workflow stages to mid-run Agent edits is a bigger determinism question (we snapshot the workflow def in #87, not the Agent defs). Filed #180 to decide snapshot-vs-generation-pin there.

if err := ctrl.SetControllerReference(run, cm, r.Scheme); err != nil {
return corev1.Volume{}, corev1.VolumeMount{}, fmt.Errorf("setting ConfigMap owner reference: %w", err)
}
if err := r.Create(ctx, cm); err != nil && !errors.IsAlreadyExists(err) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is the bare Create plus AlreadyExists swallow enough here? createSandbox can still fail after this point, and the content depends on agent.Spec.Params and agent.Spec.Execution rather than just the immutable run spec, so on a retry I think you could get a stale params.json next to a freshly rendered KONVEYOR_PROMPT. createInlineSkillConfigMaps uses CreateOrUpdate and createAgentRunForStage does Get + isOwnedBy, was there a reason to do this one differently?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

No good reason — switched to ctrl.CreateOrUpdate, matching createInlineSkillConfigMaps. On a createSandbox retry the params ConfigMap is now updated to the freshly rendered content instead of an AlreadyExists swallow leaving a stale params.json next to a fresh KONVEYOR_PROMPT.

})
// Capture the harness's opaque termination data (usage/cost
// report). Stored verbatim, never interpreted (ADR 0018).
if term := agentContainerTermination(pod); term != nil && json.Valid([]byte(term.Message)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

json.Valid passes arrays and bare scalars but the CRD has terminationData as type: object, so a non-object message would get the whole status patch rejected, setTerminalOutcome included. Unmarshalling into a map[string]any would cover it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed — now unmarshals into map[string]any and only stores terminationData when the message is a JSON object, so an array/scalar can't get the whole status patch (and setTerminalOutcome with it) rejected against the type: object schema. Extracted as terminationDataFromPod and covered by TestTerminationDataFromPod.

var stageParams []konveyoriov1alpha1.ParamValue
var skipped []string
for _, p := range pbRun.Spec.Params {
if declared[p.Name] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

declared only has the stage Agent's params, so workflow params that are only used in the guide land in skipped on every stage, even though coerceWorkflowParams uses them fine below.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Right — a workflow-only param (used in the guide) was being filtered against the stage Agent's declarations and logged as skipped on every stage. Now the filter also consults the snapshotted workflow declarations (pbRun.Status.Params): the stage AgentRun still receives only the params its Agent declares, but a param declared by the workflow (not the stage Agent) is no longer flagged as skipped — only genuinely-undeclared params (typos) are.

// section) and keep the string form for substituting the guide
// (workflow scope only — the guide is ambient across stages and must
// not reference stage-agent params). ADR 0009/0018.
workflowRaw, workflowStrs, err := coerceWorkflowParams(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How should a permanent config error here end up? A bad workflow param value or a $(workflow.typo) in the guide comes back as a plain error and the caller returns it, which I think backs off forever with phase stuck on Running. Spec is immutable and the declarations come from the frozen Status.Params, so I can't see what would ever clear it. The AgentRun side fails terminally with InvalidParams for the same class of thing, should this match?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It should match, and now does. A permanent workflow config error — a bad workflow param value or an unresolved $(workflow.<name>) in the guide — is wrapped in a configError and the caller fails the run terminally with InvalidParams (phase Failed, completion time set), exactly like the AgentRun side, instead of returning a plain error and backing off forever with phase stuck Running. Genuinely transient errors (Agent lookup, API) still requeue as before.

// standalone runs. Coercion happens in the workflow controller,
// which owns the AgentWorkflow param declarations.
// +optional
WorkflowParams *runtime.RawExtension `json:"workflowParams,omitempty"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is workflowParams meant to be user-writable? Agent params get the not declared by Agent check, but this is preserve-unknown-fields on the spec with no webhook, so $(workflow.anything) on a standalone run splices whatever the user wrote into the Agent's prompt. Would a status field, or the ConfigMap the workflow controller already owns, be a better home for it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

You're right, and digging into it with Tekton as the reference convinced us the root issue is the ambient model itself, not just the missing validation. Tekton passes params by explicit wiring — each pipelineTask maps pipeline params into the Task's own declared params, and the TaskRun carries only the Task's params (no ambient "pipeline params" bag); referenced Tasks must have params passed explicitly, and cross-stage data flows through results. We're doing the opposite: an ambient workflowParams bag on the AgentRun spec plus a $(workflow.*) scope in agent-facing text.

Rather than band-aid it with a webhook, filed #181 to adopt Tekton-style explicit stage wiring: AgentWorkflowStage.params map $(workflow.*) into each stage's declared agent params, the controller stamps the resolved values as the stage AgentRun's own spec.params, and workflowParams / the $(workflow.*) agent scope are removed (the guide keeps $(workflow.*), rendered controller-side). That dissolves this concern at the root — a standalone run then has only agent params, already validated against declarations. It supersedes part of ADR 0009/0018, so it's its own ADR rather than expanding #169; keeping the current mechanism as the dev-preview stopgap here.

konveyoriov1alpha1 "github.com/konveyor/agentic-controller/api/v1alpha1"
)

// podWithExit builds a pod whose agent container has terminated with the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The comment says termination message but the signature is exit code only and Message is hardcoded empty, so the terminationData branch isn't covered anywhere. The workflow tests also still set ConditionTypeReady on the AgentRun at 185, 214, 340 and 529.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Both addressed. Extracted the termination-message parsing into terminationDataFromPod and added TestTerminationDataFromPod covering the previously-uncovered branch: a JSON object is captured verbatim, while an array/scalar/invalid/empty message yields nothing (the object-only guard from the type: object fix). podWithExit takes a message again so the blob is exercised. And the workflow tests no longer set ConditionTypeReady on the child AgentRun — they now drive stages via the Succeeded condition the sequencer reads.

Comment thread skills-lock.json Outdated
@djzager
djzager force-pushed the impl/115-116-crd-params-execution branch from 93b6c4d to 092a662 Compare August 26, 2026 15:56
@djzager

djzager commented Aug 26, 2026

Copy link
Copy Markdown
Member Author

Thanks for the thorough read. Confirming the split: #169 is intentionally controller-only — it writes /run/konveyor/params.json and defines the exit-code / terminationData contract, but nothing here consumes it. The harness half is #174 (Savitha): it replaces the KONVEYOR_PARAM_MAX_TURNS read with a generic params.json loader (#117), and adds limit enforcement, exit 0/1/2, and the /dev/termination-log usage blob (#118). So config.go:105 reading the old env var is expected on main until #174 lands.

On the doc leftovers: the AgentRun godoc at agentrun_types.go:209 is ours and stale — fixing it here. harness/README.md:69 and skills/verify/SKILL.md are harness/skills-side and belong with #174; flagging them there so they land with the consumer change.

@fabianvf fabianvf left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

savitharaghunathan added a commit to savitharaghunathan/agentic-controller that referenced this pull request Aug 26, 2026
Replace the hard-coded KONVEYOR_PARAM_MAX_TURNS env read with a generic
loader of the controller's three-section /run/konveyor/params.json
(ADR 0009, delivered by PR konveyor#169), and append a "## Parameters" section
to the agent prompt so workflow/agent values are visible with zero
skill involvement.

Closes konveyor#117

Assisted-By: Claude Code <noreply@anthropic.com>
Signed-off-by: Savitha Raghunathan <saveetha13@gmail.com>
@djzager
djzager force-pushed the impl/115-116-crd-params-execution branch from c43887b to d1af9f6 Compare August 26, 2026 20:46
… scope

Amends ADR 0011 (execution controls) and ADR 0009 (parameter delivery),
surfaced while implementing konveyor#115/konveyor#116/konveyor#119:

- Execution fields (mode, maxTurns, maxCost) resolve onto AgentRun.spec;
  the workflow controller stamps stage-resolved values. Limits are Agent
  defaults; mode stays an execution-time concern (AgentRun/stage only).
- Terminal outcome moves to a Knative/Tekton-style Succeeded condition
  (Unknown while running, True/False terminal); exit 2 -> LimitReached.
  Ready is removed from AgentRun; ACPReady (serving) and phase (coarse
  mirror) are unchanged.
- The workflow guide renders from workflow.* only; stage instructions
  and the agent prompt render from both scopes.

Signed-off-by: David Zager <david.j.zager@gmail.com>
Implements ADRs 0009/0011 (with the 0018 amendment) on the controller.

Params (konveyor#115/konveyor#116):
- Rename AgentParam/AgentRunParam/AgentParamType -> Param/ParamValue/
  ParamType (Go only; CRD field names unchanged).
- Deliver typed params via /run/konveyor/params.json (workflow/agent/
  execution sections) mounted read-only; remove KONVEYOR_PARAM_* env.
- $(scope.name) substitution across the agent prompt, run instructions,
  workflow guide, and stage instructions; unresolved refs and bad
  coercion fail the run terminally with InvalidParams.
- Workflow-level params on AgentWorkflow; resolved workflow params
  stamped onto stage AgentRuns.

Execution controls (konveyor#115):
- ExecutionLimits (maxTurns, maxCost) as Agent defaults, overridable per
  stage; ExecutionSpec (mode + limits) on AgentRun and stages. The Agent
  cannot declare mode (ADR 0011/0018). Single resolveExecution rule.

Terminal model (konveyor#119):
- AgentRun gains a Succeeded condition (Unknown while running, True/False
  terminal) and opaque terminationData read from the pod termination
  message. Harness exit 0/1/2 -> Succeeded / Failed / LimitReached. Ready
  is dropped from AgentRun; ACPReady and phase are unchanged.

Workflow snapshot (konveyor#87):
- AgentWorkflowRun snapshots the full stage definitions, guide, and
  params at init and executes from the snapshot, so a mid-run workflow
  edit cannot change stages already planned.

Signed-off-by: David Zager <david.j.zager@gmail.com>
- coerceParams: a declared-but-unset param resolves to empty in
  $(scope.name) instead of failing as an unresolved reference; both
  scopes stringify the canonical coerced value via one shared rule.
- AgentRun: validate params/gateway only before Sandbox creation, so a
  live run is not re-validated against a since-edited Agent; params
  ConfigMap uses CreateOrUpdate; terminationData is stored only when the
  message is a JSON object (CRD type: object).
- Workflow sequencer keys off the child AgentRun's Succeeded condition,
  not phase; shed the legacy Ready condition on live AgentRuns.
- Workflow: workflow-declared params are no longer flagged as skipped;
  permanent workflow config errors fail the run terminally
  (InvalidParams) instead of requeuing forever.
- ParamsFilePath is the single source of truth for the mount path/key.
- Docs: AgentRun godoc no longer says params are injected as env vars.
- Tests: canonical stringify, unset-param, terminationData object-only,
  and workflow stages driven via Succeeded.

Signed-off-by: David Zager <david.j.zager@gmail.com>
konveyor#143 (merged) surfaces the harness's human-readable termination message
(e.g. a non-git source) on the terminal condition. Our Succeeded model
replaced the Ready condition, so thread that message through
setTerminalOutcome: it is preferred over the generic reason on a failure
outcome and now lands on Succeeded=False. terminationData still captures
a JSON-object usage blob; a plain-string failure message is surfaced as
the condition message. Updated the konveyor#143 test to assert Succeeded.

Signed-off-by: David Zager <david.j.zager@gmail.com>
@djzager
djzager force-pushed the impl/115-116-crd-params-execution branch from d1af9f6 to c469132 Compare August 26, 2026 20:53
@djzager
djzager merged commit 22fee49 into konveyor:main Aug 26, 2026
25 checks passed
djzager pushed a commit that referenced this pull request Aug 26, 2026
…174)

Replace the hard-coded KONVEYOR_PARAM_MAX_TURNS env read with a generic
loader of the controller's three-section /run/konveyor/params.json (ADR
0009, delivered by PR #169), and append a "## Parameters" section to the
agent prompt so workflow/agent values are visible with zero skill
involvement.

Closes #117

Assisted-By: Claude Code <noreply@anthropic.com>

<!--
## PR Title Prefix

Every **PR Title** should be prefixed with an emoji alias to indicate
its type.

- Breaking change: ⚠️ (`⚠️`)
- Non-breaking feature: ✨ (`✨`)
- Patch fix: 🐛 (`🐛`)
- Docs: 📖 (`📖`)
- Infra/Tests/Other: 🌱 (`🌱`)
- No release note: 👻 (`👻`)

For example, a pull request containing a new feature might look like
`✨ Add agent status reporting`.

Use the **alias** (`✨`) not the emoji character directly.

## Changelog Fragment

PRs with `✨`, `🐛`, or `⚠️` prefixes require a
changelog
fragment in `changes/unreleased/`. Create one with:

make changelog-create NAME=<pr-number>-<short-description> KIND=<kind>

Or copy `changes/template.yaml` to `changes/unreleased/<name>.yaml` and
edit it.

For more information, see the Konveyor
[Versioning
Doc](https://github.com/konveyor/release-tools/blob/main/VERSIONING.md).
-->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Harness parameters and execution controls are now loaded from a
controller-provided JSON file.
* Workflow and agent settings appear in a dedicated “Parameters” section
of the agent prompt.
* Added maximum-turn and cost-limit enforcement with graceful handoff
behavior.
* Harness exits distinguish successful, failed, and limit-reached
outcomes.
  * Usage summaries are recorded for every execution.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Savitha Raghunathan <saveetha13@gmail.com>
ibolton336 added a commit to konveyor/tackle2-hub that referenced this pull request Sep 3, 2026
## What

Update `github.com/konveyor/agentic-controller/api` from the
pre-parameter-delivery commit to the merge commit of
konveyor/agentic-controller#169.

This lets the Hub agentic endpoints deserialize and forward the
structured parameter and execution-control fields introduced by that PR,
including workflow parameter declarations and `AgentRun.spec.execution`.

## Validation

- `go test github.com/konveyor/agentic-controller/api/v1alpha1`
- Full Hub package compilation is blocked in the lightweight dev
container because CGO/sqlite3 is unavailable; no Hub source behavior
changes are included.

## Follow-up

A companion tackle2-ui PR consumes the updated contract in the
run-creation forms.

Signed-off-by: Ian Bolton <ibolton336@users.noreply.github.com>
Signed-off-by: Ian Bolton <ibolton@redhat.com>
ibolton336 added a commit to konveyor/tackle2-ui that referenced this pull request Sep 11, 2026
## What

Integrate the parameter delivery and execution-control contract from
konveyor/agentic-controller#169 into the agentic console.

- align the handwritten TypeScript contract with the controller API
- serialize standalone run supervision mode under `spec.execution.mode`
- render typed standalone Agent params
- show workflow-scoped and stage-Agent params in separate form sections
- allow stage-specific params instead of requiring every stage Agent to
declare them
- add request serialization coverage

The browser still does not create `params.json`; it supplies run values
through Hub, and the controller validates/coerces them and writes
`/run/konveyor/params.json`.

## Dependency

- konveyor/tackle2-hub#1129

## Validation

- `npm run lint` (passes with the repository's 20 existing warnings)
- `npm run build`
- `npm test -w client -- --runInBand
src/app/api/rest/agent-runs.test.ts` (2 tests pass)

## Contract note

Issue #3524 mentions `allowedModes`, but the reconciled #169/ADR 0018
contract has invocation-level `auto | approve` mode and
Agent/stage-owned budget limits. This change follows the merged
controller API.

Closes #3524

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added automatic and approval-required supervision modes when creating
runs, including approval guidance.
  * Added structured validation for agent and workflow run parameters.
* Workflow run forms now distinguish workflow-level parameters from
stage-agent parameters.
* Added support for execution limits, Git configuration, and parameter
metadata.

* **Documentation**
* Added guidance for defining, validating, delivering, and
troubleshooting agentic run parameters and supervision modes.

* **Tests**
* Added coverage for run creation, parameter serialization, validation,
and approval-mode behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Ian Bolton <ibolton@redhat.com>
djzager pushed a commit that referenced this pull request Sep 16, 2026
… says why (#232)

Closes #231.

## Problem

goose renders a rejected model call as assistant prose and ends the turn
normally:

```
Ran into this error: Server error: Failed to call Bedrock: … UnrecognizedClientException …
"The security token included in the request is invalid." …
```

The harness recognised that text since #170 but only logged a WARN and
told viewers to *"see the pod log"*. Exit 0, `stopReason=end_turn`, run
**Succeeded** in green, Gateway **Ready** (Bedrock cannot be probed,
#222). On the demo cluster today three runs died this way on a rotated
AWS key and were read as a broken plan ladder, because nothing anyone
could see said otherwise.

## Change

**Harness**

- `classifyOutcome` takes `providerError` (the existing
`PromptResult.ClosingProviderError()`). With `TurnsUsed == 0` the stage
is `outcomeFailed`: the model never worked, so nothing succeeded. A
provider error *after* real work keeps the turn's outcome — that is
#129's broader contract — but still gets the notice below.
- The termination log's `stopReason` becomes `provider error: <the
provider's error line>` (redacted, cut at 1000 runes). The line is the
failure itself: `acp.ProviderErrorText` skips narration goose streamed
ahead of it (*"Here is the plan:"* … *"Ran into this error: …"*), and
`LooksLikeProviderError` is built on it, so detection and selection
cannot drift. The cap is sized for #231's real line: goose renders the
AWS SDK error with Debug formatting, ~500 runes with *"The security
token included in the request is invalid."* past rune 160, which a
200-rune cut lost.
- The viewer notice quotes the same line — *"the model provider rejected
the call — no work was done: Ran into this error: … security token … is
invalid."* — instead of pointing at a pod log a console user cannot
open. The stage-end notice and the returned error name it too.

**Controller**

Unchanged. The termination blob stays opaque to the controller (ADR
0011, ADR 0018, `CONTEXT.md`): it is stored verbatim on
`status.terminationData`, and `setTerminalOutcome` copies the
termination message onto the `Succeeded=False` condition as before.
Showing `terminationData.stopReason` beside the phase instead of the raw
blob belongs in the UI, which knows the harness schema — a tackle2-ui
follow-up.

Net effect: the run is **Failed** instead of Succeeded, and the
provider's error line — the sentence that tells the operator to rotate
the Gateway credential — is in the viewer notice and in the termination
blob's `stopReason`, which the console shows raw beside the phase until
the UI renders it.

Exit-code contract (ADR 0011) is unchanged: this is an exit-1 failure,
not a new code. #170's *"exit status is unchanged on purpose … belongs
with the Succeeded condition work in #119"* deferral is what this
resolves; #119 landed in #169.

## Verification

- `gofmt`, `go vet`, `go test ./...` green in `harness/`; `go vet` plus
`TestSetTerminalOutcome`, `TestPodTerminationMessage`,
`TestTerminationDataFromPod` green in `internal/controller/`.
- New cases: `TestClassifyOutcome` — provider error with no turns →
failed; after 7 turns → succeeded; at the native limit → limitReached
still wins. `TestProviderErrorText` — the failure without the narration
ahead of it, in both of goose's forms; a quoted phrase is not a failure.
`TestProviderErrorSummary` — narration in the same or an earlier
message, the trailer form, #231's Bedrock line kept whole, the 1000-rune
cut.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: ibolton336 <ibolton@redhat.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.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

5 participants