Vue single-file components yielded a Module node and IMPORTS edges but no
Function definitions or CALLS edges from the `<script>` block, so a component's
call graph was empty (#1410).
Adds a control test pinning current behaviour for Svelte, HTML and Astro, which
still extract imports but no functions or calls. That is deliberate scoping,
not an oversight: it documents the remaining gap rather than hiding it, and it
proves this fix does not leak into sibling languages. Svelte is tracked by
#1807 and will be a follow-up.
Re-opened from #1843, which carried no Signed-off-by and so failed DCO. The
tree is byte-identical to that commit; only the sign-off was added.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Layer 8 (scripts/security-vendored.sh) failed because the seven new arkts
files were untracked in the checksum baseline. Regenerated with --update
after reviewing the diff: the only additions are the arkts grammar's own
files, plus MANIFEST.md's hash changing because the preceding commit edited
it. Nothing else was re-blessed.
Verified independently rather than taken on trust:
arkts/_common_scanner.h sha256 == typescript/_common_scanner.h sha256
(da66ef2bd14a...), i.e. the external scanner body is the already-reviewed
typescript one, and arkts/scanner.c is a 21-line symbol-rename trampoline
that allocates nothing and delegates scan straight through.
All three security-static layers now pass locally: static allow-list, UI
audit, and vendored integrity -- the last also reporting no subprocess,
network, or dangerous calls anywhere in vendored grammar code.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The vendored LICENSE carried tree-sitter-typescript's MIT text plus an
appended derivative notice. That is not what MIT asks of a derivative -- it
asks that upstream's notice travel with the code -- and the appended text
meant the provenance audit could not byte-verify the file against upstream,
so arkts landed as NO-MANIFEST-ENTRY and failed license-gate.
The vendored LICENSE is now byte-identical to upstream's, and arkts is
registered in the audit's FORKS map, which is defined for exactly this case:
a self-maintained fork whose vendored LICENSE must match its upstream. That
is a routing entry, not an exemption -- check_upstream still byte-verifies
against the real upstream repository, so the fact is checked rather than
asserted. Audit now reports arkts as IDENTICAL.
Our copyright for the ArkTS additions is not lost, it moves to where our
SOURCE is: the grammar.js header (which now states all three copyrights
explicitly) and THIRD_PARTY.md, which ships inside the release archives.
The edit to scripts/audit-license-provenance.py is a maintainer-only
enforcement-surface change, made deliberately and stated here rather than
slipped in; a contribution may never supply its own audit entry.
No generated parser or source changes -- comments, licence text, manifest
prose and one routing entry only.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The fixture returned "/etc/myapp/conf.d", which is_filesystem_path() rejects
before is_rest_path() is consulted, so the value was never classified as a URL
and never recorded as a builder -- with or without the language gate. The test
passed identically with the gate removed, so it certified nothing.
"/srv/" is not one of the ten filesystem prefixes, so the value is URL-shaped
and only the JS/TS/TSX gate prevents it being recorded for C.
Verified by revert-check: with the gate removed the test now FAILS at
tests/test_extraction.c:3455; with the gate restored it passes.
Follow-up to #1010. The gate implementation is unchanged and is the author's.
Co-authored-by: Charles Queiroz <fcqueiroz@liquibase.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Routing .ets through the plain TypeScript grammar produced no definition
node for any @Component struct (0/19 across the OpenHarmony sample corpus),
parsed struct members as statement labels and build() as a call expression,
and registered ArkUI builtins (Column, Row, ListItem, ...) as user function
definitions with 28% fabricated cross-file CALLS edges. This adds a
dedicated ArkTS grammar that keeps everything TypeScript already handles
and models the ArkTS syntax TS cannot parse.
Grammar (tools/tree-sitter-arkts/, generated parser vendored to
internal/cbm/vendored/grammars/arkts/, ABI 15 via tree-sitter-cli 0.25.10):
first-party derivative of tree-sitter/tree-sitter-typescript v0.23.2
@ 75b3874edb2d (the commit our vendored typescript/tsx come from) on the
tree-sitter-javascript v0.23.1 base @ 3a837b6f3658 (the version upstream's
package-lock pins). LICENSE carries the upstream MIT text verbatim plus a
derivative notice; the external scanner is a symbol-rename trampoline over
a byte-identical copy of the already-vendored typescript scanner header.
ArkTS additions: decorated struct component declarations with class-shaped
bodies; UI-DSL trailing-closure component calls with attribute chains after
the closing brace; decorators on function declarations (@Builder,
@Extend(T), @Styles); @Extend/@Styles leading-dot attribute-chain bodies;
anonymous stateStyles style blocks; import lazy named imports.
Wiring: CBM_LANG_ARKTS enum + lang spec (struct_declaration as a class
kind labeled Struct, public_field_definition fields), .ets extension,
LANG_NAMES/userconfig entries, grammar shim, and TS-family joins across
the extractors and the name-resolution guards (parse_es_imports, JS
keywords/test-file/func-kind/scope tables, TS heritage/param/receiver/
namespace/type-ref/channel/usage paths, js_ts_family and the weak-method-
match suppression). Field definitions now also carry their decorators, so
@State/@Prop/@Link members are findable with their roles.
Tests: grammar corpus (35 cases), per-language regression + label golden +
import net + extension cases, seven ArkTS extraction tests including the
phantom-builtin regression (ArkUI builtins must never be definitions), and
capability-ledger / call-node-manifest / call-argument-matrix rows. On the
14-file OpenHarmony sample corpus the TS-grammar baseline had 12/14 files
with parse errors (231 ERROR nodes); the arkts grammar parses 14/14 clean,
and an end-to-end index yields all 19 components as Struct nodes, 68
methods, 119 fields, and zero phantom builtin definitions.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Add omp (Oh My Pi) as a STABLE, detected client surface with full
four-layer integration: MCP server registration, durable instructions,
skill, and three direct-MCP tiered subagent profiles (Scout/Verify/
Auditor). This matches Claude Code's integration depth — omp subagents
have direct MCP access (confirmed via ablation testing), unlike
parent-handoff-only clients (Pochi, Cursor, Rovo, Augment).
Schema (agent_clients.h):
New CBM_AGENT_CLIENT_OMP enum value, positioned before COUNT.
Profile (agent_clients.c):
- stability: CBM_AGENT_STABLE (auto-detected, auto-installed)
- capabilities: MCP | INSTRUCTIONS | SKILL | AGENT
No HOOK — omp's context event is fire-once advisory only; the
recommended upstream path is skill + agents + MCP, not TS hooks.
- detection_command: "omp" (specific enough per existing clients)
- marker: ~/.omp/agent (omp-managed config root)
- resolve_path: ~/.omp/agent/mcp.json
- json_client: true (uses standard mcpServers JSON edit path)
- agent_json_canonical: added to type:"stdio" branch alongside
GitLab Duo and Visual Studio (omp's schema expects this field)
Dialect (agent_profiles.h/.c):
New CBM_GRAPH_DIALECT_OMP, direct-capable (not in handoff-only list).
- tool prefix: "mcp__codebase_memory_mcp_" (single underscore,
hyphens→underscores — verified at runtime via omp device inventory)
- render: YAML frontmatter with tools (read/grep/glob + tier-specific
MCP tool allowlist), read-summarize: false (exact source verification
for evidence tiers — omp's read returns structural summaries by
default), autoloadSkills: [codebase-memory] (subagents start blank)
Install/uninstall (cli.c):
- install_omp_durable_context: writes AGENTS.md + skill +
3 tiered agent profiles to ~/.omp/agent/
- uninstall_omp_durable_context: removes managed block + skill +
tiered profiles
- legacy_omp_verify_agent_content: migration source for prior installs
- Wired into install_agent_client_registry and
uninstall_agent_client_registry loops
Design decisions (documented for reviewers):
1. No HOOK capability: omp's tool_call event can only block/pass (no
augmentation channel like Claude's additionalContext). A context-
event nudge was tested but is advisory-only and excluded from the
upstream path to keep the PR minimal.
2. read-summarize: false: omp's read tool returns structural summaries
by default; Tier 2/3 require exact snippets for material claims.
3. autoloadSkills: omp subagents start with no conversation history;
without autoload the skill decision matrix is never loaded.
4. Direct-capable (not handoff): ablation testing confirmed omp
subagents can call mcp__* tools directly. write→xd:// dispatch
does NOT work in subagents ("xd:// is not mounted in this session").
Test coverage:
- test_agent_clients.c: omp in stable_ids (19 entries), capabilities
array, json_schemas (type:stdio assertion), foreign_entries
- test_agent_profiles.c: omp in direct_dialects array, direct-capable
assertion, dedicated test verifying direct profile has
mcp__codebase_memory_mcp_ prefix + read-summarize + autoloadSkills,
and handoff profile excludes MCP tools
- test_cli.c: required_agents updated to 44, all 43→44 / 37→38 string
contracts updated across README/npm/index.html/llms.txt/main.c
Build: scripts/build.sh passes (exit 0).
Tests: scripts/test.sh passes (exit 0, all suites green).
Lint: scripts/lint.sh has one pre-existing error in pass_lsp_cross.h
(parameter name mismatch imp_keys/imp_vals vs imp_names/imp_qns),
unrelated to this change.
Signed-off-by: DavidHLP <lysf15520112973@163.com>
handle_url_builders ran for every vendored grammar, so a C helper
returning an absolute pathname became a URL builder and minted a Route
node plus an HTTP_CALLS edge in a language that speaks no HTTP. Gate
recording to JavaScript, TypeScript and TSX.
Builder entries now carry a flag in the per-file constant map: only a
call_expression argument resolves one, so handing a builder to a
callback builds no request. A builder whose returns are not all
route-shaped literals is declined instead of attributing its one literal
to call sites that take the computed branch.
Five negative tests cover the cross-language case, the mixed builder,
the ambiguity tombstone, the builder reference and the non-URL helper.
Signed-off-by: Charles Queiroz <fcqueiroz@liquibase.com>
Maintainer asked for in-tree coverage of {content, details} wrapping and
how tool errors are surfaced, plus a cheap abort-signal line. String-shape
only; no live Pi process.
Signed-off-by: Alex Musichen <alex.musichen@gmail.com>
detect_url_in_args() (the arg_url HTTP_CALLS emitter used when the callee
is a local client wrapper, not a known HTTP library) reads per-arg values,
not first_string_arg. Resolve call_expression args through the builder map
and flatten template_string args to the {} form there as well, so wrappers
like apiFetch(buildPath(id)) emit the edge and join the canonical route.
Signed-off-by: Charles Queiroz <fcqueiroz@liquibase.com>
URL-shaped literals returned from small builder functions never reached
call arguments, so client(buildPath(id)) produced no HTTP_CALLS edge and
no Route node, static or template alike.
handle_url_builders() records builderName -> returned URL in the same
per-file constant map used for module-level consts, covering return
statements and arrow expression bodies; a call_expression branch in
extract_url_or_topic_arg() resolves client(buildPath(id)) through it.
Composed builders inline already-recorded substitutions and truncate the
query string, so `${basePath(id)}?${params}` joins the server route
exactly. Ambiguous builders (two different URLs) are tombstoned.
Signed-off-by: Charles Queiroz <fcqueiroz@liquibase.com>
cli_install_plan_receipt_no_mutation_issue388 asserted inline, so any failure
returned before free(json) and test_rmdir_r(tmpdir). A red run therefore leaked
the receipt and left a stray /tmp/cli-plan-* directory behind -- the failure
report was accompanied by exactly the noise that makes the next debugging
session harder.
Record what went wrong, release everything, then fail. The message now names
the specific missing marker rather than reporting that some marker was absent,
which is the difference between reading a failure and bisecting one.
The env-isolation half of the original PR is not carried over: main's
tf_setup_cache_sentinel already unsets CODEX_HOME along with every other client
home override, so a per-test save/unset/restore would be redundant now.
Distilled from #1149 by Anand Aiyer, which predates that sentinel and could not
land as written once its companion PR closed unmerged.
Co-authored-by: Anand Aiyer <anand@aiyer.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
scripts/test.sh labels its pre-build contracts as a running 0a..0u sequence.
The new blob-generator contract was slotted in as "0f2" to sit next to the
other build-tooling contract, which breaks the one convention the list has.
Append it as 0v instead; these steps all run before the build, so position
carries no meaning beyond the label.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
vendored/nomic/code_vectors_blob.S is a GENERATED file that was hand-edited.
1f674c80 added the ELF .note.GNU-stack section to it after finding that every
Linux binary we had ever shipped requested an executable stack: an object
carrying no such note tells GNU ld nothing about its stack requirement, and ld
then assumes the worst for the entire link. That fix went into the artifact
only. scripts/extract_nomic_vectors.py still emitted the Mach-O branch alone,
so the next regeneration would have overwritten the tracked .S, dropped both
the ELF and COFF branches, and put GNU_STACK RWE back into every release --
silently, because nothing compared the generator against its own output.
write_blob_s() now emits the tracked wrapper verbatim, and the new contract
test fails if the two ever drift again. The template is compared rather than
executed so the test does not need the torch/transformers import the extraction
script pulls in. The write is pinned to UTF-8 because the WHY comment carries
an em dash and the default encoding is locale-dependent.
check-binary-composition.sh already fails a release whose binary has an
executable stack; this closes the same hole at edit time, where the diff is
still small enough to read.
Reported and originally fixed by Anand Aiyer in #1151, which also carried the
COFF branch. That PR sat unreviewed for a month while the .S half was
rediscovered and landed independently without credit.
Co-authored-by: Anand Aiyer <anand@aiyer.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Java enum methods already resolve on main: find_class_member_body
descends into enum_body_declarations (Java-gated) while find_class_body
keeps returning enum_body, which is what extract_enum_members needs to
reach the constants — they are siblings of enum_body_declarations, not
children.
That distinction was unguarded. java_enum_dedup_preserves_calls_issue1234
asserted the methods and the absence of duplicate Function defs, but
never asserted the constants, so collapsing the two lookups would have
passed the suite while silently dropping every enum constant from the
graph.
PR #984 proposed exactly that collapse — redirecting the shared
find_class_body — which is what surfaced the gap. Verified binding:
pointing extract_enum_members at find_class_member_body reddens the new
Variable assertions and nothing else.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Co-authored-by: sahil-mangla <manglasahil2017@gmail.com>
A busy startup transition always resolves, by one of two routes: the live
peer holding it finishes its command, or the holder is already dead and
the operating system has not finished reclaiming the lock. Waiting was
capped at 10s regardless, so a clock decided a user-visible outcome and
commands were refused with "CLI startup coordination remained busy" while
nothing was actually wrong.
The second route is Windows-specific and is what makes this reachable in
practice. On POSIX the kernel drops flock the instant an owner dies. Windows
byte-range locks do not: Microsoft documents that after a process terminates
holding one, "the time it takes for the operating system to unlock these
locks depends upon available system resources", and that until then "access
to these files may be denied". A loaded CI runner is exactly where those
resources are scarce, and tests/windows/test_daemon_stability.py creates the
condition deliberately by hard-killing daemons with `taskkill /F`, including
a crash-recovery section -- so the next client meets a lock whose owner no
longer exists and is refused for a reason that no longer applies.
The cap becomes a backstop against a peer that never finishes rather than a
budget for healthy contention, and the message it prints now names both
explanations instead of just "busy", which sent reporters looking for a CBM
session that had already exited.
Clean exits already release via main_local_transition_close, so no new
release path is needed; this only affects what happens after an abrupt
termination or under genuine concurrency.
VERIFICATION LIMIT, stated plainly: this cannot be reproduced or verified on
macOS -- the mechanism requires Windows lock-reclaim semantics, and a local
12-client storm passes even under the old cap. 458 daemon/ipc/runtime/cli/
watcher tests pass and lint is clean, but whether this removes the CI flake
can only be established on Windows CI.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Three related defects in how the watcher decides what counts as a change.
1. `git rev-parse --git-dir` walks UP the tree, so an ordinary folder that
merely sits under an unrelated repository answered yes. The watcher then
inherited that ancestor's dirty state -- permanently non-empty and about
entirely different files -- and reindexed on every poll forever. A
directory is now git-managed only if it carries its own .git (directory
or gitlink file) or the ancestor actually tracks something inside it, so
a real monorepo sub-package still qualifies while a scratch or ignored
folder does not.
2. `git status` reports the whole repository regardless of -C, so every
package in a monorepo reindexed whenever any sibling was edited. The
status call is now scoped with a `-- .` pathspec.
3. Porcelain paths are REPOSITORY-relative but were stat'ed against
root_path. For a project watched at the repository root the two coincide
and the bug is invisible; for a subdirectory project every stat missed,
silently degrading the dirty signature to text-only and losing the
size/mtime component that makes an edit to an already-dirty file
detectable. Paths now resolve through `rev-parse --show-cdup`.
--show-cdup rather than --show-toplevel because MSYS/Cygwin git returns a
translated absolute path from the latter that will not join onto the
native path we hold; a relative hop composes on every platform.
Distilled from #1001 by bethzyy, whose diagnosis identified all three.
Main independently fixed that PR's two headline problems (the unbounded
non-git file count, and per-poll churn via a dirty-state signature) while
it waited, and the watcher was rewritten onto a supervised argv-spawn
harness in between, so this is a reimplementation against the new code
rather than a rebase of theirs.
Both new tests are verified binding: reverting the classification guard
reddens the nested-directory test, and removing the pathspec reddens the
monorepo one, each without disturbing the other.
Addresses the remaining parts of #713, #841 and #937.
Co-authored-by: bethzyy <bethzyy@users.noreply.github.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
pkg/npm/README.md claimed 159 languages and 14 MCP tools while the root
README says 158 languages and 15 MCP tools. The tool count is verifiably
wrong: the TOOLS[] table in src/mcp/mcp.c has 15 entries. The language
count is aligned to the root README, which states 158 consistently in
five places.
Each stale figure appeared twice; both occurrences of each are fixed, so
the package README no longer contradicts itself or the root README.
Distilled from two PRs that each caught one half: #967 by ketpatil77
(language count) and #1268 by nhtkylc91 (tool count). Neither fixed all
four occurrences, and both touched the same file, so they are combined
here with credit to both.
Note for whoever refreshes these next: 159 grammar directories exist
under internal/cbm/vendored/grammars/, which does not reconcile trivially
with either figure (some languages share a grammar; some enums have no
vendored grammar). This change deliberately does not re-derive the
number, only stops the two READMEs disagreeing.
Co-authored-by: ketpatil77 <ket.patil77@gmail.com>
Co-authored-by: nhtkylc91 <nhtkylc@hotmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
A Swift protocol requirement — a bodyless `func generate() -> String`
inside a protocol — was absent from the graph entirely. Swift codebases
are heavily protocol-driven, so the requirement is very often the
declaration a reader is looking for, and "who declares generate()" had no
answer.
Three edits deliver it: protocol_body joins the class-body types so a
protocol's members are walked at all; protocol_function_declaration joins
swift_func_types, because extract_class_methods gates on that set and
would otherwise walk the requirement and discard it; and both name
resolvers accept the node, which has the same simple_identifier shape as
function_declaration.
Distilled from #613 by xbsjason. That PR also carried an enum/struct half
whose two assertions fail on CI: tree-sitter-swift has no
struct_declaration or enum_declaration node type (it models both as
class_declaration), so the corresponding swift_class_types entries are
inert and a bare enum is still labeled Class. That is a real pre-existing
modelling gap, left untouched here and now documented at the dead entries
rather than silently deleted.
Also dropped from the original: a find_first_descendant_by_kind rescue in
extract_class_methods. Removing it changes no test outcome once the
function-type entry is present, so it was carrying nothing.
Advances #43.
Co-authored-by: xbsjason <xbsjason@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
`nix develop .#cross` on macOS exposes the OTHER darwin architecture's
zlib, so `scripts/build.sh --arch <target>` can link an x86_64 binary on
Apple Silicon and vice versa. The native clang already cross-emits object
code via -arch; zlib is the product's single link-time dependency, and the
host-arch copy cannot satisfy a cross link, which is the whole gap.
Darwin-gated and opt-in: the attribute set is empty on Linux, the default
shell is untouched, flake.lock is unchanged and no new flake input is
introduced. The shell deliberately does not use inputsFrom, since that
would put the host-arch zlib back on the link path.
Distilled from #724 by Kris Williams. That version also wired libgit2 via
pkg-config, which is now dead weight: libgit2 was removed project-wide for
GPL-licensing reasons and appears nowhere in the tree, so both it and
pkg-config are dropped here — leaving zlib as the only library the cross
shell needs to place.
Validated by parsing the flake with nix-instantiate in a nixos/nix
container (no Nix toolchain on the maintainer host), which also confirms
the shellHook's ''${...} escaping resolves to literal shell expansions.
Evaluating or entering the shell needs a macOS host with Nix; no CI leg
exercises it, as noted on the original PR.
Addresses #705.
Co-authored-by: Kris Williams <115474+kriswill@users.noreply.github.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>