<?xml version='1.0' encoding='utf-8'?>
<rss version="2.0">
  <channel>
    <title>NoteFlowAI project releases</title>
    <link>https://noteflowai.github.io/physical-ai-radar/updates/</link>
    <description>New features with verified releases and usage documentation.</description>
    <item>
      <title>fix: reject Inspect samples without a usable id instead of inventing case None</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/evalarc/0.17.10/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/evalarc/0.17.10/</guid>
      <description>evalarc diff and load_results now reject Inspect samples that have no usable identity. This covers JSON logs and .eval archives. Each sample is checked in recorded order, before any case bookkeeping and before the duplicate-sample guard. A sample that is not a JSON object fails with ValueError: 'Inspect log sample at position N is not an object (got TYPE)'. A sample whose id is missing, null, boolean, float, a list or an object fails with: 'Inspect log sample at position N has an invalid id SHOWN (TYPE): expected an integer or a string'. Both messages end with re-export guidance. The CLI prints evalarc: MESSAGE, exits 2, writes no diff.json, and leaves the --markdown file unchanged. Two null-id samples now get the invalid-id message instead of 'duplicate sample None epoch 1'. Integer, string and empty-string ids behave as before. docs/ci-gate.md and docs/ci-gate.zh-CN.md describe the accepted id types, both messages and the re-export command.

Controller-executed evidence: at base c722b75, tests/test_feature_b64744a8af0c.py had 13 failures and 1 pass. The failures were KeyError 'id', TypeError for non-object samples, no error for null/true/1.5/list/object ids, the misleading duplicate message, and CLI exit 1 (a gate verdict computed on an invented case) instead of 2. At candidate e835b05, the controller ran the feature tests, the full pytest suite, ruff check, ruff format --check, scripts/build_site.py and scripts/check_site.cjs. All exited 0. I did not run anything myself.</description>
      <pubDate>Tue, 13 Oct 2026 18:30:12 +0000</pubDate>
    </item>
    <item>
      <title>fix: reject duplicated promptfoo result rows instead of counting them as attempts</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/evalarc/0.17.9/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/evalarc/0.17.9/</guid>
      <description>evalarc diff now rejects a promptfoo --output JSON file that contains the same result row twice, instead of counting the copy as an extra attempt. The promptfoo loader keys each row of results.results[] by (testIdx, promptIdx, provider). The key is checked only when testIdx is an integer and not a bool. If a key repeats, loading stops with a ValueError, the same way duplicate Inspect samples are already handled. The message names the duplicated testIdx, promptIdx and provider and the test label. The label and provider are escaped and truncated with the existing _shown_id helper. The message also tells the user to re-export with promptfoo eval --output. evalarc diff exits 2 for a duplicate in either the baseline or the current file, prints the message to stderr and gives no gate verdict. A list or object promptIdx used to crash the loader with an unhandled TypeError. It is now reported as malformed input with exit 2. Output from --repeat, which gives each repeat a new testIdx, still loads as attempts of one test. The unmodified promptfoo example pair still exits 1. docs/ci-gate.md and docs/ci-gate.zh-CN.md describe the rule, exit 2 and how to recover. Controller results: before the fix, the new test file failed on the base code (9 failed, 5 passed). The duplicate cases did not raise, and the non-scalar promptIdx cases crashed with TypeError at results_diff.py line 543. At commit f18d1890 the controller ran the new tests, the full pytest suite, ruff check, ruff format --check, scripts/build_site.py and scripts/check_site.cjs, and all exited 0. The author had no tools and ran none of these checks.</description>
      <pubDate>Sun, 11 Oct 2026 14:35:37 +0000</pubDate>
    </item>
    <item>
      <title>fix: escape untrusted run status and metric name in the Markdown gate summary</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/evalarc/0.17.7/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/evalarc/0.17.7/</guid>
      <description>evalarc diff now escapes untrusted input-file text in specific parts of its main Markdown gate summary (summary.md, the --summary target and the GitHub Action job summary). It does not escape the whole summary. Three parts are covered: the run-status sentence for an unfinished current or baseline run, the 'Headline metric: ... from the source tool.' sentence, and the case and check identifiers in the change table. A new private helper, _inline_code_text, does the escaping. It turns pipes into \|, turns backticks into apostrophes, and replaces each CRLF, CR or LF with a space. A crafted status, metric name or identifier therefore stays inside one single-line code span. It cannot add a fake '### EvalArc' heading line. Before this change, table cells and the baseline-incomplete sentence already escaped pipes and backticks. They now also replace carriage returns. Ordinary values render the same as in 0.17.6, with backtick code spans. diff.json and the result dict still hold the raw values. gate_passed, blocking_changes and exit codes 0, 1 and 2 are unchanged. docs/ci-gate.md Limits describes the escaping. Checks were run by the controller, not by me. On base commit 20babd1, the new test failed 5 of 8 cases because a raw CR was still in the output. On candidate f4daf73, the controller ran six checks and all exited 0: the new test file, the full pytest suite, ruff check, ruff format --check, scripts/build_site.py and scripts/check_site.cjs.</description>
      <pubDate>Tue, 06 Oct 2026 19:05:09 +0000</pubDate>
    </item>
    <item>
      <title>fix: reject duplicate Inspect samples instead of counting them as extra attempts</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/evalarc/0.17.5/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/evalarc/0.17.5/</guid>
      <description>evalarc diff now rejects an Inspect AI log that contains the same (sample id, epoch) pair more than once. This applies to JSON logs and .eval archives. Before this change the copy was silently counted as an extra attempt, which inflated the assessed count and could hide a less_covered change. Ids are compared after str(), so an integer 7 and a string '7' in the same epoch count as duplicates. A duplicate in either the baseline or the current file makes load_results raise ValueError. evalarc diff then prints the message to stderr and exits 2 before any comparison, so no diff.json is written. The message names the sample id and epoch and points to re-exporting with inspect log dump. The id is shown with repr() and truncated to 80 characters, so control characters stay escaped. Samples without an integer epoch are not checked. The same id in different epochs still counts as repeated attempts. The diff.json schema, change kinds and gate logic are unchanged. docs/ci-gate.md and docs/ci-gate.zh-CN.md add this case to the exit-2 list. Both explain the 7 vs "7" collision and why counting a copy would misstate coverage and mask less_covered. Both give the recovery step inspect log dump LOG.eval &gt; LOG.json (or rerun) and note that samples without an integer epoch are not checked. The commit sets the version to 0.17.5. Controller results: on base 1d62bef, tests/test_feature_e0a029ce3b1b.py had 5 failures and 1 pass. Duplicates on either side exited 1 instead of 2, and the 7/'7' collision, duplicate .eval members and control-character ids raised no error. On candidate 37e8719 these all passed: that test file, the full pytest suite, ruff check, ruff format --check, scripts/build_site.py and node scripts/check_site.cjs.</description>
      <pubDate>Mon, 05 Oct 2026 15:35:59 +0000</pubDate>
    </item>
    <item>
      <title>show streamed time to first text in wp ai-chat-bedrock usage</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/ai-chat-for-amazon-bedrock/1.66.0/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/ai-chat-for-amazon-bedrock/1.66.0/</guid>
      <description>wp ai-chat-bedrock usage, in its default per-day mode, now ends with one Time to first text line for streamed answers. The line uses the existing first-token counters, which the dashboard already shows. It gives the number of answers measured, the average in ms and the median band (under 1 s, 1 to 2 s, 2 to 5 s, 5 to 10 s, or 10 s and over). It covers the --days window, capped at the 30-day retention period: --days=90 prints '30 days' and --days=1 prints '1 day'. With no measurements, after a reset, or when the stored count is missing or invalid, it reads 'unknown, no streamed answers recorded.' and never shows 0 ms. A missing or invalid sum prints 'average unknown'. Missing or invalid band counters print 'median unknown'. The table, Total and Prompt cache lines are unchanged. --by-model prints no latency line, and wp help ai-chat-bedrock usage describes the new line. The plugin is now version 1.66.0. Controller-run results: before the change, the new feature test failed 13 of its 14 cases, because the latency line did not exist yet. On commit a540596 the controller ran four checks and all passed. The feature test passed all 14 cases. bin/prerelease.sh passed with 62 suites, including cli.php and usage.php, plus the JS script tests, phpcs, version consistency at 1.66.0 and readme limits (changelog 4924 of 5000 characters). tests/tooling.php passed. bin/build-package.py built ai-chat-for-amazon-bedrock.1.66.0.zip with 122 files. I made no edits or runs at this stage; this text only describes those results.</description>
      <pubDate>Mon, 05 Oct 2026 14:31:38 +0000</pubDate>
    </item>
    <item>
      <title>seal one frame/channel finding into a LeRobot replay bundle</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/robot-reel/0.19.0/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/robot-reel/0.19.0/</guid>
      <description>Adds a --mark option to the existing robot-reel lerobot command: robot-reel lerobot EXPORT_DIR --mark FRAME --signal KEY/NAME [--signal ...] --note TEXT. It runs verify() on the export first. It then writes one finding.json containing: the episode, the zero-based frame, timestamps[frame], 1 to 8 key/name/value signals read from episode.json, the note, the manifest hash of episode.json, the package version (or null) and a fixed limitations statement. The sha256 of finding.json is added to manifest.json, and every other manifest entry stays the same. With --verify, alone or with --check-media / --check-source, the JSON result includes the sealed finding. An unmarked export gives the same result as before. Verify exits 2 with a named error if finding.json was edited without re-hashing, if finding.json is present but not listed in the manifest, if a listed finding.json is missing, if episode.json or a clip changed, or if a re-hashed finding disagrees with episode.json or breaks the input rules. Invalid --mark arguments, an already-marked export and any verify failure exit 2 and write nothing. If the manifest replacement fails, --mark removes the finding.json it placed in that run. Controller-executed checks: on base commit 4ed6d4b, the new feature test exited 1 with 'unrecognized arguments: --mark', so the test exercises the new option. On candidate commit 6f393b3, the controller ran robot_reel.pages --write, tests/test_feature_11ffe2c50b07.py, npm run check:js, the full Python unittest suite and node --test. All exited 0, and node --test reported 53/53 passed. My own action was writing this metadata; I ran no commands.</description>
      <pubDate>Sun, 04 Oct 2026 18:07:01 +0000</pubDate>
    </item>
    <item>
      <title>fix: label README baseline fallback on days with no live picks</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/physical-ai-radar/1.4.1/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/physical-ai-radar/1.4.1/</guid>
      <description>On a day with no live picks, the README radar block in all three languages (README.md, README.en.md, README.ja.md) now explains the empty day the same way the frozen daily page does. readme_block in pairadar/render.py keeps the existing day heading and generated/window line. After them it adds the localized no-items sentence as a Markdown quote, then a bold label from the existing glossary baseline string, then the curated baseline table, evidence legend, reasons and links. Before this change, the baseline entries appeared under the day's heading, ranked and in the pick column, with no explanation. If there are no picks and no baseline, the block shows the no-items line and an empty table header without raising. Days with picks keep the previous rendering. docs/METHODOLOGY.md describes the empty-day README behavior. Checks run by the controller on candidate dfb3436 (base 047ef4a): before the change, the new test file failed 2 of 3 tests (the empty-day label test and the nothing-at-all test), which confirms the defect. After the change, rerender exited 0, the 3 tests in tests/test_feature_b41f745004f1.py passed, and the full suite of 484 tests passed.</description>
      <pubDate>Sun, 04 Oct 2026 14:07:11 +0000</pubDate>
    </item>
    <item>
      <title>require a reviewed Git source commit when opening a skill over MCP</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/dsh-skills-anywhere/0.18.0/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/dsh-skills-anywhere/0.18.0/</guid>
      <description>MCP open_skill accepts a new optional input, expected_source_commit. Set it to the receipt.source_commit value from an earlier load, and the skill is delivered only while the Git-source checkout HEAD is still that commit. When the field is supplied, the checks run in this order: format (40 lowercase hex characters, enforced by the input schema), Git-source origin, the existing expected_sha256 and expected_bundle_sha256 checks (wording unchanged), a provable commit from the existing sourceCommit() probe, and an exact match. Any failure returns isError true with an actionable message and no instruction text or receipt. A mismatch names both the expected and the found commit. A rejected digest still runs no Git command. Calls that omit the field behave as before. tools/list shows the field as optional.

Results from the controller's runs on commit 6a40534. These are the controller's executions, not mine. With the new test file run against the base code, 4 of its 6 tests failed: the moved-checkout, unprovable-commit, malformed-pin/non-Git and tools/list tests. The 2 tests that check unchanged existing behavior passed. On the candidate, every check passed: pnpm run build, the feature test (6 tests), pnpm run check (25 test files, 251 tests), pnpm run showcase:build and pnpm run showcase:check.

The prior review finding is addressed. As the logged test source shows, the malformed-pin test reads the resolved callTool result directly and asserts both isError true and the schema message (must be a full 40-character lowercase Git commit). It no longer wraps the call in a try/catch that would turn thrown errors into isError.</description>
      <pubDate>Fri, 02 Oct 2026 23:57:45 +0000</pubDate>
    </item>
    <item>
      <title>fix: fail the diff gate when the baseline run did not finish</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/evalarc/0.17.3/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/evalarc/0.17.3/</guid>
      <description>evalarc diff and the GitHub Action now fail the gate (exit 1) when the baseline run did not finish. Before this change, an Inspect baseline log whose status was not success could pass. Checks missing from it were classified as 'added', and 'added' never blocks. Change classification, counts and blocking rules are unchanged. When the baseline is incomplete, diff.json gains "baseline_incomplete": true and gate_passed is false. When the baseline is complete, the key is left out, so those reports keep their previous content apart from created_at and version.

After the existing summary line, default stdout prints: 'The baseline run is incomplete (status error); N check(s) appear only in the current run and were not compared. Rerun the baseline to completion; the gate fails.'

summary.md and --markdown add a line below the table: 'The baseline run did not finish (status `error`); the gate fails because checks missing from it cannot be compared. N check(s) appear only in the current run. Rerun the baseline to completion.' The heading reads 'EvalArc: the baseline run is incomplete' only when there are no blocking changes and the current run finished. Otherwise it shows the existing blocking heading or 'the baseline and current runs are incomplete'.

index.html shows 'Gate failed' with the reason 'the baseline run is incomplete'. When there are no blocking rows, it says 'Rerun the baseline to completion and compare again.' evalarc verify recomputes the same result. docs/ci-gate.md and the README CI-gate paragraph document the rule.

The controller ran the checks; the author did not run anything. On base commit dc848cc, tests/test_feature_f107c0a41d3a.py failed (5 failed, 1 passed), which confirms the gap. On candidate bc6089b, these all exited 0: the feature test, the full pytest suite, ruff check, ruff format --check, scripts/build_site.py and node scripts/check_site.cjs.</description>
      <pubDate>Thu, 01 Oct 2026 18:30:10 +0000</pubDate>
    </item>
    <item>
      <title>record the Git source commit in MCP open_skill receipts</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/dsh-skills-anywhere/0.17.0/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/dsh-skills-anywhere/0.17.0/</guid>
      <description>On candidate commit 59390b7, a successful MCP open_skill call that passes its expected_sha256 and expected_bundle_sha256 checks now returns structuredContent.receipt.source_commit. For a skill from a Git source, it is the 40-character lowercase HEAD SHA of that source's cache checkout at load time. For agent directories, plugin marketplaces and extra local directories it is null, and no Git command runs for them. For a Git source whose commit cannot be proven, it is also null and the server logs one 'skills-anywhere: source commit unavailable for &lt;name&gt;: &lt;reason&gt;' warning. That covers Git missing or failing, a timeout, or a work tree with no .git of its own or one outside the cache. The load still returns unchanged content in that case. Git runs without a shell and with GIT_* variables removed. Discovery stops at the cache directory, so the commit of an enclosing repository is never reported. Rejected loads throw their existing errors before any Git lookup. The advertised outputSchema declares source_commit. The schema string stays skills-anywhere-load-1. These are unchanged: loadReceipt(), LoadReceipt, OpenedSkill, the exported openSkill(), dsh provider and tool receipts, skill:// resources and rendered text. Controller-run checks on 59390b7: an acceptance-before run failed 3/3 feature tests because the receipt had only 11 fields. After the change, pnpm run build passed. tests/test_feature_ea6b51801ed9.test.ts passed. pnpm run check passed, and its log shows 24 test files and 245 tests passing. pnpm run showcase:build and pnpm run showcase:check also passed.</description>
      <pubDate>Thu, 01 Oct 2026 14:20:15 +0000</pubDate>
    </item>
    <item>
      <title>count final Bedrock chat failures by cause on the dashboard</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/ai-chat-for-amazon-bedrock/1.53.0/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/ai-chat-for-amazon-bedrock/1.53.0/</guid>
      <description>Version 1.53.0 makes failed Amazon Bedrock chat calls visible on the existing admin dashboard. The Usage today card has a fourth metric, Failed requests. The seven-day usage panel has one line, for example 'Failed chat requests, last 7 days: 4. Throttled 3, access denied 1.' When there are none it reads 'No failed chat requests recorded in the last 7 days.' The line also appears in the existing empty state. A site where every call fails no longer looks like a quiet site.

A failure is counted once per handle_chat_message() or stream_chat_message() call, only if that call sent at least one Bedrock HTTP request and still returned an error after fallback and Converse adaptation retries. AI_Chat_Bedrock_Usage::classify_failure() sorts it into one of six categories using the HTTP status and the plugin error code: throttled (429), access_denied (401/403), validation (400), unavailable (5xx), network (status 0 with aicfab_unreachable or aicfab_stream_interrupted) or other.

Some cases are not counted:
- Missing credentials, an invalid region or model, the daily limit, and encoding errors. None of these send a request.
- Retries that later succeeded.
- Stopped streams.

Only per-day category counts are stored in the existing ai_chat_bedrock_usage option. No message, prompt, output, error text, user ID or IP is kept. Requests, tokens, per-model counts and the daily cap still count successful invocations only.

Controller-run checks on commit d1da36b:
- Before the change, the new test failed with exit 255 (undefined classify_failure) on the base code.
- tests/test_feature_364f48c382c4.php passed with assertions enabled.
- bin/prerelease.sh passed: 47 suites including usage.php and admin-ux.php, phpcs clean, and versions all 1.53.0.
- tests/tooling.php passed.
- bin/build-package.py built the 1.53.0 zip.

The stream_chat_message() return-path check that failed in the prior review now passes.</description>
      <pubDate>Wed, 30 Sep 2026 18:43:50 +0000</pubDate>
    </item>
    <item>
      <title>fix: name the first bad frame in LeRobot timeline errors</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/robot-reel/0.18.1/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/robot-reel/0.18.1/</guid>
      <description>robot-reel lerobot now names the first frame that breaks an episode's timeline. It still stops with exit status 2 and writes no output. Each message gives the dataset-relative data file, the episode and the first bad frame_index. There are five cases: a missing frame (with the next recorded frame_index), a repeated frame, an unexpected value (negative, null, NaN or non-numeric), a frame with no finite timestamp, and a timestamp that is not after the previous frame's (both values shown). Frame indices are now checked before the metadata length check, so a frame dropped mid-episode is reported as a gap instead of a count mismatch. A missing final frame keeps the old 'Episode metadata lists N frames but ... holds N-1' message. Every timeline message ends with 'Robot Reel does not reorder, fill or interpolate frames; repair or re-export the episode.' The accept/reject rule is unchanged, so float or bool frame_index columns equal to 0..length-1 are still accepted. docs/lerobot.md has a new 'Timeline errors' section. Controller results for commit f8e0c1e: before the fix, the new test file ran 10 tests on the base code with 5 failures (the old generic messages) and 5 skips. After the fix, one gated run passed the new test file, the full Python unittest suite, npm run check:js, node --test (52 of 52) and robot_reel.pages --write (0 files updated). I wrote this delivery text only; I did not run any of these checks myself.</description>
      <pubDate>Wed, 30 Sep 2026 18:30:18 +0000</pubDate>
    </item>
    <item>
      <title>claim-audit worksheet for recently published numeric claims (RA-01)</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/physical-ai-radar/1.4.0/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/physical-ai-radar/1.4.0/</guid>
      <description>Adds python3 -m pairadar.claims, an offline, read-only command for the RA-01 claim review. It reads the published JSON Feed store (radar/feed.json by default). For each figure in the items[]._radar entries within the chosen window, it prints one row of a deterministic Markdown review sheet. The columns are #, day, evidence, claim, status, context, source and an empty review cell. Each status comes from distill.number_context applied to the stored text:
- quoted: the figure was found in the stored excerpt.
- title: the figure was found only in the title.
- no-excerpt: the stored excerpt is empty, as it is for baseline entries.
- unlocated: the figure is not in the stored title or truncated excerpt. Check the original source first.

The reference day is the newest valid entry date, not the system clock. The header gives the store path, reference day, days, total figures T, rows shown S and skipped entries K. A whole-window status-count line always appears. When the sheet is capped, a line gives the number of hidden unlocated and no-excerpt figures and a --limit T rerun value. Items that are not objects, lack a _radar dict or have a non-canonical date are skipped and counted. Store problems (missing, unreadable or invalid JSON, wrong shape or version, no dated entries) exit 2 with one 'claims: ' stderr line and no traceback.

Controller-executed checks on candidate 3bd0c4f:
- The feature test ran on the pre-change tree and failed with ImportError because pairadar.claims did not exist yet.
- On the candidate, python -m pairadar --rerender --no-readme exited 0.
- tests/test_feature_02985cec3631.py exited 0.
- The full unittest suite passed: 469 tests, OK.

The author did not run any of these checks.</description>
      <pubDate>Wed, 30 Sep 2026 15:47:04 +0000</pubDate>
    </item>
    <item>
      <title>fix: fail the diff gate when a check has fewer assessed attempts than the baseline</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/evalarc/0.16.1/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/evalarc/0.16.1/</guid>
      <description>evalarc diff now has a blocking change kind, less_covered. A check gets it when the current run assessed fewer attempts than the baseline and the pass rate did not fall. A smaller sample can no longer be reported as improved or unchanged. For example, 2/2 to 1/1, 1/2 to 1/1, and 2/2 to 1/1 with one unassessed attempt are all less_covered, and the command exits 1. The existing kinds still take precedence. A lower pass rate is still reported as regressed or less_reliable. A check that passed before and now has zero assessed attempts is still unassessed. The CLI now reads its list of blocking kinds from BLOCKING, so it prints lines such as '  less_covered: cancel-pending / match'. With --output, diff.json keeps schema evalarc.results-diff.v1 and lists less_covered in counts and changes. summary.md shows a 'less covered' row with the baseline and current fractions, and the index.html row uses the existing failed styling. Comparisons where no check lost assessed attempts give the same results as before. docs/ci-gate.md and docs/ci-gate.zh-CN.md list less_covered in the change table.

Controller-executed checks on image sha256:3effc5c7...: On base commit e99bc9e, the new tests/test_feature_3412504c3ce6.py failed 6 of 15 tests. The trimmed comparison exited 0 and reported 'Unchanged: 16', which confirms the gap. On candidate commit d4a4afd, all of the following exited 0: that test file, the full pytest suite (including the unchanged tests/test_results_diff.py), ruff check, ruff format --check, scripts/build_site.py and node scripts/check_site.cjs. The earlier review finding that ruff format --check would fail is resolved by that passing run. I did not run these checks myself.</description>
      <pubDate>Tue, 29 Sep 2026 15:30:08 +0000</pubDate>
    </item>
    <item>
      <title>score keyword lane classification against a labeled shortlist</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/physical-ai-radar/1.3.0/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/physical-ai-radar/1.3.0/</guid>
      <description>python3 -m pairadar.lanes has a new --labels PATH option. It compares the keyword lane classifier with a hand-labeled JSON file. For each entry it scores title + '. ' + summary using ranked_lanes, whose top lane is the one distill.classify picks. It then applies the existing thin/ambiguous rules. Those rules now live in a flag_reasons helper that inspect() also calls.

The command prints a JSON report with:
- labels: the file's basename only
- labeled and margin
- agreement for all, confident (unflagged) and flagged entries, each as n, agree and rate (rate is null when n is 0)
- disagreements in input order, each with id (null when absent), title, label, predicted, reasons, best and runner_up

A disagreement with reasons [] is a confident misfile that the flags missed.

The whole file is validated before anything is scored. Invalid input exits 2 with one 'lanes: ' stderr line and empty stdout. This covers an unreadable file, invalid JSON or non-UTF-8 text, no items list, a non-object entry, an empty or non-string title or lane, a non-string id or summary, an unknown lane (the valid ids are listed), and combining --labels with --latest or --fail-on-suspicious. In labels mode the exit code is 0 whenever a report is printed. docs/METHODOLOGY.md section 9 documents the format and its limits.

The controller ran the checks, not me. In the pre-feature run, 8 of the 10 feature tests errored because argparse rejected --labels. On candidate c109acf the controller ran python -m pairadar --rerender --no-readme (exit 0), the 10 feature tests (all passed) and the full unittest suite (446 tests, OK).</description>
      <pubDate>Tue, 29 Sep 2026 08:58:22 +0000</pubDate>
    </item>
    <item>
      <title>choose the content gaps period and export the same window</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/ai-chat-for-amazon-bedrock/1.48.0/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/ai-chat-for-amazon-bedrock/1.48.0/</guid>
      <description>The Content gaps card on the Conversations screen now has a Period control with Last 7 days, Last 30 days and Last 90 days, plus a Show button. The form does not submit when the select changes. After Show, the summary sentence, the gap table and the empty state cover the chosen window. The empty state names the window and suggests trying a longer period.

The period carries over to the Stored exchanges filter, pagination and the Reset link. Reset clears only the log search, source and rating. At 30 days, including when invalid input falls back to 30, none of those forms or URLs gain aicfab_gap_days, and Reset is the plain page URL, as in 1.47.6.

Download content gaps CSV exports the window shown on screen. Seven-day and 90-day files are named ai-chat-bedrock-content-gaps-7d-YYYYMMDD-HHMMSS.csv and ...-90d-.... A 30-day file, or one with a missing or rejected period, keeps the legacy name. The columns are unchanged.

The new AI_Chat_Bedrock_Insights::gap_window() accepts only an int, or a string of digits alone, equal to 7, 30 or 90. Anything else returns 30. The export handler reads the period only after the manage_options and nonce checks. The version is 1.48.0.

The controller ran the checks on commit f279e41. The feature test failed on the base commit, which lacks the feature, and passed on the candidate. On the candidate, bin/prerelease.sh passed: 40 suites, including admin-ux.php, insights.php and the earlier content-gap test test_feature_9ecd4de5ba50.php, which had failed in the prior attempt; phpcs was clean across 75 files. tests/tooling.php and bin/build-package.py also passed (89 files).</description>
      <pubDate>Tue, 29 Sep 2026 08:24:55 +0000</pubDate>
    </item>
    <item>
      <title>Decision coverage for recorded choices</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/evalarc/0.16.0/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/evalarc/0.16.0/</guid>
      <description>How often does a recorded choice component answer when it abstains below a confidence threshold, and how often is it wrong? EvalArc 0.16.0 chooses the threshold on calibration decisions, then reports coverage and selective error on held-out decisions. Invalid responses stay visible instead of being counted as wrong answers.</description>
      <pubDate>Mon, 28 Sep 2026 05:05:07 +0000</pubDate>
    </item>
    <item>
      <title>Check a skill choice against the installed shortlist</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/dsh-skills-anywhere/0.16.0/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/dsh-skills-anywhere/0.16.0/</guid>
      <description>Skills Anywhere 0.16.0 adds a two-step route command. Prepare builds a shortlist of installed skills that can actually be opened; apply checks a saved choice against that shortlist before it is used. The result says whether the pick is selected, absent, below the confidence threshold, stale or invalid. The command does not load or run the skill.</description>
      <pubDate>Mon, 28 Sep 2026 01:15:09 +0000</pubDate>
    </item>
    <item>
      <title>stall-threshold sensitivity for recorded step-limit failures</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/robot-reel/0.17.0/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/robot-reel/0.17.0/</guid>
      <description>Adds --stall-sweep T1,T2,... to python -m robot_reel.stress. It re-labels verified recorded step-limit traces at up to eight stall thresholds in metres and reports per-threshold counts, changed and stable trials, and threshold-dependent boundaries against the unchanged 1 mm reference. No policy is re-run and no threshold is validated.</description>
      <pubDate>Sun, 27 Sep 2026 14:55:47 +0000</pubDate>
    </item>
    <item>
      <title>report consecutive-failure streak and failing_since date for struggling sources in pairadar.health</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/physical-ai-radar/1.2.0/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/physical-ai-radar/1.2.0/</guid>
      <description>`python3 -m pairadar.health` now tells the maintainer and the nightly repair gate whether a struggling source has been continuously dark or is flapping. Each entry under `struggling` gains two fields next to the existing `source` and `failed_days`: `streak_days`, the number of consecutive runs (counted back from the latest run that recorded health inside the window) on which the source appears in `failed`, and `failing_since`, the `date` of the oldest run in that streak. A run on which the source answered, was served by its fallback endpoint, or was not attempted ends the streak. A new public helper `health.streak(runs, reference, window, source)` returns `(streak_days, failing_since)` and `(0, None)` when the source did not fail the latest run. All previously emitted keys (`reference`, `window_days`, `threshold_days`, `runs_with_health`, `failures`, `fallbacks`, and `source`/`failed_days` inside `struggling`), the worst-first ranking, the threshold and latest-run rules, and `--fail-on-struggling` exit codes are unchanged. The module docstring and docs/automation.md explain the two fields.</description>
      <pubDate>Sun, 27 Sep 2026 04:36:04 +0000</pubDate>
    </item>
    <item>
      <title>content-gap insight export as CSV for editorial workflow</title>
      <link>https://noteflowai.github.io/physical-ai-radar/updates/ai-chat-for-amazon-bedrock/1.47.0/</link>
      <guid>https://noteflowai.github.io/physical-ai-radar/updates/ai-chat-for-amazon-bedrock/1.47.0/</guid>
      <description>Export ranked content gaps as CSV for editorial planning.</description>
      <pubDate>Sun, 27 Sep 2026 04:09:46 +0000</pubDate>
    </item>
  </channel>
</rss>