Skip to content

fix(redact): exempt git identity vars from ENV-assignment redaction - #3

Merged
Kyzcreig merged 1 commit into
mainfrom
fix/redact-git-identity-allowlist
Jun 3, 2026
Merged

Kyzcreig merged 1 commit into
mainfrom
fix/redact-git-identity-allowlist

Conversation

@Kyzcreig

@Kyzcreig Kyzcreig commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Problem

agent/redact.py's ENV-assignment redaction matches any VAR=value whose name contains a secret-like substring. GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL contain AUTH (inside AUTHOR), so they were falsely redacted to VAR=*** — mangling git commit-authoring commands in tool output.

This surfaced while re-authoring commits to satisfy the contributor-check attribution CI: every export GIT_AUTHOR_EMAIL=... got rewritten to ***, corrupting the author identity.

Fix

Add a leading word-boundary + negative-lookahead allowlist (_GIT_IDENTITY_ALLOWLIST) that exempts the four git identity vars (GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL) from ENV-assignment redaction. They are never secrets.

Verification (empirical, not just reasoning)

  • Git identity vars pass through untouched — including with an export prefix.
  • Every genuine secret STILL redacts: OPENAI_API_KEY, AWS_SECRET_ACCESS_KEY, *_AUTH_TOKEN, *_AUTHORIZATION_KEY.
  • 3 new allowlist tests + 2 still-redact guards.
  • Full redact suite: 79 passed, 0 regressions (plus config-bridge + PII redaction suites green).

Risk

Minimal. Narrow allowlist of 4 well-known non-secret variable names; the secret-name matching is otherwise unchanged.

…redaction

The ENV-assignment redaction pattern matches any VAR=value whose name
contains a secret-like substring. GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL
contain "AUTH" (inside "AUTHOR"), so they were being falsely redacted to
VAR=***  mangling git commit-authoring commands in tool output (observed
when re-authoring commits for the contributor-attribution CI check).

Add a leading word-boundary + negative-lookahead allowlist
(_GIT_IDENTITY_ALLOWLIST) that exempts the four git identity vars while
still redacting every genuine secret. Verified: real keys (OPENAI_API_KEY,
AWS_SECRET_ACCESS_KEY, *_AUTH_TOKEN, *_AUTHORIZATION_KEY) still redact;
git identity vars (incl. with 'export ' prefix) pass through untouched.

Tests: 3 new allowlist tests + 2 still-redact guards; full redact suite
79 passed, 0 regressions.
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

🔎 Lint report: fix/redact-git-identity-allowlist vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 9592 on HEAD, 9592 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 5059 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@Kyzcreig
Kyzcreig merged commit 5e6854b into main Jun 3, 2026
19 of 20 checks passed
@Kyzcreig
Kyzcreig deleted the fix/redact-git-identity-allowlist branch June 3, 2026 01:52
Kyzcreig added a commit that referenced this pull request Jun 5, 2026
…redaction (#3)

The ENV-assignment redaction pattern matches any VAR=value whose name
contains a secret-like substring. GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL
contain "AUTH" (inside "AUTHOR"), so they were being falsely redacted to
VAR=***  mangling git commit-authoring commands in tool output (observed
when re-authoring commits for the contributor-attribution CI check).

Add a leading word-boundary + negative-lookahead allowlist
(_GIT_IDENTITY_ALLOWLIST) that exempts the four git identity vars while
still redacting every genuine secret. Verified: real keys (OPENAI_API_KEY,
AWS_SECRET_ACCESS_KEY, *_AUTH_TOKEN, *_AUTHORIZATION_KEY) still redact;
git identity vars (incl. with 'export ' prefix) pass through untouched.

Tests: 3 new allowlist tests + 2 still-redact guards; full redact suite
79 passed, 0 regressions.

Co-authored-by: Kyzcreig <9063726+Kyzcreig@users.noreply.github.com>
Kyzcreig pushed a commit that referenced this pull request Jun 27, 2026
…110 #3)

ingest (quarantine) SHALLOW-COPIES rows, so messages[src_idx] is row is ALWAYS False
on the normal single-pass path -> the identity guard blocked every stamp. The suffix
index is structurally correct (1:1 order-preserving ingest + front-only fold); guard
on a cheap role-match instead, and rely on the consumer (in-range validation +
reconcile -> A-floor) as the real gate. Test now passes COPIED working rows to prove
the stamp fires through ingest copies.
Kyzcreig added a commit that referenced this pull request Jun 27, 2026
…rtition (#110)

* feat(compaction): Option B — provenance-stamped exact in-turn kept partition

Makes the in-turn granular announce's folded/kept split EXACT (was A-floor approx,
bounded <=7%). Engine stamps _src_idx (origin index into messages) on tail rows
before _assemble_context, single-pass only (1:1 mapping holds); the pipeline's
shallow-copies carry it through drop/strip/rewrite by construction, stubs lack it.
Consumer harvest_provenance_partition() reads exact kept_pre off the returned
compressed (NO engine instance state -> no cross-session race, the singleton finding);
strip_provenance() removes the key before compressed flows onward (wire/cache guard).
Precedence: B (exact) -> replay -> A-floor (approx). Malformed/duplicate/multi-pass/
length-mismatch -> falls to A-floor, never a wrong B split.

13 new tests (provenance partition + engine stamp survival/single-pass-only/strip);
451 compaction + 197 context_engine green. Speculative helpers from #109 removed.

* fix(compaction): B provenance — suffix-from-end stamp (Greptile #110 ×2)

#1 Provenance never engaged: after a real front-fold, working_messages is shorter
than messages even single-pass, so the len-equality guard skipped every production
stamp. Fix: the fresh tail is a SUFFIX of the original messages (fold removes from
the FRONT only), so index from the END: src_idx = len(messages)-(len(tail)-off),
guarded by a per-row identity check (messages[src_idx] is row). New test proves the
stamp engages + maps exactly after a 60-row front-fold.

#2 Strip-failure could leave provenance in the transcript: made the strip inline
(no import that could fail and silently leave _src_idx) — del with pop() fallback.

13 B tests green.

* fix(compaction): B stamp guard uses role-match not identity (Greptile #110 #3)

ingest (quarantine) SHALLOW-COPIES rows, so messages[src_idx] is row is ALWAYS False
on the normal single-pass path -> the identity guard blocked every stamp. The suffix
index is structurally correct (1:1 order-preserving ingest + front-only fold); guard
on a cheap role-match instead, and rely on the consumer (in-range validation +
reconcile -> A-floor) as the real gate. Test now passes COPIED working rows to prove
the stamp fires through ingest copies.

---------

Co-authored-by: Apollo <apollo@ang.ventures>
Kyzcreig pushed a commit that referenced this pull request Sep 19, 2026
Adopts the DNS-rebinding-pinned transport hardening (repo issues #2/#3,
PR #3) and the starter-feed/settings failure-surfacing + SSRF-gated icon
proxy fix (issue #6, PR #8). Full range in
tony-simons-aiowa/hermes-newswire deccdc4..e6b438e (13 commits):

Security-relevant highlights:
- All outbound fetches (feeds, redirects, icons) now go through a pinned
  transport: the SSRF gate's validated address set is bound to the actual
  connection — no second DNS lookup, so DNS rebinding/TOCTOU has no
  window; the plugin fails closed if the pin seam changes.
- New GET /icon.json proxies favicons through the same gate and returns
  base64 data URLs — the renderer's <img> no longer performs unpinned
  DNS resolutions of feed-controlled hostnames. 64 KB cap enforced
  mid-transfer; image content-type allowlist; bounded, normalized TTL
  cache.
- Renderer surfaces backend failures (settings/sources banners,
  starter-feed inline errors) instead of silent no-ops.

Capabilities unchanged (all empty — dashboard plugin, no tools/hooks/
env). Verification at the new pin: 129 pytest, 33 renderer interaction
checks, 26 ESM render smoke, hermes plugins validate clean.
@Kyzcreig
Kyzcreig restored the fix/redact-git-identity-allowlist branch September 21, 2026 10:32
Kyzcreig added a commit that referenced this pull request Sep 21, 2026
FleetReview round 3 on PR #821 raised three P1s against 8cead1e.

P1 #1 "watchdog starves teardown" — REFUTED BY MEASUREMENT, with a
regression test so it cannot become true silently. The premise is that
the inner leash is "drain plus a small grace", so a 35s cap would arm a
~40s watchdog and leave persistence ~5s. DEFAULT_SHUTDOWN_WATCHDOG_GRACE_S
is 60.0, not small, so under launchd the inner term never wins the min()
and the armed deadline is clamp - LAUNCHD_HARD_EXIT_RESERVE_S. Measured
at the production clamp (60): drain 35 -> armed 50 (window 15, need 15);
drain 28 with a 22s record -> armed 50 (window 22, need 22). The P1's own
proposed assertion passes by construction, both sides being clamp - 10.

The arming arithmetic is extracted to resolve_armed_shutdown_watchdog_delay
so the invariant is measured against the expression gateway.run really
arms with, at both arming sites (live arm + diagnostic snapshot).
test_stop_arms_the_watchdog_at_the_hard_exit_deadline drives the real
stop() and reads the wall-clock delay handed to arm_shutdown_watchdog —
not a call count, not re-derived arithmetic. The grace branch does bind on
a clamp far above the drain (clamp > drain + 70); clamp=300 is pinned too,
so shrinking the grace to the "small grace" the P1 assumed turns the
production row red instead of flipping it quietly.

P1 #3 "unbounded teardown reserve" — FIXED at the read/record boundary,
not with a drain floor (Argus's floor withdrawal stands). Two bounds:

  * record_teardown_timing(budgeted=...) marks a stop that actually ran
    under a supervisor deadline. An unconstrained stop (hermes gateway
    stop, Ctrl+C, foreground) can legitimately take far longer than any
    launchd budget; reading that back as the reserve zeroes the next
    drain. Unbudgeted samples are still written for diagnostics but never
    read back. Legacy records with no provenance field are not trusted.
  * read_last_teardown_seconds(max_seconds=...) rejects a sample larger
    than resolve_max_actionable_teardown_reserve_s(clamp) — the window
    that exists before the hard exit. At clamp 60 a 55s record drove the
    drain to 0.0 and stayed poisoned if that stop hard-exited before
    recording a new sample; it now degrades to "no measurement" (drain
    35.0) and is replaced by the next real measurement.

P1 #2 "cron drain eats teardown" is DUPLICATE-OF t_f753b2b5 / PR #835 and
is not fixed here; the stale `# 45` comment in the cron-leash test is
corrected to `# 35` and cites the card.

Also replaces test_stop_path_arms_via_the_shared_resolver, which read
GatewayRunner.stop's source with inspect.getsource — banned outright by
AGENTS.md ("Never read source code in tests") — with the behavioral
stop()-driving test above.

Verified:
- Mutation-checked, each applied to source and reverted:
  arm from resolve_shutdown_watchdog_delay(drain, grace_s=5.0) -> armed
  33.0 vs 50.0, 1 failed; drop the budgeted guard -> 2 failed; drop the
  max_seconds ceiling -> 2 failed.
- Invariant sweep over clamp {None,1,4,8,10,20,30,45,50,60,90,120,300,600}
  x configured {5,20,30,50,180,600} x last_teardown {None,0,5,22,45,58,
  1e9,inf} = 672 combos through the production boot read: 0 violations of
  window >= max(15, measured) wherever the drain is non-zero; the 240
  residuals are all the specified nonnegative saturation (drain == 0);
  armed < clamp everywhere; non-launchd passthrough intact.
- Focused: 46 + 18 pass. Broad shutdown/restart surface, 19 files:
  383 passed, 0 failed, 4 skipped.
- ruff clean on all changed files; git diff --check clean.

Not verified and not claimed: the live safe-restart at host load >= 20
with 5 active turns ending in a clean exit status, the gateway-exit-diag
teardown line on a real shutdown, merge-queue landing, deployment, the
external fleet-config-lint headroom assertion, and the upstream PR.
Kyzcreig added a commit that referenced this pull request Sep 24, 2026
… path binds session, every status writer guarded

- no caller session identity (plain shell, cron opener) => guard skipped,
  one stderr note; behaviour identical to base (Argus #1)
- _caller_session_id: explicit slash session, then ContextVar-first
  resolve_current_session_id, env only outside the gateway; gateway
  /kanban passes the invoking session_id into run_slash (Argus #2)
- guard schedule, reopen, reopen-review, request-review, request-changes,
  link (child), specify, decompose; _home_session_guarded binds the card
  param by name via inspect.signature; tool handlers for request_review,
  request_changes, link bound + foreign_ok (Argus #3)
- AST contract tests: every tasks.status/assignee/priority/session_id
  writer is guarded or listed in EXECUTION_LANE with a reason; every CLI
  verb and tool handler reaching a guarded writer binds the actor

Verified: test_kanban_home_session.py 56 passed; 7/8 mutations killed
(8th equivalent: CLI + db sessionless checks are redundant layers);
kanban suite 146 files: 2074 passed, 3 skipped, 1 failed
(workspace_retention[dir_card_states2], passes isolated on head and base).
Kyzcreig added a commit that referenced this pull request Sep 24, 2026
…ndex for the whole rebuild (t_d3963974 follow-up to #966)

Third defect of freeze #3, not covered by #966: repair_external_content_fts ran
DROP/CREATE VIRTUAL TABLE/'rebuild'/DROP+CREATE TRIGGER as separately autocommitted
statements (legacy isolation opens no implicit txn for DDL). For the 13-28 min rebuild
other connections saw messages_fts missing ('no such table', live 05:10) or empty
(docsize=0 vs 2.79M on disk). Now: BEGIN IMMEDIATE .. one COMMIT, rollback on error;
no-op loads still take no write lock.

test_lcm_fts_atomic_rebuild.py: RED on dc228d5 (engine-load structural path torn;
failed rebuild left index dropped). The explicit-repair path passed only because
#966's parity-marker write opens a txn first. tests/context_engine 397 passed.
Kyzcreig added a commit that referenced this pull request Sep 24, 2026
… path binds session, every status writer guarded

- no caller session identity (plain shell, cron opener) => guard skipped,
  one stderr note; behaviour identical to base (Argus #1)
- _caller_session_id: explicit slash session, then ContextVar-first
  resolve_current_session_id, env only outside the gateway; gateway
  /kanban passes the invoking session_id into run_slash (Argus #2)
- guard schedule, reopen, reopen-review, request-review, request-changes,
  link (child), specify, decompose; _home_session_guarded binds the card
  param by name via inspect.signature; tool handlers for request_review,
  request_changes, link bound + foreign_ok (Argus #3)
- AST contract tests: every tasks.status/assignee/priority/session_id
  writer is guarded or listed in EXECUTION_LANE with a reason; every CLI
  verb and tool handler reaching a guarded writer binds the actor

Verified: test_kanban_home_session.py 56 passed; 7/8 mutations killed
(8th equivalent: CLI + db sessionless checks are redundant layers);
kanban suite 146 files: 2074 passed, 3 skipped, 1 failed
(workspace_retention[dir_card_states2], passes isolated on head and base).
github-merge-queue Bot pushed a commit that referenced this pull request Sep 24, 2026
…sed replays reported lost (t_43e058b7) (#961)

* fix: retain session prompt across route metadata writes (#958)

Verified targeted session-state, restore, accounting, and model-resume tests: 236 passed, 1 pre-existing dashboard-auth fixture warning deselected. Reproduced NULL from billing route before fix.

Co-authored-by: Kyzcreig <9063726+Kyzcreig@users.noreply.github.com>

* fix(gateway): restart follow-ups keep adapter-granted admission; refused replays reported lost (t_43e058b7)

Argus r8 N1 (t_e253d9d5): SessionSource.to_dict drops is_bot / role_authorized /
delivered_via_upstream_relay / profile_route_rejected, so a spooled follow-up
admitted only by ALLOW_BOTS, ALLOWED_ROLES or the relay was refused as
"Unauthorized user" on boot replay, its spool file acked, and
restart_followup_lost logged 0 lines.

Trust model: to_dict stays wire-safe (unchanged). The spool record carries the
flags in a separate `admission` block and the whole record is HMAC-SHA256'd
with a per-home 0600 key (<home>/gateway/restart_followups.key). On load the
flags are restored only if the MAC verifies; otherwise no trust flag is
restored (only fail-closed profile_route_rejected is honoured) and
PHASE=restart_followup_untrusted is logged. Live policy is still re-evaluated
by the normal intake. A replay the intake refuses (unauthorized /
profile_route_rejected) now logs PHASE=restart_followup_lost with reason.

MF (same review): AST contract that the post-turn draining site spools
pending_event itself, not None.

Verified: new real stop->boot e2e (human/bot/role/relay, forged, tampered,
gate-closed-during-restart, to_dict class guard) 8/8; on base 3 admission arms
fail, human control passes. Focused restart suites 49/49. Mutants: MAC
unchecked, refusal unreported, admission unrestored, MF pending_event=None all
KILLED. Argus probe_r8_source_authz_real_intake: B/R PRESERVED, CONTROL ok.
Session/authz/startup-restore suites 445 passed.

* fix(kanban): judge goal deliverables before completion; fail open for operator errors (#960)

* fix(kanban): grade goal deliverables before completion and isolate judge errors

Verified 81 targeted tests pass (one ACP-dependent test excluded). Mutating the completion rubric makes the first-completion regression fail as expected.

* refactor(kanban): one shared goal-mode handoff gate for CLI and tool surfaces

Argus r1 (t_c4e23682): _goal_mode_handoff_rejection was byte-identical in
tools/kanban_tools.py and hermes_cli/kanban.py; only the tool copy was
test-gated, so 4 CLI mutants survived (Issue NousResearch#38367 two-copies class).

- goals.kanban_handoff_rejection is now the single predicate (judge with
  completion_handoff=True; owned worker retries then blocks transient on
  judge error; operator fails open with a judge_error event; caller's conn).
- Both surfaces' complete + request-review delegate to it, injecting only
  their run-id resolver and judge-availability probe.
- CLI tests drive the real `kanban complete` / `request-review` argv path
  (build_parser -> kanban_command): completion_handoff reaches the judge and
  the card closes; real judge prompt accepts first completion; owned-worker
  500 -> 2 calls, blocked transient, error on stderr, rc!=0; review gated.
- AST contract: exactly one function in the tree calls the judge with
  completion_handoff, and both surface wrappers delegate to it.

Verified: 86 passed, 1 deselected (inherited ModuleNotFoundError: acp, also
red on ce0c9d3). Mutation matrix: baseline green precondition, 16/16 KILLED
by named failing tests (M01-M13 re-targeted at the shared helper + W1-W9
wiring/duplicate-predicate mutants).

---------

Co-authored-by: Kyzcreig <9063726+Kyzcreig@users.noreply.github.com>

* fix(kanban): resume dependency-wait PR and page stranded ready cards (#952)

* fix(kanban): respawn guard honors worker dependency_wait->promoted resume; add requeue_task + stuck-guard probe

t_7d7ff489. Rule 4 active_pr no longer strands a card whose own dependency block (kind=dependency) postdates the newest PR comment and whose promotion has not yet spawned. requeue_task emits operator-intent 'requeued' for READY cards. respawn_guard_stuck_tasks lists cards held by active_pr >= 30 min.

* fix(kanban): surface guarded ready cards and provide requeue verb

Verify dependency_wait promotion dispatches once and subsequent crash is guarded; CLI requeue and one-shot alert tests pass (168 passed, 1 skipped in focused suites). t_7d7ff489.

* test(kanban): keep corruption probe independent of watcher call count

Verified: corrupt-board regression 2 passed, 22 deselected; ruff and diff check pass. Original two failures reproduced on clean fork base.

* fix(kanban): keep PR continuation through status comments and unobserved ticks

Verified 213 passed, 2 skipped across focused DB/CLI/watcher suites; subprocess stdin guard passed.

* fix(kanban): consume event-ordered PR requeue intent

Verified 183 passed, 1 skipped across DB/CLI/watcher; ruff and subprocess stdin guard pass. Same-second event-order mutant fails the regression test.

* docs(kanban): state one-shot PR intent ordering

* fix(kanban): bind PR comments to events and consume dependency intent

Verified real dispatcher regressions RED before fix, then 210 passed, 2 skipped in focused DB/CLI/watcher suite; ruff and subprocess guard passed.

* fix(kanban): pin READY requeue to PR comment identity

Legacy same-second inline audit comments can share author and length; requeue snapshots the PR row id and remains one-shot. Verified 211 passed, 2 skipped; ruff clean.

* fix(kanban): snapshot comment identity for every resume intent

Verified focused DB/CLI/watcher/core suite: 217 passed, 2 skipped. Legacy equal-second strict mutant fails the intended arm.

* fix(kanban): guard-stuck age ignores data events; board-scoped recovery command

- respawn_guard_stuck_tasks: only kinds that can change the active_pr answer
  (_RESPAWN_GUARD_FAILURE_RESET_KINDS + dependency_wait + spawned, or a guard
  decline for another reason) restart the continuous-guard age; comments,
  heartbeats, attachments are data.
- render_operator_command(board, verb, *args): single renderer, always emits
  --board <slug>; clear_verb uses it; watcher passes the probed board.
- test_triage_resolve_records_who_and_why: expect after_comment_id ==
  max(task_comments.id) at emit time (CI slice 15/16 red).

Verified: new tests RED on 581299d (4 failed), GREEN here; recovery
command executed via real CLI on default and secondary boards (rc0);
no-board renderer mutant fails; 244 passed/1 skipped on db/triage/
watchers/cli/boards; ruff clean.

---------

Co-authored-by: Kyzcreig <9063726+Kyzcreig@users.noreply.github.com>

* fix(lcm): FTS parity COUNT(*) ran under _LOAD_LOCK on every engine load (freeze #3) (#966)

* fix(lcm): FTS parity COUNT(*) ran under _LOAD_LOCK on every engine load (freeze #3)

Card t_d3963974. Third Apollo boot-cost freeze. The cause:
_fts_needs_rebuild_structural ran `SELECT COUNT(*) FROM messages`
(SCAN messages USING COVERING INDEX, 2.5M rows) plus
`COUNT(*) FROM messages_fts_docsize` on EVERY MessageStore/SummaryDAG
construction, under plugins.context_engine._LOAD_LOCK. Measured on an APFS
clone of the fleet DB: 13.4 s of a 17.9 s cold MessageStore() init. The
two autocommit COUNTs could also straddle a concurrent ingest. They gave a
false mismatch 15/300 times, and each mismatch triggered a full inline
FTS drop+rebuild. That happened twice on 2026-09-24: held 779.8 s, 12
turns queued.

Provenance: the count came in with the original vendor import 8b86963
(2026-06-16) and was carried unchanged through re-vendor 27b6178.
#887/#902/#903 did not touch it. #903's plan check exempted
USING COVERING INDEX and skipped messages_fts*, so it passed on this code.

Fix:
- The parity check moves to _fts_count_parity_mismatch. It never runs on
  the throttle=True (load) path. A metadata marker
  (fts_parity_checked_at:<fts>) throttles it to once per
  LCM_FTS_PARITY_CHECK_INTERVAL_HOURS (default 6 h). When due, it runs
  on the existing background integrity thread (own connection). A
  mismatch sets the /lcm doctor integrity flag instead of rebuilding
  inline. Both counts are read in one snapshot.
- Explicit repair (throttle=False) and /lcm doctor still run parity
  synchronously.
- A no-op load writes nothing:
  - _clear_integrity_failed runs only after a real repair. Before, it
    also erased the background corruption flag on every load.
  - The messages_dedup_v1 and schema_version upserts are marker-gated.
  - The integrity claim uses a 1 s busy timeout, not 30 s.
- Lifecycle GC (on_session_start, every agent init):
  - no longer holds BEGIN IMMEDIATE across two SELECT DISTINCT
    session_id full scans;
  - uses indexed per-session probes;
  - runs at most once per 6 h per process.
- _backfill_search_content no longer rewrites NULL over NULL for
  undecryptable rows. That rewrite fired msg_fts_update on every boot.

Test: test_lcm_init_cost_regression now traces engine construction +
on_session_start on the loading thread. It fails on ANY SCAN of messages,
messages_fts*, summary_nodes and nodes_fts*, covering index included
(only LIMIT-bounded statements are exempt). It also asserts that a
steady-state load needs no write lock and preserves the corruption flag.
RED on df43599 (3 failed); GREEN with this change;
tests/context_engine: 393 passed.

* test(lcm): lock parity-race repro and document restart meltdown

---------

Co-authored-by: Apollo <apollo@angventures.io>

* fix(gateway): restart follow-ups keep adapter-granted admission; refused replays reported lost (t_43e058b7)

Argus r8 N1 (t_e253d9d5): SessionSource.to_dict drops is_bot / role_authorized /
delivered_via_upstream_relay / profile_route_rejected, so a spooled follow-up
admitted only by ALLOW_BOTS, ALLOWED_ROLES or the relay was refused as
"Unauthorized user" on boot replay, its spool file acked, and
restart_followup_lost logged 0 lines.

Trust model: to_dict stays wire-safe (unchanged). The spool record carries the
flags in a separate `admission` block and the whole record is HMAC-SHA256'd
with a per-home 0600 key (<home>/gateway/restart_followups.key). On load the
flags are restored only if the MAC verifies; otherwise no trust flag is
restored (only fail-closed profile_route_rejected is honoured) and
PHASE=restart_followup_untrusted is logged. Live policy is still re-evaluated
by the normal intake. A replay the intake refuses (unauthorized /
profile_route_rejected) now logs PHASE=restart_followup_lost with reason.

MF (same review): AST contract that the post-turn draining site spools
pending_event itself, not None.

Verified: new real stop->boot e2e (human/bot/role/relay, forged, tampered,
gate-closed-during-restart, to_dict class guard) 8/8; on base 3 admission arms
fail, human control passes. Focused restart suites 49/49. Mutants: MAC
unchecked, refusal unreported, admission unrestored, MF pending_event=None all
KILLED. Argus probe_r8_source_authz_real_intake: B/R PRESERVED, CONTROL ok.
Session/authz/startup-restore suites 445 passed.

* fix(gateway): reject torn spool keys and gate replay refusals

Verified: 57 focused restart tests passed; forged invalid-key, tampered-admission, and two refusal-site arms exercised via real restart.

---------

Co-authored-by: Kyzcreig <9063726+Kyzcreig@users.noreply.github.com>
Co-authored-by: Apollo <apollo@angventures.io>
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.

1 participant