Skip to content

[pull] main from DeusData:main - #157

Merged
pull[bot] merged 17 commits into
MrDolphin:mainfrom
DeusData:main
Sep 24, 2026
Merged

pull[bot] merged 17 commits into
MrDolphin:mainfrom
DeusData:main

Conversation

@pull

@pull pull Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

bmcnaboe and others added 17 commits August 19, 2026 10:14
Signed-off-by: Brian McNaboe <bmcnaboe@gmail.com>
Signed-off-by: Brian McNaboe <bmcnaboe@gmail.com>
Distilled from #1245 by Andrew Hundt (d15071c, the match_qn half).

match_qn was a raw strstr(qn, library_id). Short ids fired inside
unrelated words, and a service kind REPLACES the plain CALLS edge, so a
false match both invents a service edge and deletes a real call:

  "gin."    in plugin.           -> route registration; any "/"-prefixed
                                    first argument then minted a Route node
  "nconf"   in encryptionconfig  -> CONFIGURES for every call into the
                                    package
  "tonic"   in Monotonic         -> gRPC; unparseable, so NO edge at all
  "express" in expression        -> route registration
  "get_env" in get_envelope, get_environ -> CONFIGURES
  "dio" "surf" "curl" "resty" "phin" "hyper" "treq" "rocket" in studio,
  surface, curly, restyle, dolphin, hyperbolic, streq, rocketmq

The rule (qn_hit_on_boundary; every occurrence of an id is tried, not
only the first). A separator is any character that is not an ASCII
letter or digit.

  before: start of string | separator | the id itself starts with a
          separator ("@trpc/server") | the id starts with an uppercase
          letter -- a capital opens a new CamelCase word whatever
          precedes it (AsyncHttpClient, IHttpClientFactory, NSURLSession)
  after:  end of string | separator | the id itself ends in a separator
          ("gin.") | uppercase letter (GuzzleHttp, FeignClient,
          KafkaProducer) | digit (urllib2, Mint.HTTP2, amqp091-go)

Rejected: a lowercase- or digit-initial id glued to a preceding letter
or digit, and any id continued by a lowercase letter. Two deliberate
differences from the upstream hunk: upstream's before-rule needed a
GuzzleHttp table entry and would have dropped IHttpClientFactory and
NSURLSession, and without the digit after-boundary urllib2 and
amqp091-go stop matching. All three match on main today.

No recall is traded for this. No boundary rule can tell "grequests"
from "myrequests", so a library whose own name glues a prefix or suffix
onto another id gets an explicit entry with the kind (and broker) it had
on main -- 47 of them, found mechanically by running main's matcher and
the new one over 173 candidate names and listing every one that went
from a kind to NONE:

  HTTP      grequests txrequests redaxios gaxios libcurl curlpp curlcpp
            hyperlocal guzzlehttp
  ASYNC     aiokafka pykafka rskafka librdkafka rdkafkacpp aioamqp
            amqpstorm pamqp pyamqp amqprs amqpcpp pynats jnats aiomqtt
            amqtt hbmqtt umqtt mqttools emqtt rumqtt gomqtt libmosquitto
            mosquittopp pubsublite gocelery
  CONFIG    wgetenv getenvb qgetenv dotenvx phpdotenv
  ROUTE_REG apiflask honox fasthttprouter
  GRAPHQL   gqlparser gqlgenc aiogqlc

Left at NONE on purpose: names that are not clients of the id they
contain and were misclassified on main -- hypercorn, hyperlink,
hyperopt, openresty, gqlalchemy, celeryconfig, kafkacat, gnatsd,
pypubsub. One visible reclassification: hyperium/tonic was HTTP through
"hyper" inside "hyperium" and is now gRPC through "tonic".

RED, production reverted and the final tests kept:
  infrascan_service_pattern_match_rejects_ids_inside_words
      FAIL tests/test_infrascan.c:105:
      svc_case_mismatches(cases) == 28, expected 0 == 0
  4 passed, 1 failed
All 28 negatives mismatch on main. The 47 glued-library positives are
RED the other way round: against the boundary rule without the entries.
Each positive uses a name containing no second id, so it binds to its
own entry -- "aiokafka.AIOKafkaProducer" would have passed through
"KafkaProducer" and proven nothing.

GREEN: infrascan 5, pipeline 281, parallel 74, extraction 350,
edge_types_probe 59, route_canon 11, lang_contract 41, cross_repo 8,
registry 64, mcp 318 (4 Windows-only skips). 0 failed.

Effect on real code -- production binaries, main 92abefa against this
change. main against main differs by 0 edges and 0 nodes on all three
corpora, so every delta below is the change:

  django      CONFIGURES 76 -> 74; both calls return as CALLS
              (geom.get_envelope, WSGIRequestHandler.get_environ)
  typescript  CONFIGURES 12,026 -> 12,024; one bogus Route "/*type*/"
              gone with its CALLS and HANDLES; 3 calls return as CALLS
  kubernetes  CONFIGURES 7,962 -> 7,609: all 353 are "nconf" inside
              encryptionconfig / authorizationconfig /
              authenticationconfig, and all 353 return as CALLS.
              8 bogus Routes gone -- os.ReadFile of the serviceaccount
              namespace file, t.Run("/TwoWay"), "/tmp/test" -- with 26
              CALLS, 2 HANDLES and 21 TESTS edges that pointed at them.
              374 plain CALLS restored; 8 of them (validate.Monotonic and
              friends) had NO edge at all on main. GRPC_CALLS 60 -> 60.
              HTTP_CALLS 398 -> 407: nine calls main swallowed as route
              registrations now reach the existing arg_url heuristic like
              every other package.

88 of the 408 removed service-derived edges were read at the call site
-- all of django and typescript, 82 of 402 in kubernetes: 88 false
positives, 0 true positives lost. None of the three corpora uses a
glued-name library, so the 47 entries are covered by the unit test only.

Known limits: "_" is a separator, so optical_fiber.len still matches
"fiber." exactly as requests_get matches "requests" by design; a
lowercase id followed by a capital matches (kafkaProducer), which also
admits a dioXide-style name; a user wrapper glued without a separator
(mygetenv) is no longer classified; a glued-name library missing from
the list is NONE until someone adds it.

Co-authored-by: Andrew Hundt <ATHundt@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
… qualifiers

Distilled from #1245 by Andrew Hundt (c51e5cb, the extract_defs.c
half; the mcp.c and token-reduction parts are not taken).

For C-family functions return_type was the text of the declaration's
`type` node alone. In these grammars the `*`, `&` and `&&` live on the
declarator and cv-qualifiers are sibling nodes, so

    const char *get_name(void)     published   char
    robj **lookupKeys(...)         published   robj
    Text &operator=(const Text &)  published   Text

and no C or C++ function in any index had a pointer return type at all.
The C/C++ resolver's return-type parser already strips a leading
const/volatile and handles trailing `*`, `&`, `&&` -- branches that were
unreachable for functions, so every `Foo *f()` was registered as
returning Foo by value.

c_declared_return_type() renders the declared type at both sites
(functions and methods) for C, C++, CUDA, GLSL, HLSL, ISPC, Slang and
Objective-C -- the list resolve_func_name_c_family uses:

    <leading cv-qualifiers, source order> <base type, verbatim>
    <one space><markers, outermost first>

so `const char *`, `char **`, `Text &`, `Text &&`, `Text *&`,
`volatile unsigned long *`; a pointer-level qualifier follows its star
(`char *const *`); east-const `char const *` normalises to
`const char *`. With no qualifier and no marker the old text is returned
unchanged. It measures, then writes into one exact-size arena
allocation; the declarator walk is a plain child chain, no recursion.
def.return_types is untouched.

Two deliberate differences from the upstream hunk. Upstream copied every
type_qualifier node, which in these grammars includes constexpr,
_Noreturn, mutable and __extension__ -- `constexpr int f()` would have
become `constexpr int`. Only const, volatile, restrict, __restrict,
__restrict__ and _Atomic are kept, and both cases are asserted unchanged.
And the walk stops at the first declarator that is neither pointer nor
reference, so `int (*f(void))(int)` stays `int` rather than becoming a
wrong `int *`.

RED, production reverted and the final tests kept:
  c_function_return_type_preserves_pointer_and_qualifier
      FAIL tests/test_extraction.c:1005: "char" != "const char *"
  cpp_method_return_type_preserves_pointer_and_qualifier
      FAIL tests/test_extraction.c:1072: "char" != "const char *"
  351 passed, 2 failed
c_function_return_type_plain_unchanged is the control: it passes before
and after.

GREEN: extraction 353, c_lsp 762, lang_contract 41, pipeline 281.

Effect on real code -- Redis, production binaries, old against new.
39,132 nodes in both. 1,442 Function nodes change, in return_type only:
void -> void * (270), char -> const char * (101), unsigned char ->
unsigned char * (91), char -> char * (81), robj -> robj * (57), ...
Functions with a `*` in return_type: 0 -> 1,440. With one worker the
complete edge set apart from SEMANTICALLY_RELATED is byte-identical old
against new across six runs (three each) -- CALLS, USAGE, WRITES, IMPORTS
and the rest do not move. SEMANTICALLY_RELATED goes 350 -> 325 because
that pass tokenises the return-type string into its type vector.

One thing measured rather than assumed. With several workers, which
`dict.h` Redis' src/dict.c imports (src/ or deps/hiredis/) depends on
worker merge order. That is main's defect, not this change's: unmodified
main picked src/dict.h in 2 of 10 runs and the hiredis header in 8; with
one worker both binaries always pick src/dict.h. This change picked
src/dict.h in 6 of 10. At ten runs a side that gap is not separable from
chance (Fisher exact, two-sided p = 0.17), and a shifted rate would be
unsurprising for a timing-dependent pick; it is recorded here rather
than explained. The include-target fix is a separate change (#2227).

Known limits, all pre-existing and out of scope: struct and class FIELDS
have the same defect (`const char *data;` publishes char); C prototypes
and declaration-only C++ methods are not extracted as definitions, so
they are not reached; where a macro is misparsed as the type
(`LUA_API const char *f()`), the base was already wrong and now reads
`const LUA_API *`; the resolver's parser does not yet understand a
pointer-level qualifier or a leading _Atomic/restrict and falls back to
a named-type lookup for those, as it effectively did for every pointer
return before.

Co-authored-by: Andrew Hundt <ATHundt@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
…ure on Windows

Several processes first-starting together against a runtime directory
that does not exist yet -- test-windows-guards' section_cold_storm, or a
host that launches more than one MCP server on its very first run -- all
observe the final path component absent. One CreateDirectoryW wins; the
others get ERROR_ALREADY_EXISTS. win_private_directory_tree_secure()
treated that as a failure and recorded no validation detail, so every
loser exited with a bare

    secure CLI coordination could not be created (endpoint)

The function called right after the walk, win_runtime_directory_secure(),
has always tolerated ERROR_ALREADY_EXISTS for the same directory, and the
POSIX walk tolerates EEXIST at the same point; only the Windows walk in
front of them did not.

Seen four times on 2026-09-21 on unrelated PRs (#1768 three attempts in
a row, #2140, #1999, #808). The racy lines date from July; the guard
began exercising them on 2026-09-03, when each guard section was given
its own empty CBM_RUNTIME_DIR, so the final component is now absent at
storm time on every run.

The walk now treats ERROR_ALREADY_EXISTS from its own CreateDirectoryW as
"the directory I wanted exists". Nothing is trusted because of that: an
ancestor still goes through win_directory_component_secure(), and the
final component through win_runtime_directory_secure(), which refuses a
non-directory or reparse point and enforces owner and DACL.

Every refusal on this path now names its component and its rule. The
walk reports the Windows error when it can neither create nor inspect a
component, and win_runtime_directory_secure() says whether the path
could not be created, cannot be inspected, exists but is not a directory,
or is a reparse point. One helper owns the wide-to-UTF-8 conversion for
these messages and the existing ancestor message now uses it too, so
src/daemon/ipc.c stays at its memory-core baseline.

Deterministic reproduction, no threads and no timing: a test seam fires
in the walk between "component observed absent" and CreateDirectoryW,
and the test plays the process that wins the creation.

  daemon_ipc_windows_private_directory_survives_lost_creation_race
  daemon_ipc_windows_private_directory_refuses_and_names_a_planted_file

Verification. macOS arm64 (ASan+UBSan): build clean, daemon_ipc 53 passed
(2 Windows-only skips), daemon_bootstrap 28 passed, lint-memory-core
unchanged (ipc.c stays at 146 raw sites). The two new tests are Windows-only
and have NOT been run locally: the Windows VM was down when this was written,
so the RED run on the seam-only tree and the GREEN run with this fix are
delegated to CI's windows-latest legs (test-windows shards, test-windows-guards)
by an explicit maintainer decision. If the guard suite's cold storm still
fails with this in place, the attribution above is wrong and this reverts.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
CLI_USAGE described outer --json but did not mention tool-level
--format json. Add a help-only pointer that tools which accept format
support --format tree|json, and distinguish payload JSON from the full
MCP envelope. No new flags, env vars, or output-default changes.

Fixes #2102

Signed-off-by: Lowpower <3133734+Lowpower@users.noreply.github.com>
Signed-off-by: Lowpower <3133734+Lowpower@users.noreply.github.com>
feat(cli): point top-level cli --help at --format json
…nment

fix(hooks): clear repository git environment for tests
…izer-timeout-only

fix(tests): scale daemon runtime sanitizer timeout
…types

fix(extract): a C-family function's return type keeps its pointer and qualifiers
fix(daemon): losing the runtime-directory creation race is not a failure on Windows
…oundaries

fix(service-patterns): match library ids on identifier boundaries
@pull pull Bot locked and limited conversation to collaborators Sep 24, 2026
@pull pull Bot added the ⤵️ pull label Sep 24, 2026
@pull
pull Bot merged commit 5958f54 into MrDolphin:main Sep 24, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants