Skip to content

hyp session ignore's receipt reports the write it made, not a drop nothing verified - #522

Open
philcunliffe wants to merge 5 commits into
masterfrom
fix/issue-460
Open

hyp session ignore's receipt reports the write it made, not a drop nothing verified#522
philcunliffe wants to merge 5 commits into
masterfrom
fix/issue-460

Conversation

@philcunliffe

@philcunliffe philcunliffe commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

The defect

hyp session ignore printed:

session <id>: ignored - the gateway will drop this session (1 ignored)

The gateway cannot know that. control.js adds an opaque token to a Set and answers ignored: true for whatever it was handed; the drop happens later, in the client adapter, keyed on the session_id that adapter resolves and stamps (LLP 0066 R5). Nothing in between compares the two. So an id no exchange will ever carry - a Codex thread id, a finished session's id, a typo - printed exactly that line while the session kept being recorded. As the triage on #460 recorded, a GET after the POST does not help: it is the same Set.has over the same set.

The direction taken

Per the maintainer's answer on #460 (options 3 + 4): accept-and-document, not a new gateway capability. Having the route resolve and echo the grain would put client-grain knowledge into a deliberately provider-agnostic control surface (LLP 0050), so the contract becomes the stated narrow one and the receipt stops implying more. Correctness stays where PR #458 put it: the caller resolves the container before it calls, and #435 will narrow that to one resolver.

What changed

Docs (design landed with the code). LLP 0066 gains §receipt-is-membership and R14: ignored: true is set membership; a confirmed opt-out is reported as the write it is; a read-back does not upgrade the claim; machine-readable receipts are covered too; and any surface calling the route directly applies the CLI's response validation. LLP 0067 gains §cli-receipt, and the two places that quoted the removed string are updated.

CLI (session_command.js). The headline is now ignored - this id is in the gateway drop set, followed by a MEMBERSHIP_NOTE stating what that proves - printed next to the success, where a caller reading "ignored" as done will see it. --json carries guarantee: "set_membership" beside status: "ok", since a bare ok is what the skills parse. The reader prints the same qualifier from the same constant, for the reason EPHEMERAL_NOTE is shared: two statements of one contract drift.

Route (control.js). JSDoc states what the reply is a reply about, and why the route may not say more.

Skills. The three shell paths that POST the route (claude/hypaware-ignore, claude/hypaware-privacy, codex/hypaware-privacy) gained the echo check the JS resolver already had - session_id compared byte-exactly against the id sent, ignored a real boolean, total a real number - and each states the narrow contract in prose. Previously they printed opt-out confirmed for session <whatever the responder said>.

Test evidence

test/plugins/ai-gateway-session-ignore-receipt.test.js constructs the exact false-confidence case rather than asserting a string in isolation: an id live traffic does not carry is registered successfully, the real Codex exchange projector is then shown recording that traffic anyway (the row is stamped with the container), and the receipt printed over that outcome is required not to have promised a drop.

Before the fix (6/6 fail); the actual output captured by the failure is the defect itself:

not ok 1 - a successful ignore receipt does not claim a drop, for an id live traffic never carries
  error: 'the gateway never saw an exchange; it cannot promise a drop, and here there is none'
  actual: |-
    session thread-subagent: ignored - the gateway will drop this session (1 ignored)
    ...
not ok 2 - the --json receipt states its guarantee, so `status: ok` cannot read as "dropped"
not ok 3 - the reader carries the same qualifier, so writer and reader cannot drift
not ok 4 - claude/skills/hypaware-privacy/SKILL.md checks the control reply is about the session it posted
not ok 5 - claude/skills/hypaware-ignore/SKILL.md checks the control reply is about the session it posted
not ok 6 - codex/skills/hypaware-privacy/SKILL.md checks the control reply is about the session it posted
# pass 0
# fail 6

After (6/6 pass):

ok 1 - a successful ignore receipt does not claim a drop, for an id live traffic never carries
ok 2 - the --json receipt states its guarantee, so `status: ok` cannot read as "dropped"
ok 3 - the reader carries the same qualifier, so writer and reader cannot drift
ok 4 - claude/skills/hypaware-privacy/SKILL.md checks the control reply is about the session it posted
ok 5 - claude/skills/hypaware-ignore/SKILL.md checks the control reply is about the session it posted
ok 6 - codex/skills/hypaware-privacy/SKILL.md checks the control reply is about the session it posted
# pass 6
# fail 0

Full suite: 3084 pass / 8 fail, against a 3078 pass / 8 fail baseline on the same origin/master - the 8 leave ... failures are pre-existing and untouched by this change. npm run typecheck clean; npm run smoke -- session_optout_capture_drop and core_boot_noop both ok.

Scope note

Deliberately does not touch §cli-response-check's open question about proving the responder is the gateway - that is #451, in flight separately. The echo check added to the skills is the existing CLI check being applied where it was missing, which the #460 review explicitly folded in.

Fixes #460

…fied

`hyp session ignore` printed "ignored - the gateway will drop this session".
The gateway cannot know that: `control.js` adds an opaque token to a `Set` and
answers `ignored: true` for whatever it was handed, and the drop happens later
in the client adapter, keyed on the `session_id` that adapter stamps on the row
(LLP 0066 R5). Nothing compares the two, so an id no exchange will ever carry -
a Codex thread id, a finished session's id, a typo - printed the same line while
the session kept being recorded.

Per the maintainer direction on #460 (options 3 + 4), this is accept-and-document
rather than a new gateway capability: making the route answer the grain question
would put client-grain knowledge in a deliberately provider-agnostic control
surface (LLP 0050). So the contract becomes the stated one and the receipt stops
implying more.

- LLP 0066 gains §receipt-is-membership and R14: `ignored: true` is set
  membership, the caller owns resolving the container first (R13), a read-back
  does not upgrade the claim, and any surface calling the route directly applies
  the CLI's response validation. LLP 0067 gains §cli-receipt.
- The CLI receipt says "this id is in the gateway drop set" and prints what that
  proves, next to the success where a caller reading "ignored" as done sees it.
  `--json` carries `guarantee: "set_membership"` beside `status: "ok"`, since a
  bare `ok` is what the skills parse. The reader prints the same qualifier, from
  one shared constant, for the reason EPHEMERAL_NOTE is shared.
- The three skills that POST the route directly gained the echo check the JS
  resolver already had (`session_id` compared byte-exactly against the id sent,
  `total` a real number), and each states the narrow contract in prose.

Regression test constructs the exact false-confidence case rather than asserting
a string: an id live traffic does not carry is registered successfully, the real
Codex projector is then shown recording that traffic anyway, and the receipt
printed over that outcome must not have promised a drop.

Co-Authored-By: Claude <noreply@anthropic.com>
@philcunliffe

Copy link
Copy Markdown
Contributor Author

🤖 neutral: PR #520 and PR #522 both write the #460 contract, in different documents

These two PRs were built in parallel this tick by workers blind to each other. Each is coherent alone; the overlap only bites if both merge. Flagging rather than resolving, because where a normative statement lives is your call.

PR #520 (fix/issue-451) mints LLP 0166 §membership-not-grain, which states the #460 contract in full prose - ignored: true means set membership, the caller owns resolving the key, option 1 declined because the route treats the token as opaque - and explicitly defers the code: "realized in the change set for #460 rather than here." It was written that way because you asked in the #451 thread that the two contract statements be written together.

PR #522 (fix/issue-460) states the same contract as LLP 0066 §receipt-is-membership + R14, and implements it.

So after both merges the rule is stated normatively in two places. That is the drift LLP discipline exists to prevent, and a future reader has no signal which is authoritative.

Options:

  • (a) Keep LLP 0066 R14 as the normative home (it is the spec, and it is where the requirement is numbered and implemented); trim LLP 0166 §membership-not-grain to a one-line citation of R14 instead of a restatement.
  • (b) Keep LLP 0166 as the joint decision record you asked for; drop R14 and have LLP 0066 §receipt-is-membership cite LLP 0166.
  • (c) Accept both as-is if you read 0166 as narrative framing and 0066 R14 as the binding requirement - in which case say so, and 0166 should say so too.

Also note: both PRs edit llp/0067-session-opt-out.design.md - #520 adds an Extended-by forward-ref at §cli-response-check, #522 adds §cli-receipt. Different hunks, so git should merge them cleanly, but whichever lands second will need a merge-base pass.

Neither PR is blocked on this; both remain individually reviewable. Reply here (or on the sibling PR) and neutral will apply your choice on a later tick.

…o R14 too

Two findings from the #460 review, both the reviewed change's own rule applied
where it was not yet applied.

The removal receipt printed "recording resumed", which is the same inference
from the same `Set` answer read the other way: a token no exchange carried was
suppressing nothing to resume, and `.hypignore` is an independent governor (R7)
that can keep a session unrecorded whatever this verb removed. It now reports
the removal it made.

The three skill verifiers gate `total` with `isinstance(..., int)` while
claiming to mirror `validateControlResponse`. `isinstance(True, int)` is True in
Python, so a responder answering `total: true` passed the skill check and would
have failed the CLI's `typeof total !== 'number'` - a gap in the very check
LLP 0066 R14 makes a MUST for surfaces calling the route directly. bool is now
excluded.

Both are pinned by tests that fail without the fix, and LLP 0067 §cli-receipt
records the removal half of the contract.

Co-Authored-By: Claude <noreply@anthropic.com>
@philcunliffe

Copy link
Copy Markdown
Contributor Author

Verdict: approve, with two findings fixed in-branch (8a7a79f). The change does what #460's settled direction (options 3 + 4) asked for: the receipt now states set membership, names who owns the key, and stops promising a drop. Both findings are the reviewed change's own rule not yet applied to two places it reaches.

Verification performed

  • Fail-then-pass, done by hand. Reverted only the five source files (session_command.js, control.js, the three SKILL.md) to origin/master and left the test in place: 6/6 fail. Restored: 6/6 pass. The claim in the PR body holds.
  • Baseline. npm test on the head: 3084 pass / 8 fail, and the 8 are exactly the leave ... failures tracked as test/core/leave-command.test.js: 8 of 11 tests fail on master (exit 1 from hyp leave), red since the tests were introduced #512. No others.
  • After the fixes: 3085 pass / 8 fail (the same 8), npm run typecheck clean, npm run smoke -- session_optout_capture_drop ok.
  • The tests are not vacuous. Test 1 is the strongest thing here: it drives the real createControlHandler over a live socket and the real createCodexExchangeProjector, so "the registered id suppresses nothing" is proven against the code that performs the drop rather than against a restatement of it. Tests 4-6 are text assertions over SKILL.md, which is the only feasible check for shell embedded in markdown and follows the existing convention in codex-privacy-skill-session-id.test.js.
  • Receipt paths swept. All four report shapes plus the error paths were checked for a residual drop promise: the mutation text, the mutation --json, writeStatus's three branches, and the hyp session: stderr paths. A repo-wide grep for will drop this session / gateway will drop finds only the LLP and test sites that quote the removed string deliberately. Nothing remaining promises a drop.
  • Shell. All three scripts are #!/usr/bin/env bash with set -euo pipefail, so bash constructs are in bounds; the added checks keep the [ ... ] convention already in the files and quote every expansion. python3 -c '...' "$ID" passes the id as sys.argv[1], which is correct. Fail-closed confirmed: in hypaware-ignore the verifier sits inside total="$(...)", and under set -e a failing command substitution in an assignment aborts before the printf 'Ignored session ...' line can run (verified empirically: exit 1, nothing printed). In the two privacy skills the verifier is the last command, so a mismatch exits nonzero with no opt-out confirmed line.
  • LLP. #receipt-is-membership, #cli-receipt, R13 and R14 all resolve, and the LLP 0050 rejection of option 1 is recorded with its ground rather than as a preference. The @refs earn their place: [constrained-by] on the route (why it may not say more) and [implements] on the receipt are genuinely different statements, and neither is inferable from the code.
  • House rules. No em dashes, no semicolons, no @typedef, no inline import('...') types anywhere in the diff.

Findings

1. Low - hypaware-core/plugins-workspace/ai-gateway/src/session_command.js:332 - the removal receipt made the mirror-image overclaim. not ignored - this id is out of the gateway drop set, so recording resumed is the same inference from the same Set answer, read the other way. If the id was never one live traffic carried, nothing was being suppressed and nothing resumed; and .hypignore is an independent governor (R7) that can keep the session unrecorded whatever this verb just removed. R14 forbids exactly this ("no surface may present a control-route reply as evidence that the id it sent is the one live traffic carries"), and LLP 0067 §cli-receipt is titled for the write verbs, plural. The PR rewrote this line already, so this was in scope rather than pre-existing-and-untouched.

Fixed: the headline now reads ... out of the gateway drop set, so this opt-out suppresses nothing now. Pinned by a new test (the unignore receipt reports the removal, not a resumption it cannot verify) that drives runSessionUnignore against the live control handler and fails on the old string. LLP 0067 §cli-receipt records the removal half of the contract.

2. Low - hypaware-core/plugins-workspace/claude/skills/hypaware-ignore/SKILL.md:44, .../claude/skills/hypaware-privacy/SKILL.md:55, .../codex/skills/hypaware-privacy/SKILL.md:144 - the numeric check is weaker than the one it says it mirrors. All three comments claim the "same three checks hyp session ignore applies (validateControlResponse)". Two of the three match: r.get("ignored") is not True is strict, and the session_id echo is byte-exact. The third does not. isinstance(True, int) is True in Python, so a responder answering {"ignored": true, "total": true, "session_id": <echo>} passes the skill's check and would be rejected by the CLI's typeof body.total !== 'number'. That matters precisely in the case the check exists for - some other local process now owning a recycled port - and R14's third bullet makes applying "the same response validation the CLI does" a MUST for surfaces calling the route directly, so the gap sits inside the rule this PR mints.

Fixed: isinstance(r.get("total"), bool) is now excluded in all three, with a one-line comment saying why. The existing test regex still matches, and a new assertion pins the bool exclusion so the divergence cannot come back.

Not findings, recorded so the next reader does not re-derive them

  • writeStatus's not_ignored branch still prints not ignored - this session IS being recorded, which is the same class of claim. It is untouched by this PR, it carries FOLDER_GOVERNOR_NOTE immediately after it, and it errs in the fail-loud direction (over-warning about recording, not under-warning), so it is left alone rather than pulled into this change.
  • The reader's --json gains no guarantee field. Correct as argued: it already reports status as ignored / not_ignored / unknown beside a tri-state ignored, which is a membership answer on its face, unlike the write verbs' bare status: "ok".

Known cross-PR overlap (already surfaced, no action taken here)

PR #520 (fix/issue-451) mints LLP 0166 whose §membership-not-grain states this same #460 contract in prose and defers the code to this PR, while this PR states it as LLP 0066 §receipt-is-membership + R14. Both PRs also edit llp/0067-session-opt-out.design.md in different hunks. Already flagged on both threads; which document owns the rule is the maintainer's call, and nothing was changed here on that account.

… was believing

The CLI's removal receipt was corrected in round 1, but the skill that DELETEs
the same route was left printing "Recording re-enabled for session <id>" off
`--fail-with-body` alone, with the response body sent to /dev/null. That is the
same mirrored overclaim, plus R14's last bullet (any surface calling the route
directly applies the CLI's response validation) unapplied entirely rather than
merely weakly: any 200 from whatever owns that port read as success.

It now reads the reply and asserts `ignored` is a real boolean false, `total` a
real number with bool excluded, and `session_id` echoed byte-exactly, then
reports the removal in the CLI's words. Pinned by the shared skill loop plus a
removal-specific test; LLP 0067 §cli-receipt records it.

Co-Authored-By: Claude <noreply@anthropic.com>
@philcunliffe

Copy link
Copy Markdown
Contributor Author

Verdict: approve, with one finding fixed in-branch (de7cbc6). Both round-1 fixes landed and are correct, verified against the committed tree rather than the round-1 write-up. The remaining finding is the same class again: R14, the rule this PR mints, not yet applied to the fourth surface in the family. Round 1 fixed the CLI's removal receipt; the skill that calls the same route for the same removal was still making the mirrored claim, and was the one caller in the set with no response validation at all.

Round-1 fixes: verified, not taken on trust

Fix 1 (the unignore receipt) landed. session_command.js:330 now reads ... out of the gateway drop set, so this opt-out suppresses nothing now; recording resumed survives nowhere in src/. The pinning test (the unignore receipt reports the removal, not a resumption it cannot verify) drives runSessionUnignore against the live control handler and asserts both halves. LLP 0067 §cli-receipt carries the removal paragraph.

Fix 2 (the bool hole) landed, and I exercised it. python3 is absent from the review host, so I ran the verifiers under a borrowed CPython 3.11: extracted each skill's python3 -c body verbatim from the committed SKILL.md and fed it JSON on stdin, exactly as the pipeline does.

At a537627, the pre-fix predicate against {"session_id": "sess-abc", "ignored": true, "total": true}:

rc=0  stdout='True'

Run through the whole extracted bash block, that is the receipt a hostile-or-confused responder got:

Ignored session sess-abc. Total ignored: True
    exit=0

At 8a7a79f, same input, all three verifiers:

opt-out NOT confirmed: {"session_id": "sess-abc", "ignored": true, "total": true}
    exit=1

Fails closed, nothing printed. I swept eleven adversarial bodies (bool total, string total, ignored: 1, missing ignored, wrong session_id echo, {}, a JSON array, non-JSON) plus the honest reply, against all verifiers: only the honest reply for that verb's own direction is accepted, everything else exits nonzero. The bool exclusion is placed before the int test, so short-circuiting is not load-bearing either way.

Finding

1. Medium - hypaware-core/plugins-workspace/claude/skills/hypaware-unignore/SKILL.md:27-33 (pre-fix) - the removal skill made the mirrored overclaim and skipped R14's validation MUST entirely. It sent the response body to /dev/null and printed Recording re-enabled for session <id> off --fail-with-body alone.

Two separate breaches of the rule this PR mints:

  • The claim. This is the exact line round 1 removed from the CLI, in the surface a user is more likely to actually hit. The gateway cannot know recording resumed: a token no exchange carried was suppressing nothing to resume, and a .hypignore ancestor (R7) is an independent governor. The skill's own Notes already conceded the second half while the printed line asserted the opposite.
  • The validation. R14's last bullet is a MUST: "Any surface that calls the route directly rather than through the CLI MUST apply the same response validation the CLI does ... including the byte-exact session_id echo." This skill calls the route directly and validated nothing - not the echo, not ignored, not that the body was JSON. hyp session unignore runs validateControlResponse on the DELETE path just as it does on POST (session_command.js:913), so this was not the CLI's check being weaker here; it was absent. Any 200 from whatever now owns a recycled port read as this session's success.

The PR swept the three skills that POST and missed the one that DELETEs, which is why round 1's sweep did not name it: it looked at the receipt paths in session_command.js and at the skills the diff touched, and this file is in neither set. But it is the fourth member of the same family, R14 names it, and round 1 had already established that the removal direction is in scope by rewriting the CLI's half of it.

Fixed (de7cbc6). The skill now captures the reply and applies the CLI's three checks, adapted to the verb: ignored a real boolean false (removal is what was asked for, so is not False is the correct assertion here, not a copy of is not True), total a real number with bool excluded for the reason above, and session_id echoed byte-exactly. The receipt is the CLI's own words: Session <id> is out of the gateway drop set, so this opt-out suppresses nothing now. Total ignored: N. A "What the confirmation proves" note states the narrow contract, as the sibling skills do.

Verified end to end by running the extracted bash block against a stubbed curl:

--- {"session_id":"sess-abc","ignored":false,"total":2}
Session sess-abc is out of the gateway drop set, so this opt-out suppresses nothing now. Total ignored: 2   exit=0
--- {"session_id":"other","ignored":false,"total":2}
removal NOT confirmed: the reply is about session "other", not "sess-abc"                                   exit=1
--- {"session_id":"sess-abc","ignored":false,"total":true}
removal NOT confirmed: {...}                                                                                exit=1
--- {"session_id":"sess-abc","ignored":true,"total":2}
removal NOT confirmed: {...}                                                                                exit=1
--- not json
removal NOT confirmed: the reply was not JSON, so it is not the control route                               exit=1

Fail-closed holds: the verifier sits inside total="$(...)", so under set -euo pipefail a nonzero exit aborts the assignment before the printf can run - nothing is printed on any bad reply.

Pinned two ways: the skill is added to the shared SKILLS loop (so it inherits the echo, numeric and bool assertions), plus a removal-specific test asserting the body is no longer discarded, Recording re-enabled is gone, and ignored is asserted as a real false. Fail-then-pass proven by reverting the SKILL.md alone with the tests in place: 2/2 fail, restored 9/9 pass. LLP 0067 §cli-receipt records it.

Verification performed this round

Not findings, recorded so a third reader does not re-derive them

  • ai-gateway/src/index.js:74 still summarises the verb as Resume recording this AI session, and the unignore skill's intro says requests "are recorded again". These are statements of the verb's purpose, not receipts presented as evidence of a reply, which is what R14 governs; the CLI's own hyp session unignore - resume recording this session JSDoc is left in the same shape by this PR. Consistent, and changing them would be churn.
  • writeStatus's not_ignored branch is unchanged, for the reasons round 1 gave (untouched, carries FOLDER_GOVERNOR_NOTE, errs fail-loud). Still agree.
  • MEMBERSHIP_NOTE prints on the ignore path only, not on unignore. Correct: the removal headline now states membership on its own face, so a second paragraph restating it would be noise.
  • The pre-fix unignore skill erred toward the safe privacy direction (a user wrongly believing they were being recorded), which is why this is Medium and not High. The validation gap is what carries the weight, since it is an explicit MUST minted by this very change set.

Known cross-PR overlap (already surfaced, no action taken here)

PR #520 (fix/issue-451) mints LLP 0166 whose §membership-not-grain states this same #460 contract in prose, while this PR states it as LLP 0066 §receipt-is-membership + R14. Both PRs also edit llp/0067-session-opt-out.design.md; my round-2 edit adds a paragraph inside §cli-receipt, which is this PR's own new section, so it does not widen the overlap with #520's §cli-response-check forward-ref. Which document owns the rule remains the maintainer's call. Untouched here, and the sibling branch was not modified.

@philcunliffe

Copy link
Copy Markdown
Contributor Author

🤖 neutral: triage rung — PR #522 can ship safely at de7cbc6

Two review rounds exhausted (both real security-adjacent holes found and
fixed in-branch: the mirror-image unignore-receipt overclaim and the
isinstance(True, int) bool hole in round 1; hypaware-unignore's total
absence of response validation in round 2). Rather than a third automated
review round, triage independently re-verified the round-2 fix and swept for
anything a third round would have caught, specifically hunting for a fifth
unswept caller of the control route given what the same validation-gap shape
meant last round.

Result: no fifth caller, no open blocker. Full-tree grep for
_hypaware/ignore/session (no extension filter, unlike the narrower test/

The one open item — the cross-PR overlap with #520 on which document
(LLP 0066 R14 vs LLP 0166 §membership-not-grain) is normative — is not a
defect; it's already on this thread (and #520's) as the maintainer's call,
so it isn't re-litigated or re-filed.

Full evidence and independent verification steps recorded in #535.

@philcunliffe
philcunliffe marked this pull request as ready for review July 31, 2026 06:42
@philcunliffe philcunliffe added neutral:approved neutral reviewed this and holds it for a maintainer merge (own or adopted PR; LLP 0025/0030) and removed neutral:approved neutral reviewed this and holds it for a maintainer merge (own or adopted PR; LLP 0025/0030) labels Jul 31, 2026
test and others added 2 commits July 31, 2026 19:13
# Conflicts:
#	hypaware-core/plugins-workspace/ai-gateway/src/session_command.js
…the removed one

The merge is textually one conflict (both sides added a constant after
EPHEMERAL_NOTE in session_command.js) and one semantic one. Master's
ai-gateway-session-responder-trust.test.js, landed with #520, pinned the
headline this branch deletes - `ignored - the gateway will drop this session`
- as the precondition for "a spoofed success reads as done". That string is
exactly what LLP 0066 R14 forbids, so the assertion moves to the receipt this
branch prints instead. The test's own claim is untouched: an impostor's
confirmed `ignored` still has to carry the trust disclosure, and the drop set
it names is still the impostor's.

Co-Authored-By: Claude <noreply@anthropic.com>
@philcunliffe

Copy link
Copy Markdown
Contributor Author

neutral: triage rung - PR #522 can ship safely at 5f86d8a

Re-run of the triage disposition previously recorded at de7cbc6. Since
then the branch took two merge-forward commits from origin/master
(b3d3652e, 5f86d8a); gh pr diff 522 at the new head shows the PR's own
diff surface is unchanged except for one test file whose assertion string
was updated to match this PR's own receipt wording (a conflict against
master's #520 trust test, resolved correctly - the disclosure assertion is
still enforced, just against the current string).

Both prior review rounds' findings (the mirror-image unignore-receipt
overclaim, the isinstance(True, int) bool hole, and
hypaware-unignore's missing response validation) are still present and
correct in the committed tree at 5f86d8a. No new finding surfaced from the
merge: it is mechanical (concatenation in session_command.js, one regex
update in the trust test), independently re-run and green.

Full suite npm test: 3272 pass / 0 fail / 1 skipped (the previously-tracked
#512 leave-command baseline failures are gone at this head - fixed on
master and carried in by the merge, not a regression). Typecheck clean.
Relevant test files (ai-gateway-session-ignore-receipt.test.js,
ai-gateway-session-responder-trust.test.js) 13/13. Smoke
session_optout_capture_drop and core_boot_noop both ok. GitHub CI green
on all checks at this head.

The previously-flagged cross-PR overlap with #520 (which LLP owns the "the
control plane states, not proves" rule) is no longer an open ambiguity: this
branch's own history now includes #520's llp/0166-session-control-plane-states-its-guarantees.decision.md,
which explicitly relates to both LLP 0066 and LLP 0067 and does not
contradict either.

Full evidence recorded in #535.

@philcunliffe philcunliffe added the neutral:approved neutral reviewed this and holds it for a maintainer merge (own or adopted PR; LLP 0025/0030) label Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

neutral:approved neutral reviewed this and holds it for a maintainer merge (own or adopted PR; LLP 0025/0030)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

hyp session ignore's POST receipt cannot prove the id matches live traffic - it's Set membership, not drop verification

1 participant