Commit Graph
247 Commits
Author SHA1 Message Date
Martin Vogel 587f134e5c Merge pull request #2385 from HazielMagallanes/fix/opencode-v2-setup-hooks
fix(cli): register OpenCode 2 hooks via the setup context
2026-11-01 14:27:30 +01:00
Martin Vogel fb88365dc0 fix(cli): add no OpenCode 2 compaction note until one has run on a real host
The V2 compaction hook pushed a plain object into the compaction request's
event.messages. OpenCode's Message is a schema class at v2.2.5, and nobody has
run this path on a real 2.x host. The try/catch only guards the array push,
which cannot throw. If OpenCode rejects the object, that happens inside its own
compaction, out of the plugin's reach, so an untested note could break
compaction for every OpenCode 2 user. Losing the note costs far less.

Remove the V2 compaction registration and the sessionHooksRegistered flag it
needed. The V2 tool hook, proven on OpenCode 2.2.5, stays, and so does the
1.18.x experimental.session.compacting path. OpenCode 2 sessions still get the
session note on the first tool result of a session. A later change can add a
note after compaction together with a real 2.x run.

The replacement test pins that the generated module registers no session hook
and touches neither event.messages, a system property nor an instruction entry.

Refs #2204

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-11-01 12:55:46 +01:00
Martin Vogel 01ac4fe6a9 Merge origin/main into feat/codex-skill-location-compat
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-11-01 05:25:28 +01:00
Martin Vogel 12920dae15 Merge origin/main into feat/codex-skill-location-compat
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-11-01 03:03:02 +01:00
Martin Vogel b3c6becee7 Merge origin/main into codex/fix-2816-trace-recursion
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-11-01 02:08:20 +01:00
Martin Vogel 6d33c27ae7 Merge origin/main into codex/fix-2816-trace-recursion
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-31 16:40:26 +01:00
Martin Vogel 8cef96dd88 Merge origin/main into fix/adr-reindex-race
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-31 16:38:41 +01:00
Martin Vogel ecd2eab93d Merge origin/main into feat/codex-skill-location-compat
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-31 16:38:29 +01:00
Martin Vogel dd96a98494 Merge origin/main into codex/fix-2816-trace-recursion
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-31 01:00:09 +01:00
Martin Vogel b33a645e47 Merge origin/main into fix/permanent-daemon-watch
test_daemon_application.c: the new auto-watch test passes false for the approve_broad parameter that #2017 added to cbm_workspace_grant_add, the same value the existing callers use; the merged tree did not compile without it.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-30 22:37:40 +01:00
Martin Vogel 9cbc350720 Merge origin/main into feat/codex-skill-location-compat
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-30 22:30:12 +01:00
Martin Vogel 359bd2f3ff Merge origin/main into fix/adr-reindex-race
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-30 22:29:41 +01:00
Pcristin f8dfaca12a Merge branch 'main' into codex/fix-2816-trace-recursion 2026-10-29 22:25:35 +00:00
pcristin ab8f68a169 fix(mcp): expose recursion metadata in trace_path
Report stored recursion signals and the static self-call omission policy
alongside graph reachability totals. Preserve the metadata across cursor
pages and output-budget fallback responses, and document unknown legacy
metadata without changing CALLS indexing or traversal counts.

Fixes #2816

Signed-off-by: pcristin <xxxokzxxx@protonmail.com>
2026-10-29 22:21:48 +00:00
Martin Vogel 7abfb0d847 Merge branch 'main' into feat/claude-code-plugin-marketplace 2026-10-29 07:07:36 +01:00
Martin Vogel ed17097e34 Merge origin/main into fix/permanent-daemon-watch
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-29 04:07:49 +01:00
Martin Vogel 9f707646a8 Merge origin/main into fix/adr-reindex-race
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-29 03:29:37 +01:00
Martin Vogel 848975f635 Merge origin/main into feat/codex-skill-location-compat
README agent table: keep main's Claude Code row (drift note) and this
branch's Codex CLI row (skill path + PreToolUse).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-28 18:29:56 +01:00
Martin Vogel 8fae39e294 Merge origin/main into fix/adr-reindex-race
tests/test_pipeline.c: main and this branch added tests at the same spot;
both are kept unchanged.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-28 18:29:56 +01:00
Martin Vogel 97f8aef644 Merge branch 'main' into feat/claude-code-plugin-marketplace 2026-10-28 18:16:38 +01:00
Martin Vogel 545c1bd519 Merge origin/main into fix/permanent-daemon-watch
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-28 18:15:03 +01:00
Martin Vogel 5d97172b89 Merge origin/main into docs/windows-troubleshooting
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-28 18:12:53 +01:00
Martin Vogel 70d6d5017c docs(watcher): describe the watch lifetime under daemon start
A daemon started with `daemon start` now keeps a project watched after its
last owning MCP session closes, until the daemon stops, the project is
deleted, its directory disappears, or auto_watch is set to false. README
(the watcher paragraph and the index_repository row), the watcher_enabled
row of docs/CONFIGURATION.md and the comment above the index_status watch
provider still said every watch ends with its last session.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-28 00:51:48 +01:00
Martin Vogel 994432f068 Merge origin/main into feat/codex-skill-location-compat
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-27 20:05:45 +01:00
Martin Vogel 57d641bfeb Merge branch 'main' into feat/claude-code-plugin-marketplace 2026-10-27 19:18:22 +01:00
Martin Vogel 04cb937236 Merge origin/main into fix/adr-reindex-race
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-27 19:17:15 +01:00
Martin Vogel 402617555c feat(cli): note when a Claude Code session keeps searching without the graph
The search hook gives each Grep, Glob or Bash search the matching graph
symbols, but a session can keep searching text for questions the graph answers
in one call (#509). Once a session has run three searches with no
codebase-memory-mcp tool call, the next augmentation now adds one factual line
saying so. The next note comes after six searches, then twelve, and any graph
tool call resets the count.

- State: a per-session counter in the process that runs hook requests (the
  daemon), keyed by the payload's session_id, in a bounded 64-slot table that
  evicts the least recently used session, behind a spin lock. Only searches
  that reach the graph lookup count. A search without an augmentation still
  counts, and the note waits for the next augmentation.
- Reset: Claude's managed PostToolUse entry now matches
  "Read|mcp__.*codebase-memory-mcp__.*", covering plain and plugin server
  names. The previous Read-only entry, as installs up to v0.12 wrote it, is
  recognized as owned, so install replaces it in place and uninstall removes
  either.
- Scope: Claude Code only. Codex sends the same event payload with a turn_id
  but has no reset hook, so its sessions are not tracked, and payloads without
  a session_id are not tracked either.
- Cost: a graph tool call now runs one hook round-trip to the daemon for the
  reset, about 130 ms on a local build.

Refs #509

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-27 19:04:47 +01:00
Martin Vogel 762a6e47d8 Merge origin/main into feat/codex-skill-location-compat
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-26 02:20:26 +01:00
Martin Vogel 8cfefa6d38 Merge origin/main into fix/adr-reindex-race
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-25 20:14:53 +01:00
Martin Vogel d0937940a9 docs(readme): name the Codex PreToolUse and Copilot PostToolUse search hooks
The Codex CLI row still listed only SessionStart and SubagentStart after the
PreToolUse Bash hook landed (#2719), and the hook section described only
Claude Code's search context. The row now names the Codex search hook, and the
section says how Codex, Copilot CLI and VS Code's Copilot runtime get search
context.

Refs #509

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-25 20:03:40 +01:00
Martin Vogel a18b4a4574 feat(cli): give Copilot searches graph context through a PostToolUse hook
Copilot CLI's preToolUse output can only decide permissions, and its command
hooks fail closed on errors, so the graph context that Claude Code and Codex
searches get could not ride a pre-tool hook there. postToolUse
additionalContext is appended to the tool result the model sees on the same
turn (github/docs content/copilot/reference/hooks-reference.md). VS Code's
agent mode shares the Copilot harness and reads the same ~/.copilot/hooks
manifest.

- Manifest: $COPILOT_HOME/hooks/codebase-memory-mcp.json adds a PascalCase
  PostToolUse entry, matcher "Bash|Grep|Glob|bash|powershell|grep|rg|glob",
  running hook-augment --event PostToolUse --dialect copilot. The previous
  lifecycle-only manifest, as installs up to v0.12 wrote it, stays recognized
  as owned: install upgrades it in place and uninstall removes either
  generation. A foreign manifest still fails closed.
- hook-augment: the Copilot dialect reads either payload form
  (tool_name/tool_input or toolName/toolArgs), maps runtime tool names to the
  Claude names (bash/powershell to Bash, grep/rg to Grep, glob to Glob),
  parses arguments passed as a JSON string, and answers with top-level
  additionalContext.
- Fast path: Copilot's forced PostToolUse goes through the no-op gate before
  executable-identity hashing. The gate also skips tools the augmenter does
  not read, because VS Code's local hook parser ignores matchers, so a
  non-search shell call or an edit exits in about 20 ms instead of paying the
  full round-trip of about 130 ms.
- The context header no longer says the search results are "below": after a
  post-tool hook they are above it.
- Tests: the three Copilot foreign-manifest tests now create
  .copilot/mcp-config.json. Without it Copilot CLI was never detected, so
  install and uninstall never reached the manifest, and the tests also passed
  with the ownership checks removed.

Refs #509

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-25 19:50:35 +01:00
0xb007ab1eandClaude Opus 4.8 001fbe269a fix(adr): store ADRs in a per-project sidecar DB, untouched by reindex
Reworks the ADR-loss-across-reindex fix to Alternative 1 (per the PR review):
remove the race rather than narrow it. ADRs live in a per-project sidecar SQLite
DB ("<graph_db>.adr.db"), separate from the graph DB a reindex rebuilds or
deletes, so there is no capture, re-read, re-apply, or lock — and no sleeps,
polling, or timeout deciding an outcome.

store.c:
- ADR writes (store/delete/update_sections) use a cached read-write handle opened
  SQLITE_OPEN_READWRITE|CREATE and configured like the graph store (PRAGMA
  busy_timeout=10000 + journal_mode=WAL) so cross-process access waits rather
  than failing SQLITE_BUSY. A :memory: store keeps ADRs in the graph DB.
- Reads never write: cbm_store_adr_get uses the cached RW handle if open, else
  opens the sidecar SQLITE_OPEN_READONLY (with the same busy timeout) when the
  file exists, else reads the legacy graph row without creating anything. When
  the sidecar exists but is NOT marked migrated (half-created), it falls back to
  the legacy graph row so a partial sidecar never hides an ADR.
- One-time migration is atomic: adr_copy_legacy_once wraps the marker check,
  legacy copy, and marker insert in a single BEGIN IMMEDIATE...COMMIT
  (re-checking the marker under the lock; ROLLBACK->ERR on failure). New public
  cbm_store_adr_migrate_once. Sidecar path via cbm_path_for_file_api (Windows
  long-path). Dropped the write-only adr_db_path field.

pipeline.c: migrate a legacy ADR into the sidecar BEFORE the format-change and
forced-full routes unlink the old generation; preserve-and-abort on failure, and
do NOT fail open — a query-open returning NULL preserves unless the final DB
genuinely does not exist. Migration targets the FINAL path (staging suffix
stripped). The early-capture/re-apply machinery and its test hook are gone.

artifact.c: the exported artifact carries the ADR — snapshot_inject_adr always
DELETEs project_summaries first, then injects only when an ADR is present, so a
deleted ADR never resurrects through a lingering legacy row. Import restores the
ADR into the destination sidecar via cbm_store_adr_restore_from_graph_if_absent:
it copies the artifact's ADR (INSERT OR IGNORE — never clobbers a local one)
regardless of the migrated marker, so a teammate who already indexed the project
(marked sidecar, no ADR) still receives the shared decision record. store.c
factors the row copy into adr_copy_rows, shared by the one-time legacy migration
and the ungated restore.

mcp.c: delete_project removes "<db>.adr.db" (+ -wal/-shm/-journal) only after the
graph unlink succeeds; is_project_db_file skips *.adr.db. cli.c: exclude
*.adr.db from index listing/counting. README + memory-core baselines updated.

Tests (file-based): ADR written mid-reindex survives on BOTH the full and delta
routes (route-pinned FORCED_FULL / CLOSURE_REPAIR); a legacy row migrates before
a forced-full/format reindex with no prior read; migration failure (a directory
at <db>.adr.db) preserves-and-aborts with the old generation intact;
delete_project via the MCP handler removes the sidecar + journals; the artifact
export->import round trip keeps the ADR; a deleted ADR does not resurrect on
import; an import does not clobber a local ADR; an import into an already-indexed
destination (marked sidecar, no ADR) restores the artifact's ADR; a half-created
sidecar falls back to the legacy row. The import tests remove the destination
graph DB (keeping the sidecar + marker) before importing, matching production
(try_artifact_bootstrap imports only when the graph DB is missing) so the rename
succeeds on Windows, not only on POSIX.

Closes #516

Signed-off-by: 0xb007ab1e <134006168+0xb007ab1e@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-22 04:45:43 -05:00
wargloom 0325d6a3fa fix: watch explicit MCP index targets
Signed-off-by: wargloom <wargloom@gmail.com>
2026-10-21 14:03:07 +03:00
Martin VogelandKody 4c09f7c70c feat(plugin): Claude Code plugin marketplace, discovery slice
Make this repository a Claude Code plugin marketplace so users can find
and add the server from inside Claude Code:

  claude plugin marketplace add DeusData/codebase-memory-mcp
  claude plugin install codebase-memory@codebase-memory

This is slice 1 of #1188: discovery only. The plugin registers the MCP
server and nothing else; the skill, the graph agents and the context
hooks still come from `codebase-memory-mcp install`.

- .claude-plugin/marketplace.json: one plugin, sourced from ./plugin.
- plugin/.claude-plugin/plugin.json: version pinned to the current
  release. Without a pinned version every commit to main would be a new
  plugin version for installed users.
- plugin/.mcp.json: stdio server `npx -y codebase-memory-mcp@0.12.1`.
  The pin keeps main from advertising a server version npm does not
  have; the npm package downloads the release binary on first start and
  verifies it against the release checksums.
- .gitignore: the root .mcp.json stays ignored; plugin/.mcp.json is
  published.
- README: install section, and "use one or the other": with both the
  plugin and `install`, Claude Code runs the server twice under two tool
  namespaces.
- tests/test_version_metadata_contract.sh: both plugin files are
  release-tracking surfaces; a new claim form reads the npm package spec
  (`codebase-memory-mcp@X.Y.Z`), and the unregistered-surface sweep now
  covers plugin/ and .claude-plugin/. The post-release sync that bumps
  server.json must bump these pins too, or this contract goes red.

No language count is stated anywhere in the plugin metadata, so the
language-count contract has nothing new to track.

Refs #1188

Co-authored-by: Kody <kaidi.shi.1121@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-21 00:34:29 +02:00
Martin Vogel 13c38feeff docs: refresh Lens recordings and correct README claims
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-20 22:39:10 +02:00
Martin Vogel e81b6599a8 docs: center the MCP server while keeping Lens visuals up front
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-20 20:02:47 +02:00
Martin Vogel 912dae61c9 docs: add Lens walkthroughs and restore GitHub star badge
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-20 19:57:57 +02:00
Martin Vogel e77b494b3e docs: improve site discovery and invite the Discord community
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-20 19:53:16 +02:00
Martin Vogel dd357a7c97 docs: refresh project messaging and Lens website
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-20 19:08:27 +02:00
Oscar Madera d1ab2426ee Merge branch 'main' into docs/windows-troubleshooting 2026-10-20 08:40:03 -05:00
Martin Vogel cad8169697 Merge branch 'main' into feat/codex-skill-location-compat 2026-10-20 05:34:50 +02:00
Oscarbol09 a933d5318c docs(troubleshooting): add Windows troubleshooting guide
Centralize recurring Windows-specific troubleshooting entries in a dedicated document linked from README.md. Each entry includes confirmed symptoms, affected version ranges, current workarounds, and canonical tracking issue references, updated to reflect v0.10.x and v0.11.0 status.

Refs #1170

Signed-off-by: Oscarbol09 <omaderabolano@correo.unicordoba.edu.co>
2026-10-19 18:45:28 -05:00
Martin Vogel 5cdbe9b0b6 fix(deploy): taint Helm changes of and/or, later conditions, sortAlpha, parse errors
Review round 4 of #2677 (user decisions: R4-1..R4-4, R4-6, R4-7, R4-9;
R4-5 measured and left out). Every case ends in an UNKNOWN image, never a
wrong one.

Changes not run exactly (helm_template.c):
- A deep merge into a values map is not exact when a change was made at or
  below its path (a set storing a map there): every values map a set or a
  merge changes keeps its path, and such a merge leaves the images after it
  UNKNOWN.
- and / or at an operand whose truth is not known -- UNKNOWN, or a map a
  change made unknown -- leave the images after them UNKNOWN when an
  operand after it may change maps.
- In an if / with an UNKNOWN condition skips, the else-if / else-with
  conditions after it are looked at too: a change there taints, an
  assignment there makes its variable UNKNOWN.
- sortAlpha sorts a []string in place: it counts as a change (a list is
  never poisoned, so it taints). No other Sprig list function changes its
  argument in place.
- A template, a define body or a scanned template file the grammar does not
  parse may hide a change: its images are UNKNOWN.

Work (helm_template.c):
- A capture's keys leave the key set one by one; the set is no longer
  cleared by walking its whole capacity after each capture.
- Template files are found by a hash of their path, and an include of a
  name that is no define is answered once per render.

Known limit, documented (README): each template file renders on its own,
while Helm renders a chart's files with one shared .Values. Running them in
Helm's order is the planned follow-up; the coarse rule across files cost 83
more correct images on the 20 infra corpora and was left out.

On the 20 infra corpora: Image edges identical to round 4 (0 lost, 0
added), no budget trip.

Tests: R4-1..R4-4 and R4-9 fail on 3401def5 by their assertions (each sub
case too); R4-6 and R4-7 are correctness controls, their cost measured
outside the suite.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 23:42:36 +02:00
Martin Vogel 5966b7f081 Break-glass merge PR #2664 2026-10-16 18:16:17 +02:00
Martin Vogel 10c7fc3206 fix(pkg): remove the archive size caps from the npm, PyPI and Go wrappers
v0.12.0 could not be installed through npm, pnpm, pip or the Go wrapper on
any platform (#2657): each wrapper refused an archive member above 256 MiB,
and every v0.12.0 binary is 331-335 MB. The v0.11.0 binaries were already
about 300 MB, so the Go wrapper, which has had the cap since v0.10.0, was
broken before; npm and PyPI gained it in v0.12.0. The archive is verified
against the release's checksums.txt before it is unpacked, so a byte cap
only ever refused what we ship.

- npm, PyPI, Go: delete every byte-size cap (compressed archive, per member,
  aggregate, checksum manifest, download size). The checksum, the exact
  member set, path and link refusal, the member count, HTTPS and the
  --version check stay.
- All three honour CBM_DOWNLOAD_URL like install.sh, for testing: https, or
  plain http to an exact loopback authority with no proxy and no redirect.
- Smoke: scripts/smoke-wrappers.sh installs the three wrappers from the
  checkout against the release fixture and runs the binary each one
  installed. smoke-test.sh Phase 13w runs it; SMOKE_REQUIRE_WRAPPERS=1 makes
  it required on the PR smoke lanes and in the release smoke, which gates
  publishing.

Other install paths found broken while checking every channel:
- Homebrew: the formula was still at 0.10.3 and installed the dynamic Linux
  build, which needs glibc 2.38 (Ubuntu 22.04, Debian 12 and RHEL 9 lack it).
  It now installs 0.12.0 with the static -portable Linux assets and tracks
  the newest release in the version contract like the other surfaces.
- Go: `go install .../pkg/go/cmd/codebase-memory-mcp@vX.Y.Z` never worked
  (no pkg/go/v* tags, so Go fetched the whole repository, over its 500 MB
  module limit), and @latest built main's wrapper, whose version constant
  lags each release. The release now also tags pkg/go/vX.Y.Z, and the
  wrapper installs its own module version (the constant only for local and
  untagged builds).
- npm: a stable release also moves the `next` dist-tag to itself; `next`
  still pointed to 0.9.1-rc.1.
- Docs: winget and Chocolatey were listed as install channels but are not
  published; they are removed until they are. Windows arm64 ships and is
  now listed.

Fixes #2657.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-16 18:09:06 +02:00
Martin Vogel c040476634 docs(readme): point the main CI badge at the release workflow
The badge tracked dry-run.yml, which runs only when dispatched; its last
runs on main were cancelled or failed during the v0.12.0 preparation, so it
read "failing" although main passed the full matrix in the v0.12.0 release
run. release.yml runs the same full matrix on main for every release.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-16 01:47:23 +02:00
Martin Vogel eca1a004b4 feat(configlink): TOML and INI keys link by exact name in their component
A config key linked to every code symbol whose name contained it. In
YAML, JSON and HCL files that guess was retired in WP0.9; TOML and INI
files kept it, and in 17 infrastructure repositories every one of their
72 key_symbol edges was a coincidence: `.gitleaks.toml useDefault` linked
to TestConfig_UseDefault and three other symbols, a toml-merge tool's test
data `config_path` to 46 `*ConfigPath` constants all over deckhouse,
`grafana.ini admin_user` to user-cluster constants, a Rust Cargo.toml
dependency to other services' go.mod requirements.

A .toml, .ini, .cfg or .conf key now links like an infrastructure key:
by exact name (camelCase and snake_case still normalize to one key) and
only to code inside the config file's component, its directory or the
one around a config/, conf/, settings/ or etc/ directory. .env and
.properties keys keep substring matching; the tests that covered it on a
TOML key now use a .properties key.

Measured on the 17 repositories: key_symbol 95 -> 23, the 23 being the
infrastructure edges WP0.9 reviewed, the 72 removed all TOML/INI and all
coincidences, no other edge or node changed; deterministic over three
runs; the incremental re-index equals a full index for INI, TOML, code
and tfvars edits.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-14 22:09:37 +02:00
Martin Vogel dec56c9c69 feat(infra): an external chart is keyed by the URL that publishes it
An external Chart was <project>.__helm_dep__.<name>: keyed by its name,
inside one project. Two repositories naming the same published chart held
two unrelated nodes, and a project naming two charts of one name from two
publishers (kubermatic's cert-manager from charts.jetstack.io and from its
own mirror) would have merged them into one.

An external Chart whose repository URL is stated is now
__chart__<repository>/<name>, the same QN in every project, the URL in a
`repository` property: scheme and host lower-cased, a trailing slash and
`.git` dropped. A Chart.yaml dependency, a kustomize helmCharts entry, a
Flux HelmRelease chartRef and a Terraform helm_release of an oci:// chart
that name one published chart meet in one node. A chart reference (a Flux
OCIRepository url, `chart = "oci://..."`) is cut to its repository; a
repository URL never is, so repository oci://public.ecr.aws/karpenter
with chart karpenter stays oci://public.ecr.aws/karpenter/karpenter. A
repository that is no URL (an @alias, no scheme, file://, credentials in
the authority, an unexpanded ${...}) or that the pass does not read (a
Flux HelmRepository source, a Terraform `repository =`, a shell
`repo/name`) keeps the per-project node: no identity is claimed for what
was not stated. A query or fragment (`?token=...`) takes the per-project
node too, so a token never becomes part of a QN or a property.

One predicate now tells an external chart from a chart of the repository
where Terraform, shell and the Go installer index the chart directories:
an external chart a Chart.yaml depends on carries that file's path, and
must never stand in for the chart the directory declares.

Measured on 17 infrastructure repositories against an independent
reading: 141 of 141 edges into external charts reach the identity their
stated repository gives (114 by URL, 27 per project). The only
differences to the previous index are the 79 external Chart nodes that
moved to their URL identity, each with its repository property, and the
114 edges into them, re-pointed with type, source and properties
unchanged. Every chart gate (Helm includes, helmCharts, Terraform, the Go
installer, shell, CI, GitOps deploys) reads the same as before; three
runs are identical; the incremental re-index equals a full index for a
shared chart's owning file edited and deleted, a URL respelled, a
dependency moved to another publisher and to an alias, a chartRef URL
edit and a new Terraform oci:// release. The index rebuilds once on
upgrade with the other version 9 changes.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-14 21:06:48 +02:00
Martin Vogel 150851cc05 feat(infra): an Ansible kube / k8s task DEPLOYS the template it applies
kubespray applies its cluster add-ons with its own `kube` module: a
task runs kubectl apply on a manifest the play rendered from a template
a few tasks earlier. Both tasks were Functions and the template a File,
with nothing between them: no query reached the manifest a task applies.

A kube (kubespray's library module) or k8s (kubernetes.core /
community.kubernetes) task now DEPLOYS {via: ansible, module, line} the
repository file its play rendered the applied manifest from: a template
task's src (the role's templates/, else the task file's directory) or a
copy task's (its files/; never a remote_src copy, which reads the host).
The applied filename is traced through the play: a renderer whose dest
is the very string; {{ item.item.K }} over "{{ R.results }}" -> each
item of the renderer registering R; {{ item.K }} -> each of the task's
own items through the renderer of that dest; a literal filename a looped
renderer renders to; a k8s template: argument. A loop list is a literal,
a set_fact of the file, or a list of the role's defaults / vars main
file. Templates are found before copies to the same dest. remote_src is
read the way Ansible's boolean() reads it (y / yes / on / 1 / true / t,
any case): `remote_src: true` is a YAML boolean, not a string, and a
string-only read had kept every remote copy as a renderer.

The lists in a role's defaults / vars reach past the task file, so a
change to one takes the full rebuild (closure_ansible_decline); without
it, a list that names another template kept the edge to the old one. A
file holding Ansible tasks that the repair would re-walk as a dependent
-- a template it applies was deleted -- also takes the full rebuild
(closure_ansible_dependent_decline): the repair walks it without its
role's defaults, handlers and other task files, and drew none of its
list-fed edges.

Measured on 17 infrastructure repositories: 164 of 164 applied files
(52 of kubespray's 62 kube / k8s tasks, 158 templates) against an
independent reading, all in kubespray; nothing in the other 16; the rest
of the graph unchanged (+164 DEPLOYS only); deterministic over three
runs; the incremental re-index equals a full index for a template body
edit, a deleted template (with and without a same-named fallback beside
the task file), a task list edit, a copied manifest edit, a defaults
list edit and a deleted vars file. Of the 10 tasks left out, 4 act on
an object by name, one builds its definition with a template lookup,
one applies a remote_src copy, and 4 name their file through a filter, a
variable or two joined results lists.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-14 20:46:38 +02:00
Martin Vogel bff5e01e29 Merge branch 'main' into feat/infra-followup
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-14 18:59:10 +02:00