Skip to content

fix: preserve source-bound annotation and empty-default handling - #29

Merged
mrkillbob merged 6638 commits into
codex/fork-upstream-sync-20260901from
codex/pr22-annotation-delivery-20260905
Sep 10, 2026
Merged

mrkillbob merged 6638 commits into
codex/fork-upstream-sync-20260901from
codex/pr22-annotation-delivery-20260905

Conversation

@mrkillbob

Copy link
Copy Markdown
Owner

PR #22 still rejects verified source containing api_key: str | None = None as a secret. This companion fix recognizes built-in Python annotations and literal None defaults only in grant-bound source, while retaining credential values, quoted strings, presentation metadata, and the existing policy for unbound text. Outbound source is unchanged; only the scan copy is transformed.

Based on #22 at c68ecb99a785cdbf311ada73c389989ecaf16717. The reported function-argument annotation alone already passes at that head; the empty annotated credential default was reproduced as secret_detected before this fix.

Validation: scripts/run_tests.sh tests/agent/test_llm_egress_firewall.py — 140 passed. Raw and numbered source presentation accept empty defaults and reject real credential defaults. git diff --check passes. No hosted-CI or merge-readiness claim: fork Actions are disabled. This draft targets #22's branch and does not modify its existing head.

@mrkillbobbot mrkillbobbot added codex PR authored by Codex area/runtime Managed PR metadata based on title and changed paths type/bug Managed PR metadata based on title and changed paths labels Sep 5, 2026
@mrkillbobbot

Copy link
Copy Markdown
Collaborator

Hermes automated repair (pr-repair-steward)

Resolved the pinned-base merge conflicts in agent/egress_source_annotations.py and tests/agent/test_llm_egress_firewall.py, preserving annotation comment visibility and credential-default scanning. Focused check: ./scripts/run_tests.sh tests/agent/test_llm_egress_firewall.py (143 passed). Repair commit: 9b4259a.

@mrkillbobbot

Copy link
Copy Markdown
Collaborator

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: 9b4259ab89

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@mrkillbobbot

Copy link
Copy Markdown
Collaborator

Hermes automated repair (runtime-correctness-steward)

Governed inspection found no actionable feedback in comment 5564281390 for PR #29. No code changes were needed; the exact PR head remains 9b4259a.

@mrkillbobbot

Copy link
Copy Markdown
Collaborator

Hermes automated repair (ci-general-fixer)

Removed the trailing blank line that caused git diff --check to fail in agent/egress_source_annotations.py.

Commit: 1f891d5a99bda8ce7ee1df8158e1c556e9dcc6e5
Tests: pytest -q tests/agent/test_llm_egress_firewall.py (143 passed); git diff --check 9d9f08c2743c4926607c284e85b1de540d562750..HEAD (passed).

Merge remains gated and no CI or safety gate was relaxed.

@mrkillbobbot

Copy link
Copy Markdown
Collaborator

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: 1f891d5a99

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@mrkillbobbot

Copy link
Copy Markdown
Collaborator

Hermes automated repair (task-orchestrator)

Inspected PR #29 at head 1f891d5. The bounded feedback is a Codex no-major-issues review for commit 9b4259a and does not report a repairable defect, so no source changes or push were made.

Verification run:

  • git status --short --branch: ## HEAD (no branch)
  • git rev-parse HEAD: 1f891d5
  • github-pr-feedback inspect-pr: OPEN at head 1f891d5

Base automatically changed from codex/hermes-local-ci-coordination-20260903 to codex/fork-upstream-sync-20260901 September 8, 2026 09:07
teknium1 and others added 18 commits September 8, 2026 13:35
Lowering the session trigger must not replace the window-relative lean
selection budget with threshold times target_ratio. Invalidate the lean
cache through the existing property while preserving explicit legacy and
external-engine fallback behavior.

Narrow adaptation of the aux-sync diagnosis and invariants in NousResearch#93576,
without adding a required recalibration method to context engines.
Related: NousResearch#95681, NousResearch#93576

Co-authored-by: Turgut Kural <58116817+TurgutKural@users.noreply.github.com>
Add Move up/down controls for actual rooms without changing bot or folder
ordering. Preserve default pin/activity ordering until an explicit move,
retain hidden room slots, and persist Desktop-local order through room
updates, mirror merges, and hydration. No membership or routing writes.

Adapted narrowly from the group ordering idea in archived
NousResearch/Hermes-Bot-Mode#105 by @onuraycicek; rename already exists.

Co-authored-by: Onur Aycicek <onur.m.aycicek@gmail.com>
Port Adolanium's focused-turn pose from Hermes-Bot-Mode#101 and
hermes-agent#88134 to the current typed Bot Mode implementation.
Match the busy signal's connection-qualified focused owner rather than
the gateway socket, retain worker activity, and ease transitions in
elapsed time on the existing shared face clock.

Includes owner-isolation and animated-pose invariants, both proven red
on origin/main, and native Electron before/after verification against
a real temporary Hermes backend with held loopback inference.

Co-authored-by: Teknium <127238744+teknium1@users.noreply.github.com>
…tention-salvage

fix(bot-mode): clear resolved Group Chat attention and bind prompts across rename
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Bumps the uv group with 1 update in the / directory: [httpx2](https://github.com/pydantic/httpx2).


Updates `httpx2` from 2.7.0 to 2.12.0
- [Release notes](https://github.com/pydantic/httpx2/releases)
- [Changelog](https://github.com/pydantic/httpx2/blob/main/src/httpx2/CHANGELOG.md)
- [Commits](pydantic/httpx2@v2.7.0...v2.12.0)

---
updated-dependencies:
- dependency-name: httpx2
  dependency-version: 2.12.0
  dependency-type: direct:production
  dependency-group: uv
...

Signed-off-by: dependabot[bot] <support@github.com>
mrkillbob and others added 23 commits September 9, 2026 19:38
…e full supported Python range

test_encode_fingerprint_has_a_stable_literal_value() hardcoded a single
expected hex digest, but ast.dump(annotate_fields=True) started omitting
default-valued fields (e.g. arguments(posonlyargs=[], ...) ->
arguments()) starting in Python 3.12, changing the normalized string
encode_fingerprint() hashes. requires-python allows 3.11-3.13, straddling
that cutover: CI runs 3.11 (needing one literal), this dev machine runs
3.13 (needing the other) -- a single hardcoded value could only ever be
correct on one side.

Verified both literals directly (uv run --python 3.11 / 3.12 / 3.13) and
select the expected value by sys.version_info, so the test passes on the
whole supported range instead of only the interpreter whoever last
edited it happened to run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…e at call time

_media_delivery_allowed_roots() only ever returned cache roots from
MEDIA_DELIVERY_SAFE_ROOTS, a module-level constant frozen at import
time from _HERMES_HOME = get_hermes_home() -- a profile switch after
this module's import left the PREVIOUS profile's cache dirs as the
only allowlisted media-delivery destination, silently failing delivery
for anything the new active profile generated.

Added _active_profile_cache_roots(), calling get_hermes_home() at
request time instead of relying on the import-time snapshot, and
included it in _media_delivery_allowed_roots()'s return list alongside
the existing static/per-profile/kanban/operator sources.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…r bootstrap step

hermes_bootstrap.py's own trailing comment says "entry points only need
import hermes_bootstrap first", and every other bootstrap function
(apply_windows_utf8_bootstrap, suppress_platform_ver_console,
activate_durable_lazy_target) is applied there -- but
harden_import_path() was left out, so a foreign utils.py in the
launch cwd could still shadow Hermes's own utils module for any
importer that didn't explicitly call it (only 3 of the 4+ entry points
did). Idempotent (repositions the same root each call), so the 3
existing explicit call sites are harmless now-redundant belt-and-braces.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…telegram topic test

The test's switch_session mock fixture and assertion used
task_owned_cwd=/conversation_worktree= -- a signature switch_session()
never has on this branch (gateway/session.py's actual signature is
conversation_kind=/persisted_cwd=). The production call site
(GatewayRunner._process_handoff) already calls it correctly; only the
test's stale kwargs needed updating.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…id, not 0

stream_events() defaulted an absent `since` query param to 0 via
_int_param(), so a fresh Desktop WebSocket connection replayed the
ENTIRE task_events ledger on connect instead of streaming only new
events. Added _EventTail.baseline() (SELECT MAX(id)) and use it as the
starting cursor when `since` is absent; an explicit `since` (including
"0", a client deliberately resuming from the start) is still honored.
Moved the cursor resolution inside the existing try/except so a
cancellation during baselining is handled the same way as cancellation
during a normal poll.

The test's own connect mock patched the wrong module attribute
(kanban_db.connect, a deprecated compat forwarding shim, instead of
kbc.connect -- the reference _EventTail actually calls, matching every
other test in this file); fixed to match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
hermes_state_holders.py's module split (sqlite_sidecar_identity(),
deleted_sqlite_sidecar_holders()) duplicated hermes_state_dbfile.py's
older _stat_sqlite_sidecar_identity()/iter_deleted_sqlite_sidecar_holders()
-- the same /proc/<pid>/fd deleted-WAL scan and os.stat-based identity
snapshot, reimplemented rather than reused, with the newer functions
never wired into SessionDB's actual open path
(refuse_deleted_wal_generation(), called before every sqlite3.connect()
specifically to stop a second opener from minting a replacement WAL
inode over a still-held deleted generation).

Verified equivalence before delegating: hermes_state_holders.
sqlite_sidecar_identity()'s inline os.stat + dev/ino-truthy check is
the same logic as hermes_state_common.stat_db_file_identity() (which
the old function called per-suffix), and
deleted_sqlite_sidecar_holders(include_self=True) (the default) matches
iter_deleted_sqlite_sidecar_holders()'s always-include-self contract
exactly. Made the two hermes_state_dbfile.py functions (kept, since
iter_deleted_sqlite_sidecar_holders is an external plugin-compat
pointer target) delegate to hermes_state_holders instead of
reimplementing the scan, so SessionDB's real open path now exercises
the newer module and there's one scan implementation instead of two
that could silently drift apart. No production callers changed their
observable behavior; verified against the full hermes_state and
zeroed_state_db suites plus real-SessionDB-opening smoke tests.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…to ready

block_task() always parked a needs_input block behind the human-only
blocked/triage lanes, even for legacy github-pr-feedback intake cards
using needs_input to mean "start validation" -- now role-owned machine
work per recompute_ready's auto_triage docstring. That recovery path
only fires once a task is already stuck in triage (after hitting the
block-loop recurrence limit), so a first-time block never reached it.

Wire the same _is_machine_recoverable_pr_feedback_triage() check into
block_task() itself: when it matches, skip normal block/triage routing
and return the task straight to ready with a machine_handoff event, so
the dispatcher can retry it immediately instead of waiting on a human.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…eason

_should_skip_fallback_candidate() and try_activate_fallback() accepted a
FailoverReason but never special-cased the two reasons that need it:

- egress_policy_blocked: the local privacy firewall already rejected the
  outbound payload, so retrying the same request against another remote
  provider cannot succeed and only produces noisy false provider
  failures. _fallback_destination_class()/classify_destination() already
  existed to answer "is this candidate local?" but nothing called them;
  now egress-blocked failover skips any non-local (non-loopback,
  non-local-process) candidate and only activates a local fallback.
- unsupported_thinking: the selected model itself lacks thinking
  capability, a property of the model config a remote fallback chain
  can't fix, so no candidate is walked at all (matches the classifier's
  should_fallback=False intent for this specific reason).

Also fills in the missing "egress_policy_blocked" -> "local egress
policy blocked the request" label in _FALLBACK_REASON_LABELS.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nt default

test_worktree_add_without_base_uses_remote_default failed only in CI, not
locally. The fixture's bare `origin.git` was created with plain `git init
--bare`, so its HEAD symref followed the runner's ambient
init.defaultBranch -- unset on some CI images, where git's legacy
built-in default is "master". Since the fixture only ever pushes
main/feature, a "master"-pointing HEAD symref is dangling, so `git
remote show origin`'s "HEAD branch:" line resolves to "(unknown)" and
resolve_worktree_base() falls through to using local HEAD (the
just-checked-out parked-feature branch) instead of origin's default
branch -- exactly the mismatch the test caught.

Pin the bare repo's initial branch to "main" explicitly (matching the
clone's own `-b main`) so the fixture no longer depends on the runner's
git config.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ckup writes

cron/jobs.py: _epoch_file_age() clamped any negative age (stamp in the
future) to 0.0, the same value a genuinely fresh heartbeat produces. A
restored/replayed ticker_heartbeat file or gross clock skew therefore
read as "just ticked" instead of "cannot determine liveness" -- the
exact false-positive a liveness check must not produce. Now an age more
than 1s in the future (small skew still tolerated) returns None instead
of a clamped 0.0.

hermes_cli/backup.py: _write_full_zip_backup_locked() (the unattended
automatic-backup path) called `list(_iter_backup_files(...))` before
opening the archive, materializing the entire home directory tree in
memory before writing a single zip entry. Stream the walk generator
straight into _write_zip_entries() instead (peeking one entry up front
to preserve the empty/no-files-to-back-up short circuit), so each
directory's files are archived before the walk descends into the next.
The interactive `hermes backup` CLI path (_run_backup_locked) keeps its
eager list — it prints an upfront file count for progress UX, which the
unattended path has no use for.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ersation

_handle_voice_channel_input() routed every transcript from an allowlisted
fast-lane speaker into the tool-free private lane, with no check on what
the transcript actually said -- an explicit work request spoken into a
fast-lane voice channel ("fix the voice delay", "run the tests and
commit the patch") would silently lose task capability instead of being
acted on, indistinguishable from idle chat ("can you hear me?").

Add _voice_fast_lane_requests_work() (a verb-based heuristic matching
inspect/fix/run/commit/write/edit/... operation verbs) and gate the
fast-lane branch on it: casual conversation still gets a private,
tool-free turn, but an explicit operation keeps full task capability
even from a fast-lane speaker.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…er call

agent/kanban_stop.py defined successful_kanban_terminal_transition() --
matching the current tool batch against a durable {"ok": true}
kanban_complete/kanban_block result -- but nothing in the conversation
loop ever called it. A kanban worker that finished its task therefore
kept looping: run_tool_round() had no way to recognize the terminal
transition, so it fell through to "continue" and burned another API
call after the task was already durably complete.

Wire the check into run_tool_round() right after tool execution/
persistence succeeds (matching the function's own contract: call only
after the executor has persisted every tool-result row). On a match,
end the turn with turn_exit_reason="kanban_terminal_transition" instead
of continuing to the next iteration. final_response is normalized to ""
(not left None) so the finalizer's `completed` computation reads this as
a clean completion rather than a turn stuck mid-flight on a pending tool
result.

tests/run_agent/test_tool_call_incremental_persistence.py: the target
test's fabricated HERMES_KANBAN_TASK/RUN_ID have no real board row, so
_touch_activity's auto-heartbeat/comment-injection bridges
(heartbeat_current_worker_from_env / inject_new_comments_from_env) would
reach for a real kanban board -- either false-positive "lease lost" and
hard-interrupt the turn, or block waiting on a real DB connection.
Stub both bridges to no-ops; they are unrelated to what this test
exercises (kanban_complete tool-call persistence ordering).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ride

ee39240 bumped website/package.json's js-yaml override from 4.3.1 to
4.3.2 (GHSA-2883-xcg3-v3hh) but only refreshed the root/apps/desktop
lockfiles -- website/package-lock.json still pinned js-yaml@4.3.1,
so `npm ci` failed closed with EUSAGE ("lock file's js-yaml@4.3.1 does
not satisfy js-yaml@4.3.2") in the Docs Site CI job. Refreshed via
`npm install --package-lock-only` inside website/.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nts shutdown

test_stream_events_closes_connection_when_cancelled failed intermittently
in CI (never locally) with an unhandled asyncio.CancelledError surfacing
from `await task`, traced to plugin_api.py's `finally: await
tail.shutdown()`. A `finally` block's own exceptions are never caught by
its enclosing try's `except` clauses -- so when cancelling a task that's
mid-await on a `run_in_executor()` future (baseline/poll) races with
that future's underlying thread completing, asyncio can deliver a
second CancelledError at the very next await checkpoint, landing
unguarded inside the `finally`. The existing `except
asyncio.CancelledError: return` sibling already treats cancellation as
normal shutdown; extend that same treatment to the cleanup call itself.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The prior fix (0478d5e) stopped the crash but left the connection
leaking: CI showed close_calls==0 after a cancelled stream. Once
task.cancel() has landed on stream_events()'s task (the common path --
shutdown() runs from its `finally`), an internal `await
run_in_executor(...)` inside shutdown() can be skipped by asyncio
before _close() ever runs, even though the executor thread itself
can't actually be interrupted.

Replace the await with a blocking `executor.submit(...).result(timeout)`
call: a coroutine with no internal await point can't be interrupted
by task cancellation mid-body, so the connection is guaranteed to
close exactly once regardless of any pending cancellation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… suite

apps/desktop :: check:test:ui was crashing outright in CI four
consecutive runs (445-650s, never producing a single test-file result
or assertion failure -- just silence, then a bare FAIL from the task
runner). Vitest's default pool sizing (one thread per CPU) has no
memory cap, and each thread pays its own jsdom environment for this
suite; on a memory-constrained CI runner that plausibly exceeds the
available budget and the whole process is killed before anything can
report.

Capped poolOptions.threads.maxThreads to 4 for the ui project, trading
some wall-clock time for staying inside the runner's memory budget.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…olOptions

The prior fix used poolOptions.threads.{max,min}Threads, deprecated and
inert in Vitest 4.1.10 (installed here) -- CI's deprecation warning
confirmed the value was silently ignored, so the crash persisted. Use
the current top-level maxWorkers/minWorkers, which replaced
poolOptions across all pool types in the 4.x migration.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The test docblock was updated to say "The bot row's only activation side
effect is opening" and the warm tests were flipped to assert warmProfile /
warmAgent are NOT called on pointerEnter, but the implementation was never
updated to match.

Root cause: the warm() function and onPointerEnter={warm} were left in
bot-row.tsx after the tests were updated to document the new no-warm
contract. A roster can contain hundreds of rows and can reflow under a
stationary pointer, so pointer-entry warming behaves like roster-wide
warming in practice and must not start profile backends.

Fix: delete the warm() function and the onPointerEnter prop entirely.

Verified: bot-row.test.tsx 6/6 pass locally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Thread 1 (plugins_discovery): exclude github-pr-feedback from bundled
auto-discovery per plugins/AGENTS.md vendor SaaS connector policy.

Thread 2 (egress split): extract terminal classifier functions and
constants from llm_egress_runtime.py into new llm_egress_terminal.py
before adding github-pr-feedback behavior. Reduces the god-file by
~2,150 lines; runtime re-imports all moved symbols.

Thread 3 (session.py _supports_conversation_worktree): broaden the
platform allowlist to accept any non-None SessionSource, not just the
two originally hard-coded values.

Thread 4 (session.py _conversation_worktree_manager): route to the
profile-specific database via _db_for_key(session_key) instead of
always using the ambient self._db.

Thread 5 (session-sweep.ts): extend the Bot Chat hide sweep to cover
profile-wide sessions (bot-to-bot/CLI flows) on load/reconnect via
reconcileAllBotProfileSessions(), in addition to the existing
group-chat-known-id sweep.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Thread PRRT_kwDOT_eOos6gzdSH (dispatcher-readiness.ts):
- Treat HTTP 404 from disabled Kanban plugin as non-blocking (returns
  status: 'disabled' instead of throwing DispatcherReadinessError).

Thread PRRT_kwDOT_eOos6gzdSU (dispatcher-readiness.test.ts):
- Replace text-scan test (reads main.ts as raw string) with a
  behavioral test for runDispatcherReadinessGate() that verifies
  advancePhase fires before the readiness check, and a 404 test case.

Thread PRRT_kwDOT_eOos6g03Mi (secure_worker.py):
- Include symlinks in rglob inventory: path.is_file() or path.is_symlink().

Thread PRRT_kwDOT_eOos6g03Mb (secure_worker.py):
- Deny 'hooks' and 'hooks_auto_accept' keys in secure-worker audit profiles.

Thread PRRT_kwDOT_eOos6g03M0 (supply-chain-audit.yml):
- Detect changed lock-file directories and run npm audit per directory.

Thread PRRT_kwDOT_eOos6g2OkE (federation.py):
- Write toolsets to platform_toolsets["cli"] in addition to toolsets.

Thread PRRT_kwDOT_eOos6g03Ms (federation.py):
- Snapshot config/identity files before refresh; roll back on exception.

Thread PRRT_kwDOT_eOos6g2OkK (federation.py):
- Use canonical manifest _role_to_groups map to replace stale memberships.

Thread PRRT_kwDOT_eOos6g2OkG (specialist_routing.py):
- Wire CapabilityRegistry.is_profile_declared() into route decisions.

Thread PRRT_kwDOT_eOos6g4YKr (worktree_environment.py + test):
- Add _platform kwarg to _venv_python_path; use it in tests instead of
  monkeypatching sys.platform, making both branches testable on any host.

Thread PRRT_kwDOT_eOos6g03MJ (world-sync.ts):
- publishEvents now merges incoming events into the current projection
  instead of clobbering it with emptyProjection(). Add getProjection()
  to WorldSyncSink; store it in storeWorldSyncSink().

Thread PRRT_kwDOT_eOos6g03MS (index.tsx + world-sync.ts):
- Guard refreshWorldProjection with optional isCancelled predicate;
  index.tsx effect sets cancelled=true on cleanup to discard stale fetches.

Thread PRRT_kwDOT_eOos6g03MX (world-actions.ts):
- Route inspect/inspect_blocker/show_source through context.inspect door
  when provided; report as unavailable otherwise instead of silently succeeding.

Thread PRRT_kwDOT_eOos6g03Mn (discord/adapter.py):
- Check remaining configured users before disconnecting from voice channel.

Thread PRRT_kwDOT_eOos6g4YKu (model_performance_router + llm_egress_runtime):
- Add install_performance_route_table() to llm_egress_runtime; _route_for_agent
  consults the compiled table when agent.performance_surface is set.
- GatewayRunner._init_runtime_settings loads artifact from
  agent.performance_route_artifact config key at startup.
- End-to-end tests verify _route_for_agent uses the installed table.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Body was at class-method def level (8 spaces) rather than inside the
def (12 spaces), causing ruff to raise invalid-syntax and blocking CI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…gistry

The method was indented at 8 spaces (inside is_profile_declared()) rather
than 4 spaces (class level), so it was unreachable dead code. The previous
commit re-indented the body but left the def at the wrong level, causing
AttributeError in all resolve() call-sites. Dedent the entire method by 4
spaces so it is a proper CapabilityRegistry instance method.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix: P1 review findings from the PR-feedback stack merges
@mrkillbob
mrkillbob removed this pull request from stack #63 September 10, 2026 10:05
@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown

૮ >ﻌ< ა ci review

ran on d80f69a — Merge branch 'codex/fork-upstream-sync-20260901' into codex/

❌ Job failures

Check contributors / check-attribution · View job

Job Check contributors / check-attribution failed.


OSV scan / Scan lockfiles / osv-scan · View job

Job OSV scan / Scan lockfiles / osv-scan failed.


Python lints / Windows footguns (blocking) · View job

Job Python lints / Windows footguns (blocking) failed.


⚠️ Action required

Unmapped contributor email(s) · View job

New contributor email(s) are not in AUTHOR_MAP.

mrkillbob@users.noreply.github.com (Mike DeMott)

How to fix:

Run from the PR branch:

python3 scripts/audit_pr_attribution.py --fix
git add contributors && git commit -m "chore: map contributor emails" && git push

Or map one email manually (do NOT edit AUTHOR_MAP in release.py):

python3 scripts/add_contributor.py <email> <github-username>

To find the GitHub username for an email:

gh api 'search/users?q=EMAIL+in:email' --jq '.items[0].login'

⚠️ Warnings

CI timings · View report · View job

Wall time 244m59s vs 11m19s (+2064.8%). 4 job(s) slower, 6 faster,

  • OS-specific tests / Windows-only tests: +14421.0s
  • Check contributors / check-attribution: -53.0s
  • OS-specific tests / macOS-only tests: +20.0s
  • Check no committed infographics / check-no-committed-infographics: -19.0s
  • Python lints / Windows footguns (blocking): -15.0s

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mrkillbob
mrkillbob merged commit 79a7d01 into codex/fork-upstream-sync-20260901 Sep 10, 2026
2 checks passed
@mrkillbob
mrkillbob deleted the codex/pr22-annotation-delivery-20260905 branch September 10, 2026 14:11
@chatgpt-codex-connector

Copy link
Copy Markdown

💡 Codex Review

edits.append((start, end))

P1 Badge Restore empty-default masking for prefixed token names

When verified Python source contains access_token: str | None = None, refresh_token: str | None = None, or the equivalent function parameters, removing the separate identifier/default edits leaves access_token = None in the scan copy. The downstream source-assignment exemptions recognize standalone token but not these prefixed aliases, while _EGRESS_SECRET_ASSIGNMENT does recognize them, so both source-bound and presented source are incorrectly blocked with secret_detected despite containing no credential. Preserve the empty-default masking for every recognized credential alias or extend the downstream exemption accordingly.

AGENTS.md reference: AGENTS.md:L66-L67


const mainSource = fs.readFileSync(path.join(here, 'main.ts'), 'utf8').replace(/\r\n/g, '\n')

P1 Badge Exercise pool timeout wiring instead of parsing source

In the desktop Vitest suite, this reads main.ts and later uses regular expressions to assert that particular constants and call-site text exist. That can stay green when the matching text is dead or the live spawn path passes a different value, while harmless formatting or extraction breaks the test, so it does not validate the timeout behavior it claims to protect. Extract the timeout relationship and spawn options behind an importable function or constant and execute that contract directly.

AGENTS.md reference: AGENTS.md:L407-L413


@pytest.mark.skipif(sys.platform == "win32", reason="POSIX flock harness")

P1 Badge Mark cross-profile process tests for their host lanes

These real process/flock tests exercise host-dependent signal and locking behavior, but a bare skipif leaves the file undiscoverable by scripts/ci/list_os_marked_tests.py, which selects the dedicated macOS and Windows jobs by marker name. Consequently the cross-profile kill refusal is covered by the ordinary Linux run but not exercised on macOS, where the same POSIX path is shipped; split or mark the host-native cases with the repository's OS markers so each relevant lane selects them.

AGENTS.md reference: AGENTS.md:L379-L384


assert.ok(Date.now() - started < 80, 'foreground must not wait behind the background queue')

P1 Badge Replace the sub-80ms wall-clock assertion with synchronization

This assertion can fail whenever a loaded CI worker pauses the event loop for 80 ms after the foreground request has already been granted correctly; it measures scheduler availability rather than queue ordering. The surrounding activeCount and promise-state assertions can establish immediate foreground admission without a tight clock, or any unavoidable wall-clock bound must use the repository's minimum two-second allowance.

AGENTS.md reference: AGENTS.md:L345-L348


name: web-perplexity
version: 1.0.0
description: "Perplexity Search API web search and query-relevant page snippets. Requires PERPLEXITY_API_KEY — get one at https://www.perplexity.ai/account/api."

P1 Badge Ship the Perplexity backend as a standalone plugin

This adds a connector for the third-party Perplexity Search SaaS directly under the bundled plugins/ tree, making Hermes responsible for its API, credentials, configuration, and future compatibility. Repository policy explicitly excludes new vendor SaaS connectors from this tree, including when they are implemented as plugins; this backend must instead be published as a standalone plugin discovered from the user plugin directory or a pip entry point.

AGENTS.md reference: plugins/AGENTS.md:L23-L28


name: meta-ai-image-gen
version: 1.0.0
description: "Meta Model API image generation backend (muse-image). OpenAI-compatible /v1/images/generations. Saves images to $HERMES_HOME/cache/images/."

P1 Badge Ship the Meta image backend as a standalone plugin

The new Meta Model API image backend is another vendor SaaS integration bundled into the core repository, so maintaining changes to Meta's endpoint and authentication becomes part of Hermes releases. The scoped plugin policy requires new third-party-product integrations to live in standalone plugin repositories rather than under plugins/; move this manifest and implementation to that distribution path.

AGENTS.md reference: plugins/AGENTS.md:L23-L28

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/runtime Managed PR metadata based on title and changed paths codex PR authored by Codex type/bug Managed PR metadata based on title and changed paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.