Skip to content

Fixing muddle#90

Merged
pendingintent merged 21 commits intomasterfrom
fixing-muddle
Feb 8, 2026
Merged

Fixing muddle#90
pendingintent merged 21 commits intomasterfrom
fixing-muddle

Conversation

@pendingintent
Copy link
Owner

No description provided.

Copilot AI review requested due to automatic review settings February 8, 2026 22:44
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates timing validation and UI hints to reduce ambiguity around duration/window fields, aligning tests with ISO 8601-like durations (including USDM’s P-2D style).

Changes:

  • Enforced ISO 8601 duration validation for value, window_lower, and window_upper in Pydantic schemas.
  • Enforced an all-or-nothing rule for window_lower/window_upper/window_label and added extensive router test coverage.
  • Updated UI templates to guide users toward ISO 8601 duration inputs and clarified labels.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/test_timings.py Updates expectations to ISO 8601 duration strings for timing values/windows.
tests/test_routers_timings.py Adds validation test suite for duration formats and window all-or-none behavior.
src/soa_builder/web/templates/timings.html Adds input patterns/tooltips and rearranges fields; adds client-side “window group” enforcement.
src/soa_builder/web/templates/instances.html Clarifies “default condition” label copy in the instances UI.
src/soa_builder/web/schemas.py Adds duration regex validation + window all-or-none model validation.
src/soa_builder/web/routers/timings.py Converts Pydantic validation errors into HTTP 400 for UI endpoints.
.github/workflows/python-app.yml Bumps CI Python version.

Comment on lines +12 to +23
def _validate_iso8601_duration(v: Optional[str]) -> Optional[str]:
if v is None:
return v
v = v.strip()
if not v:
return None
m = _ISO8601_DURATION_RE.match(v)
if not m or not any(m.group(i) is not None for i in range(1, 8)):
raise ValueError(
f"'{v}' is not a valid ISO 8601 duration (e.g. P1D, P2W, PT8H, -P2D)"
)
return v
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

Returning None for blank strings breaks PATCH semantics in update_timing: when a client sends an empty string to clear value/window_lower/window_upper, the validator converts it to None, and the update logic interprets None as 'leave unchanged'. This makes these fields effectively non-clearable via UI/API. Consider preserving empty-string intent (e.g., return \"\" for whitespace-only inputs, or otherwise distinguish 'missing' vs 'provided but blank' for TimingUpdate).

Copilot uses AI. Check for mistakes.
member_of_timeline=member_of_timeline,
)
except ValidationError as exc:
msgs = "; ".join(e["msg"] for e in exc.errors())
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

The UI error message drops field context (loc), so users won’t know which input is invalid when multiple validations fail. Consider including loc alongside msg (e.g., window_upper: <msg>) when building msgs from exc.errors().

Suggested change
msgs = "; ".join(e["msg"] for e in exc.errors())
error_messages = []
for e in exc.errors():
loc = e.get("loc")
if loc:
loc_str = ".".join(str(part) for part in loc)
error_messages.append(f"{loc_str}: {e.get('msg', '')}")
else:
error_messages.append(e.get("msg", "Unknown validation error"))
msgs = "; ".join(error_messages)

Copilot uses AI. Check for mistakes.
<div style="display:flex;flex-direction:column;gap:2px;">
<label><strong>Value</strong></label>
<input name="value" placeholder="Value (optional)" />
<input name="value" placeholder="e.g. P7D (optional)" pattern="-?P-?(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(\d+H)?(\d+M)?(\d+S)?)?" title="ISO 8601 duration, e.g. P1D, P2W, PT8H, -P2D" />
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

The HTML pattern is more permissive than the server validator (e.g., it appears to accept bare P and PT because all components are optional), which will allow users to submit values that only fail after server-side validation. Consider tightening the pattern to align with the backend regex/logic (at minimum, require at least one numeric component and require a digit after T if T is present).

Copilot uses AI. Check for mistakes.
{% extends 'base.html' %}
{% block content %}
<h2>Timings for SoA {{ soa_id }}</h2>
<h2 title="Timing definitions should be read as 'the <Timing.relativeFromScheduledInstance> node is <Timing.value> <Timing.type of before or after> the <Timing.relativeToScheduledInstance> node.'">Timings for SoA {{ soa_id }}</h2>
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

Relying on a long title tooltip for core explanatory content is not very accessible (many screen readers don’t announce it consistently, and it isn’t keyboard discoverable). Consider rendering this help text visibly (or via an explicit help element with aria-describedby) rather than only in title.

Suggested change
<h2 title="Timing definitions should be read as 'the <Timing.relativeFromScheduledInstance> node is <Timing.value> <Timing.type of before or after> the <Timing.relativeToScheduledInstance> node.'">Timings for SoA {{ soa_id }}</h2>
<h2 aria-describedby="timings-help">Timings for SoA {{ soa_id }}</h2>
<p id="timings-help" style="font-size:0.9em;color:#555;max-width:60rem;">
Timing definitions should be read as “the &lt;Timing.relativeFromScheduledInstance&gt; node is &lt;Timing.value&gt; &lt;Timing.type of before or after&gt; the &lt;Timing.relativeToScheduledInstance&gt; node.”
</p>

Copilot uses AI. Check for mistakes.
@pendingintent pendingintent merged commit 2207d20 into master Feb 8, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants