UP-5022: link tasks against pre-consolidation platforms - #2272
claude[bot] wants to merge 4 commits into
Conversation
The engine creates two datasets per task (traces + guardrails) and posts one model linked to both. A platform older than 1.4.2592 requires an Arthur shield model to be linked to exactly one dataset and rejects that with a 400, so both create_task and link_task fail against it. Probe the platform's release version via GET /api/health once per job and emit the legacy single-dataset shape below the boundary, named after the task with no suffix the way it was pre-consolidation. Because release_version is environment-sourced and can be "unknown" or unreachable, an unreadable version still attempts the current shape and falls back when the platform answers with the one-dataset constraint; the rejected attempt's rollback clears its own datasets, so neither the retry nor a failed retry leaves orphans. On the create path the raw job spec still carries task_type, which says which dataset the task used to get. The link path has no such signal and the task's original kind is no longer recoverable, so that fallback sits behind LEGACY_SINGLE_DATASET_FALLBACK_PROBLEM_TYPE pending a product decision. Against 1.4.2592+ the two-dataset path is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019EPaWvFdzEc4AejyuJV3yv
Review follow-ups on the compatibility shim. No change to the behaviour against 1.4.2592 and later. - _parse_platform_release_version accepted digits that int() then rejects. str.isdigit() is true for characters like superscript digits, so a version string containing one passed the guard and raised ValueError out of the probe instead of reading as unknown. isdecimal() is the predicate that matches what int() accepts. - The legacy spec lookup raised a bare StopIteration if LEGACY_SINGLE_DATASET_FALLBACK_PROBLEM_TYPE named a problem type no task dataset uses. That constant is meant to be flipped, so say what is wrong. - The probe-says-old path logged nothing. It is the path that creates a task without its complementary dataset, and the job log is the only place that is visible, so warn with the chosen dataset type. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019EPaWvFdzEc4AejyuJV3yv
| # the task's original kind is unrecoverable. PENDING PRODUCT DECISION (UP-5022) — | ||
| # ARTHUR_SHIELD keeps guardrails, AGENTIC_TRACE would keep traces and evals instead. | ||
| # Flip this one value to change it. | ||
| LEGACY_SINGLE_DATASET_FALLBACK_PROBLEM_TYPE = ModelProblemType.ARTHUR_SHIELD |
There was a problem hiding this comment.
When I linked an agentic task with 724 traces against old Scope, this default made it guardrails-only, so none of its traces showed up. The traces are still in GenAI, and a platform upgrade to 1.4.2594 adds the traces dataset back. Is that OK for United until then, or should linking default to traces?
There was a problem hiding this comment.
This is useful evidence — a concrete task with 724 traces going guardrails-only is exactly what the open decision needed, and I've recorded it in the PR body under "Which dataset the link path keeps".
I'm not changing the default here. It sits behind the single documented constant LEGACY_SINGLE_DATASET_FALLBACK_PROBLEM_TYPE, so switching to AGENTIC_TRACE is a one-line change whenever the call is made. The decision is the PR author's and has been put to them with your evidence attached.
Leaving this thread open until they answer.
Generated by Claude Code
There was a problem hiding this comment.
More context for this decision. On pre-consolidation platforms, the link-existing flow in the platform UI lists each task twice, as " - traces" and " - guardrails", so the user explicitly chooses a kind. But the link request carries only task_id (unify-frontend useCreateGenAiModel.ts, calling projectCreateModelLinkTask), and CreateModelLinkTaskJobSpec has no field for it. So the user's choice never reaches the engine, and this constant overrides it every time.
In practice: a user who links an agent task and picks "traces" gets a guardrails-only model. Every later fetch or metrics job reads only inferences, so the task's traces never appear on that platform until it's upgraded.
Options:
(a) default to AGENTIC_TRACE, since most tasks linked after consolidation are agent tasks;
(b) infer the kind from the task itself, e.g. no guardrail rules but traces present → traces;
(c) keep guardrails and accept the gap until the platform upgrades.
There was a problem hiding this comment.
This changes the shape of the decision, and I've checked the part of it that sits in this repo.
Confirmed: CreateModelLinkTaskJobSpec has no field for the chosen kind. In arthur_common/models/task_job_specs.py it is exactly job_type, task_id, connector_id, onboarding_identifier, and the generated binding agrees — arthur_client/api_bindings/models/create_model_link_task_job_spec.py declares __properties = ["job_type", "task_id", "connector_id", "onboarding_identifier"]. Compare CreateModelTaskJobSpec on the create path, which does still carry the legacy task_type the engine reads. So the engine has no channel through which the user's selection could arrive, and LEGACY_SINGLE_DATASET_FALLBACK_PROBLEM_TYPE is necessarily what decides it. I didn't go into the frontend repo, so the UI half of your description — the duplicated traces/guardrails entries and the hook sending only the task id — I'm taking as reported rather than confirming.
That makes this a three-way choice rather than a default to flip. Your (a) and (c) are one-line changes to the constant; (b) is a different thing — a behaviour change, needing a signal the engine can actually read (task rules and traces off the task itself, not the job spec) and its own correctness discussion about what happens when the signal is ambiguous. Worth separating from (a)/(c) when the call is made.
Still not mine to decide, and unchanged in this push. The option set and your worked example (link an agent task, pick traces, get a guardrails-only model whose traces stay invisible until upgrade) are recorded in the PR body and have been relayed to the PR author. Leaving this thread open.
Generated by Claude Code
|
Tested against old Scope (1.4.2593, and with the reported version set to 1.4.2582, 1.4.2591 and "unknown"). Creating and linking apps works and leaves no leftover datasets. Not tested: creating a new agentic app, because the current UI no longer sends the agentic flag. Main asks: change the cutoff to 1.4.2594, add a boundary test, and decide guardrails vs traces for linking. |
The consolidation commit 18896423 first appears in version tag 1.4.2592, but the earliest release tag that ships it is 1.4.2594-release. Verified against arthur-scope: scope/app_plane/app/operators/models_operator.py is byte-for-byte identical at 1.4.2591-release, 1.4.2592-release and 1.4.2593-release, all three still carrying the "must be linked to exactly one dataset" guard at lines 390-395, and the guard is first absent at 1.4.2594-release. At the old 2592 cutoff a real 1.4.2593 platform read as consolidated, so the engine sent two datasets, the platform rejected them and the task was only created via the 400 fallback's rollback and retry. - cutoff constant moved to (1, 4, 2594), with the release-vs-version-tag distinction recorded in its comment - new parametrized test pins the exact boundary one patch apart: 1.4.2593 must take the single-dataset path straight from the version probe, with no rejection and no retry, and 1.4.2594 must keep the two-dataset path. Moving the constant in either direction fails it - the fallback's retry log line now carries the platform's own rejection text and status, read from the response body only, degrading gracefully when the body is absent, not JSON or not a mapping Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019EPaWvFdzEc4AejyuJV3yv
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueComment |
|
Re-tested 855a507 end to end against a pre-consolidation Scope release (1.4.2593), driven from that release's own UI: Create, agentic: a single traces dataset via the version probe, no retry. Traces are ingested; metrics and alert jobs complete; the dashboard shows the data. Open items: Deploy-tag version strings make the numeric cutoff ambiguous around 1.4.2592–1.4.2593 (details in the cutoff thread). |
The /api/health version cutoff cannot be made correct. arthur-scope CI sets RELEASE_TAG from the build's git tag with only a trailing "-release" stripped (.gitlab-ci.yml, set-release-tag), and that value is what /api/health reports (scope/app_plane/Dockerfile -> app/api_models/health.py), so a deploy build reports e.g. "1.4.2592-ab71a845-deploy-gcp-united". Deploy tags are cut off the same version number as the release preceding them: 1.4.2592-ab71a845-deploy-gcp-united contains the consolidation commit 18896423, while 1.4.2592-release does not. One version number therefore names platforms of both kinds and no numeric cutoff can be right for both - the customer's own deploy-gcp-united builds are exactly this case, and would have been sent the single-dataset shape despite accepting two, leaving the task half-configured until the platform's own migration ran. Always attempt the consolidated two-dataset shape and let the platform's 400 select the legacy shape. Against a current platform the requests are unchanged; against a pre-consolidation platform the cost is one rejected attempt, whose datasets are rolled back before the retry. Removes the health probe, the version parser, the cutoff constant and the boundary tests that pinned 2593/2594. The fallback dataset-type constant and its pending product decision are unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019EPaWvFdzEc4AejyuJV3yv
Requested by Tal Erez · Slack thread
Before: with the latest engine against a pre-consolidation platform, linking or creating a task fails. The engine creates two datasets for the task (traces and guardrails) and then posts one model linked to both; the older platform requires an Arthur shield model to be linked to exactly one dataset, so it rejects the model create call with a 400. The engine rolls both datasets back and the job errors out. The practical consequence is that a customer cannot take an engine upgrade without upgrading the platform at the same time.
After: the engine always attempts the consolidated two-dataset shape and, when the platform rejects it with the one-dataset constraint, creates the single-dataset form that platform accepts. Task creation and linking succeed against both old and new platforms. There is no change in behaviour against a consolidated platform: the two-dataset attempt is the only attempt, with the same requests, in the same order, with the same payloads.
How: try-then-fallback, with no version probe. The engine posts the two datasets and the model linked to both. If that model create comes back as a 400 carrying the platform's
must be linked to exactly one datasetconstraint, the attempt's own rollback deletes the datasets it created, and the engine retries with the single-dataset shape: one dataset, named after the task with no suffix the way it was pre-consolidation, and a model linked only to that. On the create path the raw job spec still carriestask_type, which says exactly which dataset the task used to get, so that path reproduces the old shape rather than guessing. Neither the rejection path nor a failed retry leaves orphaned datasets. The retry is logged with the platform's own rejection text and status, so the reason is visible in the job log — on an old platform this fallback is the normal route, not an exception. A 400 that is not the one-dataset constraint is not retried. The cost of dropping the probe is one rejected request on genuinely old platforms.Open decisions
CreateModelLinkTaskJobSpeccarries onlyjob_type,task_id,connector_idandonboarding_identifier, with no field for the task kind, andTaskResponse.is_agenticis now alwaysTrue— so the fallback dataset type sits behind the module-level constantLEGACY_SINGLE_DATASET_FALLBACK_PROBLEM_TYPEintask_management_job_executors.py, defaulting to the guardrails dataset (ARTHUR_SHIELD). Guardrails or traces is a product decision and is still open. Flipping it toAGENTIC_TRACEis a one-line change to that constant. Review evidence for the decision: linking an agentic task with 724 traces against an old platform made it guardrails-only, so none of those traces were visible until the platform is upgraded (the traces themselves are retained in GenAI). Review also established that on pre-consolidation platforms the UI's link-existing flow asks the user to choose a kind, but that choice is not carried on the link request and so never reaches the engine — which makes this a three-way choice (default to traces, infer the kind from the task, or keep guardrails and accept the gap) rather than a simple default flip.Resolved during review
The numeric cutoff was removed. The boundary was first written as 1.4.2592, then corrected in review to 1.4.2594 — the earliest release tag shipping the consolidation commit
18896423, sincescope/app_plane/app/operators/models_operator.pyis byte-for-byte identical at1.4.2591-release,1.4.2592-releaseand1.4.2593-release, all three still carrying themust be linked to exactly one datasetguard at lines 390-395. Review then showed that no numeric cutoff can be sound at all. arthur-scope CI setsRELEASE_TAGfrom the build's git tag stripping only a trailing-release(.gitlab-ci.yml,set-release-tag), and that value is what/api/healthreports (scope/app_plane/Dockerfile→scope/app_plane/app/api_models/health.py), so a deploy build reports a string like1.4.2592-ab71a845-deploy-gcp-united, which the parser reduced to1.4.2592. Deploy tags are cut off the same version number as the release preceding them:1.4.2592-ab71a845-deploy-gcp-unitedand1.4.2592-283bf583-deploy-gcp-unitedboth contain18896423, while1.4.2592-releasedoes not. One version number therefore names platforms of both kinds. The customer's owndeploy-gcp-unitedbuilds are exactly that case, and under the cutoff would have been sent the single-dataset shape despite accepting two, leaving the task with one of its two datasets until the platform's own migration ran. The probe, the version parser, the cutoff constant and the boundary tests that pinned 2593/2594 are all removed in favour of try-then-fallback.Retry log line. The fallback's warning carries the platform's status and its own rejection text, read from the response body only (never
str(exc), which also renders response headers), degrading throughdata.detail→ JSONdetail→ raw body → a fixed message, whitespace-collapsed and truncated at 500 characters. Confirmed working in review against a real pre-consolidation platform.Build numbers. App-plane 75039 is
1.4.2594-releaseand 75411 is1.4.2603-release(fromscope-version-manifest.jsonat those tags), answered in review. Informational — no longer load-bearing now that no version number is compared.🤖 Generated with Claude Code
https://claude.ai/code/session_019EPaWvFdzEc4AejyuJV3yv
Generated by Claude Code