Repository navigation
Sunglasses 0.6.14 narrows what three rules read as their carrier, adds lookahead guards to a fourth, and adds checks around the rule set. GLS-EX-015 has lookahead guards in front of its later parts; each guard asks only for something every full match already needs, so the rule returns the same match on every text, which the entry checks on a table of texts and does not prove over all texts. GLS-MCP-047, GLS-DFP-069 and GLS-SC-019 read a narrower set of texts: the bare word total does not count as MCP context, three policy keys read as a conda descriptor in the JSON form, and GLS-SC-019 matches its terms as whole words. The rest of the release is tests, scripts and documentation: scripts/pattern_perf_gate.py reads its before-tables at the merge-base and accepts a dated waiver list, a corpus of real agent text measures false positives, a ratchet covers rules whose regex can match zero characters, the generated tables are marked as generated, comments name no working session, agent or reviewer, and receipt reason texts cite no ruling numbers. Every entry below names its pull request and states what it does not cover.
Changed
-
GLS-EX-015 (indirect secret relay): the regex gets lookahead guards in front of its later parts.
Each guard asks only for something every full match already needs: a secret word within reach of
the opening word, a closing word within reach of the opening word, a closing word and an inclusion
word within reach of the secret word, and the first letter of each part before the part itself.
A lookahead consumes nothing, so the rule returns the same match (start, end, text) on every text,
holds the same texts, reports the same span, and keeps its channels, keywords and description. The
rule's entry inattack-dbandsunglasses/_prefilter_table.pyare regenerated for the new regex
text.tests/test_ex015_cost_only_equivalence.pypins the guarded form and compares the unguarded
and guarded regexes on span, text and groups over a table of word combinations, gaps at their
bound and one either side, line breaks, case-folding letters and seeded random strings, and scans
with the engine under either form in each channel the rule declares. Equivalence is argued from
the guards being necessary conditions and checked on those texts; it is not a proof over all
texts. (#492) -
GLS-MCP-047, GLS-DFP-069 and GLS-SC-019 read a narrower set of texts as their carrier. GLS-MCP-047
ends in a consuming authority-phrase condition, so a finding carries match text, and the decision
on any text is the one the lookahead form gave; its MCP-context condition does not count the bare
wordtotal(MCP,Model Context Protocol,completion/complete,values[],hasMore,
ref/promptand the other named forms count). In the JSON form of GLS-DFP-069 only the policy
keysx-conda-agent-policy,agent_instructionsandscanner_policyread as a conda
descriptor; a quotedname,channels,dependencies,variablesor environment-path key does not,
and theenvironment.ymland YAMLchannels:forms are unchanged. GLS-SC-019 matches its three
terms as whole words, with plural,-ingand-edforms, soprompt_templatesand_tool_call
in a stack trace are not terms;{{ ... }}is unchanged. Theattack-dbrows, the prefilter
table and the derivation table are regenerated for the three rules. Recall of the three is
measured on the probes in the test file only, not on a held-out set. (#494)
Internal
-
`, not at the base revision, so a pull request that forked before another one changed a rule is not judged on that rule. When the checkout cannot name a merge-base the gate fails and says to fetch the full history and the head commit; it never falls back to the base revision, and the output names the merge-base it used. A pull request that changes a rule may also remove waivers: the waivers that decide the verdict are the base revision's file minus the ids the head removes, and every entry of the head's file must be an entry of the merge-base's file with the same reason. An added entry or an edited reason alongside a rule change fails, and so does a file that is not a JSON object. A removed id is judged with no waiver and every expression of its rule is timed, changed or not, so a rule that still trips fails and a rule that stops tripping passes; a waived rule that is bounded and keeps its waiver still fails as stale. The workflow file, the timing constants and the waiver contents are not changed. Tests run on real git repositories (`tests/test_pattern_perf_gate.py`), and `tests/test_pattern_perf_workflow.py` pins the new reads in the script. (#499)scripts/pattern_perf_gate.pyreads the tables it times as "before" at `git merge-base -
tests/pattern_perf_waivers.jsonis new: a list of rule ids, each with a dated reason, for the
rules the pattern timing check found over budget on the tree when the list was made. A waiver
suppresses the growth, ratio and cap failures of that id for later pull requests; it does not
change what the rule costs, and no rule is bounded by this file. No rule, script or workflow
changes. (#496) -
tests/fixtures/real_transcripts/holds 195 excerpts of real agent text taken from public issues
of coding-agent projects (tool-call logs, session captures, protocol payloads, API error bodies,
console output), withMANIFEST.jsonrecording for each the source issue URL, the licence the
repository declares (or that it declares none), a hand label (190ordinary, 3relay, 2
attack), whether the text was read in full or screened, a hash, and the scanner's decision and
finding ids on themessage,file,api_responseandtool_outputchannels. Tokens,
token-shaped values in authentication fields, e-mail addresses, home-path user names and provider
request ids are replaced before a text is stored.tests/test_real_transcripts_corpus.pychecks
that rows and files match, that each hash matches, that no replaced detail has come back, and
that a scan is no worse than the recorded decision: anordinaryorrelaytext may not step up
(allow, quarantine, block) and anattacktext may not step down. A better scan passes. The
README says the texts are third-party text quoted for testing and that an excerpt is removed on
request through an issue. No rule, pattern, table or workflow is touched. The corpus measures
false positives on ordinary agent text and holds too few attack texts to say anything about
recall. (#490) -
Comments under
sunglasses/name no working session, agent or reviewer, and cite contract rows
ascontract row n.m. A few comments that carried
unsupported timing, error-rate or test-count claims say only what the code does. Comment text
only:ast.dumpof each of the 41 changed files is identical before and after. Ruling,
finding and test-case ids and review-round mentions remain in some comments. (#500) -
Tests only:
test_a_signal_stops_every_child_even_in_the_middle_of_a_file[sigint]starts the
command under test with SIGINT at its default, as a terminal does, so it does not time out
when the test run is started in the background of a non-interactive shell (that shell hands down
SIGINT as ignored, Python then installs noKeyboardInterrupthandler, and the command ignores
the signal). The stand-in child intests/test_scan_file_deadline.pywrites its pid to the file
named byREADING_LOGwhen it begins to read a marked file, and the test waits until four pids
are there instead of pausing a set time; after the command ends it polls for each child to be
gone. WithoutREADING_LOGthe stand-in writes nothing there. No file undersunglasses/is
touched. (#495) -
Tests only:
test_the_pager_drives_a_whole_list_through_one_channelserves both pages over one
in-memory stream that stays open until the second page is answered, and asserts that
session.closed_with()isNone. A page answered over its own one-frame stream reaches end of
stream, and a call pending at end of stream closes the session (a malformed-upstream fault, the
intended behavior), which happened whenever the reader thread was descheduled between handing
page 1 over and its end-of-stream check. The test body runs in atry, and itsfinallystops the server loop,
closes the stream and joins both threads with a bound; a worker still running after its bound
fails the test. No product change. (#504) -
The receipts wire specification (
WIRE_SPEC.md), the reason texts printed bysunglasses receipts --verify(codes.py) and the rotation note in the test vectors (VECTORS.json, regenerated byte for
byte bymake_vectors.py) cite no ruling numbers and name no reviewer. The rotation reason says that
a later segment whose genesis names another key is a claimed transition whose continuity is not
verified because rotation is not built, and that the result is not a pass. The pairing-limit reason
points at the spec section "A proxy chain's lifecycle is the session". Strings only:ast.dumpof the
five changed Python files is identical once string constants are blanked.tests/test_receipts_exit_classes.py
andtests/test_receipts_proxy_lifecycle.pyrequire the new exit-codes heading and the section
pointer. (#501) -
tests/test_empty_match_rules.pyis a ratchet over every rule and mechanism regex: each regex is
parsed and its width measured before the ids are reduced, so a regex that does not parse fails
the gate wherever it sits in a rule's list. A rule whose parsed width is zero must be in
ZERO_WIDTH, and a listed rule that consumes text must leave it; the list holds the rules that
have the shape today and may only shrink. For each of GLS-MCP-047, GLS-DFP-069 and GLS-SC-019 the
file holds an attack that still fires it (with non-empty match text for GLS-MCP-047) and texts of
ordinary form that stay quiet: a status listing withTotal loadedandoverride, a JSON request
body with one of the five removed keys per case, and a Python stack trace. The three policy keys
of GLS-DFP-069 are each shown to read as a carrier. The other zero-width rules are listed in the
ratchet and not rewritten. (#494) -
Docstrings under
sunglasses/proxyandsunglasses/extractorscite contract rows by number
(contract row 5.3) in place of seat-and-row labels, and drop ruling numbers, reviewer names and
finding ids. Statements that did not match the code say what it does: the two refusals
Session.request_release_decisionreturns and who owes the answer for each, the control channel and
activation flowbuild_routewires, the order in whichSession._closewalks the pending, settling
and claimed tables, which attemptSession.settle_attemptsettles for a token from an earlier
generation, the precedenceprocess_exit_codeapplies, and howImageExtractor._decode_partialcounts
UTF-16 loss. Docstring text only:ast.dumpof each changed file is identical to the base once string
constants are blanked, every changed string constant is a docstring, and every comment is identical
token for token. (#505) -
sunglasses/_prefilter_table.pyandsunglasses/_derivation_table.pyare marked
linguist-generated=truein.gitattributes, so GitHub collapses their diffs in a review; no merge
driver is set and Git's ordinary conflict handling applies.CONTRIBUTING.mdsays to resolve the
conflicts in the files a generated module is generated from, run the script its docstring names and
stage the result. The docstring ofscripts/gen_derivation_table.pystates which sources the table
serves.tests/test_generated_tables_marked.pyfinds each module undersunglasses/whose docstring
starts withGENERATED, reads Git's effective attributes for it and requireslinguist-generatedset
andmergeunspecified, runs the script the docstring names with--checkand requires exit status 0,
and requires the rule sentence inCONTRIBUTING.md. The rules are run on small repositories built to
break them. A script that exits 0 on--checkwithout comparing anything is not detected; the content
comparison of each table is intests/test_prefilter_table.pyandtests/test_derivation_table.py.
No pattern, engine or generated module changes. (#493) -
Docstrings in the top-level
sunglassesmodules,sunglasses/receiptsandsunglasses/receipts/tests
cite contract rows by number and drop ruling numbers, reviewer names and finding ids, keeping the
statement of what the code does.sunglasses/_prefilter_table.pyis regenerated: one line changes, the
recorded hash ofsunglasses/_prefilter.py, whose docstring text changed. Docstring text only:
ast.dumpof every other changed file is identical to the base once string constants are blanked, and
every changed string constant is a docstring. Round and release labels that are not ruling references,
reviewer names or finding ids stay as written, so some history wording remains in docstrings. (#506) -
Tests only:
test_paired_g2_04_exact_transport_calibrationreads theterminatedflag of theSETTLED
receipt and checks the whole outcome against it. The proxy's deadline starts when it begins the scan,
after the content is extracted. A worker still running when the wait ends is terminated and settles
SCAN_DEADLINEwith an incomplete inspection and no finding; a worker that exited inside the wait and
returned a complete result settles the finding it made. Both are possible for the same request. In both
cases the client gets exactly one reply, with the id of its request, and it is the withholding error;
theSETTLEDreceipt names the same request, is not forwarded, and states the same reason and
completeness as the reply; nothing arrives at the destination and no bytes go upstream. Terminated:
reasonSCAN_DEADLINE, inspection incomplete, no finding and no detector. Not terminated: reason
PROHIBITED_SECRET, inspection complete, a blocked finding, and at least oneGLS-SD-rule id. The
control route's delivery assertion is unchanged. No harness or product change. (#503)