Skip to content

Bedrock model cards: accept the new Legacy period and EOL shapes, tighten fail-closed checks - #107

Merged
thossullivan merged 10 commits into
mainfrom
fix/bedrock-bare-legacy-period
Oct 5, 2026
Merged

thossullivan merged 10 commits into
mainfrom
fix/bedrock-bare-legacy-period

Conversation

@thossullivan

@thossullivan thossullivan commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Fixes the scheduled feed-refresh failures logged on #106 (runs 36417815597 and 37309158163), and closes every finding from two Codex adversarial review passes.

Cause

  • The Claude Opus 5.5 and Sonnet 5.5 Bedrock cards say Legacy period: 6 months. The parser required at least N months|days.
  • After that fix, a live run stopped on the GPT-6.1 Sol card: Model EOL date: Not announced.

Changes (refresh/distributors.mjs)

  • "at least" is optional in the Legacy period check. Number and unit must agree and be non-zero (1 month, 1 day, N months|days for N ≥ 2).
  • BEDROCK_CARD_NO_EOL treats Not announced / Not announced. exactly like N/A.
  • Regional Legacy (EOL: …) cells are validated before any skip. A regional EOL with no exact Model EOL date now throws (review pass 1, High: it was silently dropped).
  • An exact Model EOL date earlier than a day-precision EOL no sooner than floor now throws.

Tests (refresh/test/run.mjs)

  • Acceptance for both live shapes, and near-miss shapes that must still fail closed.
  • Regional EOL with N/A, Not announced., tentative, and missing EOLs, alone, with a launch date, and with a floor.
  • All-N/A Model ID card with a malformed regional cell; error precedence; Legacy: EOL with matching regional cells; EOL-before-floor; period grammar.
  • The new tests fail on the previous parser.

Verification

  • npm test passes.
  • Live refresh.mjs --check for providers and for aws-bedrock,vertex-ai,azure-ai-foundry complete with exit 3 (changes pending), not exit 1. The live distributor diff is byte-identical across all three commits.

🤖 Generated with Claude Code

… cards

The scheduled feed refresh failed on 2026-09-28 and 2026-10-05 (issue 106).
The Claude Opus 5.5 and Sonnet 5.5 cards say "Legacy period: 6 months"
without "at least", which the card parser rejected. With that fixed, a
live run then stopped on the GPT-6.1 Sol card: "Model EOL date: Not
announced.".

"at least" is now optional in the Legacy period check. The period is only
validated, never used for a date, so no feed value changes. "Not
announced" (with or without the trailing period) is treated exactly like
N/A. Near-miss shapes such as "Not yet announced", "TBA", "six months"
and "6 weeks" still fail closed.

Verified: npm test passes; live provider and distributor --check runs
both complete (exit 3, changes pending) instead of exit 1.

Implemented directly by Claude rather than delegated to Codex: two regex
changes plus tests, below the handoff threshold.
Codex review of PR 107: once "Not announced." passed as N/A, a card with
that EOL and a regional "Legacy (EOL: YYYY-MM-DD)" cell lost the regional
date. It skipped as "no lifecycle fields" (region cells were scanned after
that skip), skipped as "no day-precision date", or with a floor emitted a
tentative active record that dropped the regional shutdown and legacy
status. Before PR 107 that card threw. The same gap already existed for
N/A and for "No sooner than" tentative EOLs.

Region cells are now scanned before any skip, and a regional EOL with no
exact Model EOL date throws. Exact-date agreement is unchanged.

The live distributor --check diff is byte-identical before and after this
change, so no current card has that shape.

Not fixed (pre-existing, out of scope): the Legacy period check accepts
"1 days" and "0 days"; an exact EOL earlier than the "EOL no sooner than"
floor is not rejected.
Pass 1 Low and missing check, pass 2 Lows:
- Legacy period number and unit must agree and be non-zero: "1 month",
  "1 day", "N months|days" for N >= 2. "1 days", "2 month", "0 days" and
  zero-padded numbers now fail closed.
- An exact Model EOL date (plain or Legacy:) earlier than a day-precision
  "EOL no sooner than" floor now throws. A month-only floor is still
  ignored, as before.
- Regional "Legacy (EOL: ...)" cells are validated before the "no model
  ids" skip, so a malformed cell on an all-N/A card fails closed. A valid
  regional cell on such a card still skips: there is no id to record.
- Tests pin the intended error precedence (malformed regional cell is
  reported before an invalid Model EOL date), and cover Legacy: EOL plus
  matching regional cells.

The live distributor --check diff is byte-identical before and after.
@thossullivan thossullivan changed the title Accept bare Legacy periods and "Not announced." EOLs on Bedrock model cards Bedrock model cards: accept the new Legacy period and EOL shapes, tighten fail-closed checks Oct 5, 2026
Codex review pass 3 of PR 107:
- Medium: only cells starting with the exact text "Legacy (EOL:" were
  validated, so "legacy (EOL: ...)", "Legacy (EOL : ...)" and
  "Legacy(EOL: ...)" bypassed the regional check and could yield an
  active record. Cells are now detected case- and space-insensitively,
  then must match the exact grammar or the card fails closed.
- Low: the Legacy period number had no upper bound. It is now one to
  three digits.

Live distributor --check diff is byte-identical before and after.
…grammar

Codex review pass 4 of PR 107 raised two opposing P2s about the regional
EOL detector: it still missed shapes such as "US East (N. Virginia):
Legacy (EOL: 2028-01-01)" and "Legacy (End of life: ...)" (silent loss),
while it could also throw on a glossary cell like "Legacy (EOL policy)".

An inventory of all 133 live cards on 2026-10-05 settles it. Regional
status cells only hold yes/no icons, "Supported", "Not supported" and
"Legacy (EOL: YYYY-MM-DD)"; no cell outside the regional tables mentions
legacy or EOL; and the regional tables use three different headers, so
scoping by header would miss some.

Any td that mentions legacy, EOL or end of life must now match the exact
regional grammar, or the card fails closed. The glossary-cell false
positive is accepted deliberately: nothing live has one, and a stopped
refresh is preferred to a lost shutdown date.

Live distributor --check diff is byte-identical before and after.
… cells

Codex review pass 5 of PR 107:
- P3 regression from ce945bb: a valid id such as vendor.legacy-model-v1:0
  or vendor.eol-model-v1:0 made its own Model ID cell look like a
  regional status and stopped the refresh. Cells whose text is only
  Bedrock ids (Model ID, Geo and Global inference ID) are now skipped.
- P2: a regional status in img alt, title or aria-label text, in a row
  header th, or split by inline markup ("Leg<span>acy</span>") bypassed
  detection. All of those are now read.
- P3: "end of lifecycle", "end of support", "E.O.L." and "retired" are
  now detected alongside legacy, EOL and end of life.

A detected cell still has to be exactly "Legacy (EOL: YYYY-MM-DD)" in its
visible text, or the card fails closed. The live distributor --check
diff over all 133 cards is byte-identical before and after, so no live
card uses any of the newly detected shapes.
Comment thread refresh/distributors.mjs Fixed
…e word check

Codex review pass 6 of PR 107 found three regressions and one gap in
ff6b70e's detector:
- Digits and underscores counted as word boundaries, so "us.vendor.eol2-
  v1:0 N/A", "legacy_model" ids and "EOL2" headers threw. Boundaries now
  exclude [a-z0-9_], and Bedrock id tokens (first segment of two or more
  characters, so "E.O.L." is not mistaken for one) and URL tokens are
  removed before the check.
- The attribute regex found "title" inside data-title and "alt=" inside
  another attribute's quoted value. Attributes are now tokenized per tag,
  which also reads unquoted alt/title/aria-label values (the gap).
- Visible text was entity-decoded twice, so "l&amp;#101;gacy" read as
  "legacy". Each candidate is now decoded exactly once.

Live distributor --check diff over all 133 cards is byte-identical.
…butes

Codex review pass 7 of PR 107 (no regressions; one bug): the per-tag
matcher stopped at the first >, so <img alt="Legacy > Active"> cut the
alt value short and the lifecycle word was missed, where c1f3e08's parent
had thrown. Tags are now matched quote-aware.

Live distributor --check diff over all 133 cards is byte-identical.
…ifecycle word check

CodeQL js/incomplete-multi-character-sanitization (alert 19) flagged the
tag-stripped joined text, since <scr<x>ipt> collapses to <script. The text
is only matched against BEDROCK_LIFECYCLE_WORD and never rendered, but any
leftover < or > is now replaced with a space so the alert does not stand.
Word detection is unchanged; live distributor --check diff is identical.
Comment thread refresh/distributors.mjs Fixed
9d9defa did not clear CodeQL js/incomplete-multi-character-sanitization
(new alert 20 on the same line): a later replace of < and > is not
recognised as sanitising when entity decoding runs afterwards. The
joined text is now built by splitting the cell HTML on tags, decoding
each text segment once, and joining them. Same text for detection; no
tag-stripping replace remains. Live distributor --check diff identical.
@thossullivan
thossullivan merged commit cf760d4 into main Oct 5, 2026
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