Skip to content

fix: rebuild symbol index after composer changes and index root config - #650

Merged
markshust merged 2 commits into
developfrom
feature/codeindexer-vendor-staleness
Nov 1, 2026
Merged

markshust merged 2 commits into
developfrom
feature/codeindexer-vendor-staleness

Conversation

@markshust

Copy link
Copy Markdown
Collaborator

Summary

Fixes two gaps in marko/codeindexer that made marko-lsp/marko-mcp report false marko.config.unknown_key diagnostics.

Composer changes now invalidate the index. IndexCache::build() persists a composerState fingerprint (path, mtime and size of vendor/composer/installed.json, falling back to composer.lock) in the cache payload. isStale() compares it to the current value, so composer require, update and remove mark the index stale and the next read rebuilds it. Vendor files are still not walked one by one, so the check costs a single stat. A payload without the new key, from an older version, counts as stale and rebuilds once.

The project-root config/ is indexed. The new ConfigScanner::scanDirectory() (which scan() now delegates to) scans <root>/config/*.php during build(). Those keys are recorded under the module name project (IndexCache::ROOT_CONFIG_MODULE). <root>/config is part of both the mtime walk and the tracked-path set, so edits, additions and deletions there also mark the index stale.

Also:

  • isStale() calls clearstatcache() first, so long-lived servers see changes other processes made to the files they stat.
  • Removed the code comments and docs that said vendor is excluded (codeindexer.md, troubleshooting.md, verification-checklist.md).

packages/devai is unchanged. Its CLAUDE.md promise that the index rebuilds itself is now accurate.

Tests

New packages/codeindexer/tests/Unit/Cache/IndexCacheComposerStateTest.php uses the real walker and scanners:

  1. A vendor package added and installed.json touched: the index is stale, and getConfigKeys() on the same instance contains new.*.
  2. A vendor package removed: the index is stale and its keys are gone.
  3. Falls back to composer.lock when installed.json is absent.
  4. A fresh process does not load the stale cache after a Composer change.
  5. <root>/config/app_only.php is indexed. Editing it or adding a new root config file marks the index stale.

Closes #649

🤖 Generated with Claude Code

https://claude.ai/code/session_017KaHtYgss6ELkNd1fkcNyX

IndexCache::isStale() skipped vendor modules and never looked at Composer
state, so the index stayed stale after composer require/update/remove. The
project-root config/ directory was never scanned, so keys defined there were
permanently reported as unknown by marko-lsp.

- Persist a Composer state fingerprint (path, mtime and size of
  vendor/composer/installed.json, falling back to composer.lock) in the cache
  payload and compare it in isStale().
- Index <root>/config/*.php via ConfigScanner::scanDirectory() under the
  module name "project", and include <root>/config in the mtime walk and the
  tracked-path set.
- Clear PHP's stat cache at the start of isStale() so long-lived servers see
  changes made by other processes.
- Fix stale comments and docs that said vendor is excluded.

Closes #649

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017KaHtYgss6ELkNd1fkcNyX
@github-actions github-actions Bot added the bug Something isn't working label Nov 1, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017KaHtYgss6ELkNd1fkcNyX
@markshust
markshust merged commit 4cebfd9 into develop Nov 1, 2026
4 checks passed
@markshust
markshust deleted the feature/codeindexer-vendor-staleness branch November 1, 2026 16:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Symbol index not rebuilt after composer require, and root config/ never indexed (LSP shows false diagnostics)

1 participant