Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d1f5c9e
Merge pull request #228 from rostilos/1.7.1-rc
rostilos Jul 22, 2026
359d654
Merge pull request #229 from rostilos/1.8.0-rc
rostilos Jul 28, 2026
d442106
Merge pull request #231 from rostilos/1.8.0-rc
rostilos Jul 29, 2026
47dc460
fix(rag): tolerate custom Magento XML and recover Qdrant writes
rostilos Jul 29, 2026
24d7b25
feat: newrelic for RAG Pipeline service
rostilos Jul 29, 2026
5088a74
fix(rag): quarantine invalid files instead of failing repository inde…
rostilos Jul 29, 2026
1f535ab
Merge pull request #232 from rostilos/1.8.0-rc
rostilos Jul 29, 2026
dc1bbc4
pytest fixes
rostilos Jul 29, 2026
0c15691
Merge pull request #233 from rostilos/1.8.0-rc
rostilos Jul 29, 2026
4a2d659
fix broken inference-ochstrator service deps
rostilos Jul 29, 2026
223c3fe
Merge pull request #234 from rostilos/1.8.0-rc
rostilos Jul 29, 2026
48dcf5f
fix python packages versions, improved ci/cd ( py tests selection ), …
rostilos Jul 29, 2026
cb168a3
Merge pull request #235 from rostilos/1.8.0-rc
rostilos Jul 29, 2026
25959bb
restore reliable analysis and recover queued jobs
rostilos Jul 29, 2026
2238334
Merge pull request #236 from rostilos/1.8.0-rc
rostilos Jul 29, 2026
c1a4c72
CI/CD fixes
rostilos Jul 29, 2026
ce90c30
Merge pull request #237 from rostilos/1.8.0-rc
rostilos Jul 29, 2026
3ede212
prevent VCS rate-limit storms during branch updates
rostilos Jul 29, 2026
0be1a62
Merge pull request #238 from rostilos/1.8.0-rc
rostilos Jul 29, 2026
7210abb
make RAG updates and scope limits reliable
rostilos Jul 30, 2026
c963634
Merge pull request #239 from rostilos/1.8.0-rc
rostilos Jul 30, 2026
b117caf
preserve task context across incremental PR analysis
rostilos Jul 31, 2026
e1d09c5
add self-hosted Gitlab instance support
rostilos Jul 31, 2026
186836a
add self-managed GitLab support
rostilos Aug 1, 2026
e0428d9
fix(vcs): improve inline reviews and conversation-aware replies
rostilos Aug 2, 2026
bc7c1aa
test coverage pipeline
rostilos Aug 2, 2026
23f4b06
parallelize RAG indexing and reuse unchanged vectors
rostilos Aug 2, 2026
6b01e69
restore branch defaults, RAG progress, and commit DAGs
rostilos Aug 2, 2026
d2dbcb5
clear stale CodeCrow review summaries on reruns
rostilos Aug 3, 2026
06553f3
Merge pull request #240 from rostilos/1.8.1-rc
rostilos Aug 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Repository Coverage

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
workflow_dispatch:

concurrency:
group: repository-coverage-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
coverage:
name: Test coverage policy
runs-on: ubuntu-latest
timeout-minutes: 60

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive

- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
cache: maven

- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip
cache-dependency-path: |
python-ecosystem/rag-pipeline/requirements.txt
python-ecosystem/inference-orchestrator/src/requirements.txt
python-ecosystem/inference-orchestrator/src/requirements.test.txt

- name: Run repository coverage gate
run: bash tools/coverage/check-repository-coverage.sh

- name: Publish coverage summary
if: always()
shell: bash
run: |
if [ -f build/coverage/summary.md ]; then
cat build/coverage/summary.md >> "$GITHUB_STEP_SUMMARY"
else
echo "# Repository coverage gate" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "The coverage runner stopped before it could create a summary." >> "$GITHUB_STEP_SUMMARY"
fi

- name: Upload coverage and test reports
if: always()
uses: actions/upload-artifact@v4
with:
name: repository-coverage-${{ github.run_id }}
if-no-files-found: warn
retention-days: 14
path: |
build/coverage/
java-ecosystem/**/target/site/jacoco/
analysis-plugins/**/target/site/jacoco/
java-ecosystem/**/target/surefire-reports/
java-ecosystem/**/target/failsafe-reports/
35 changes: 22 additions & 13 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,20 @@ permissions:

jobs:
python-tests:
name: Full Python Test Suite
name: Python Tests (${{ matrix.label }})
runs-on: ubuntu-latest
if: github.event.inputs.skip_build != 'true'
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- test_group: rag
label: RAG and plugin contracts
requirements: python-ecosystem/rag-pipeline/requirements.txt
- test_group: inference
label: Inference and review quality
requirements: python-ecosystem/inference-orchestrator/src/requirements.test.txt

steps:
- name: Checkout code
Expand All @@ -65,19 +75,18 @@ jobs:
with:
python-version: "3.11"
cache: pip
cache-dependency-path: |
python-ecosystem/rag-pipeline/requirements.txt
python-ecosystem/inference-orchestrator/src/requirements.txt
python-ecosystem/inference-orchestrator/src/requirements.test.txt
cache-dependency-path: ${{ matrix.requirements }}

- name: Install all Python test dependencies
- name: Install Python test dependencies
env:
PYTHON_TEST_GROUP: ${{ matrix.test_group }}
run: |
python -m pip install --upgrade pip
python -m pip install \
-r python-ecosystem/rag-pipeline/requirements.txt \
-r python-ecosystem/inference-orchestrator/src/requirements.test.txt
chmod +x deployment/ci/install-python-test-dependencies.sh
deployment/ci/install-python-test-dependencies.sh

- name: Run all Python test suites
- name: Run Python test group
env:
PYTHON_TEST_GROUP: ${{ matrix.test_group }}
run: |
chmod +x deployment/ci/python-tests.sh
deployment/ci/python-tests.sh
Expand All @@ -87,15 +96,15 @@ jobs:
continue-on-error: true
uses: dorny/test-reporter@v1
with:
name: Python Tests
name: Python Tests (${{ matrix.label }})
path: .ci-test-results/python/*.xml
reporter: java-junit

- name: Upload Python test reports
if: failure()
uses: actions/upload-artifact@v4
with:
name: python-test-reports
name: python-test-reports-${{ matrix.test_group }}
path: .ci-test-results/python/
retention-days: 7

Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Thumbs.db
coverage.xml
htmlcov/
.ci-test-results/
build/coverage/
.coverage-venvs/

# Python environments and packaging output
.venv/
Expand Down
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ These features are platform-independent and available through the CodeCrow web U

| Method | Bitbucket Cloud | GitHub | GitLab |
| :----------------------- | :-------------: | :---------------------------------: | :---------------------------------------: |
| OAuth / App Installation | ✅ (OAuth) | ✅ (GitHub App with OAuth fallback) | ✅ (OAuth, including self-managed GitLab) |
| OAuth / App Installation | ✅ (OAuth) | ✅ (GitHub App with OAuth fallback) | ✅ (GitLab.com only) |
| Self-managed VCS | — | — | ✅ (personal or project access token) |
| Manual Webhook | ✅ | ✅ | ✅ |
| CI Pipeline Action | ✅ | — | — |

Expand Down Expand Up @@ -155,9 +156,10 @@ normal review pipeline continues.
| Retrieval | Qdrant semantic search combined with deterministic metadata and exact-path lookup |
| Stored Context | Semantic source chunks plus exact-source, architecture, graph, plugin snapshot, and repository-detection points |
| Full Reindex | Builds a pending collection generation and atomically swaps the project alias only after successful completion |
| Resilient Writes | Quarantines malformed files and exact rejected points while retaining valid content; systemic Qdrant failures still prevent activation |
| Incremental Reindex | Applies one pinned commit change set and replaces changed semantic chunks together with affected graph and state groups |
| PR Context | Uses an immutable, commit-pinned PR overlay so changed files do not retrieve stale base-branch copies |
| Compatibility Guard | Returns HTTP 409 for stale or incompatible representation/plugin state before a review-model call; recovery is a full reindex with matching service images |
| Compatibility Guard | Host, selection, descriptor, and implementation fingerprints are provenance only and never force a reindex or filter context; snapshot integrity and Qdrant vector shape remain enforced |
| Prompt Budget | Plugin and RAG evidence share bounded context budgets; plugins cannot create an additional model stage |

The Vector Storage Explorer exposes the different point types and their
Expand Down Expand Up @@ -233,9 +235,12 @@ for the detailed invariants and failure behavior.
## Self-Hosting and Build Verification

The interactive setup configures secrets and chooses OpenRouter or Ollama for
embeddings. The production build synchronizes the pinned frontend submodule,
rejects local frontend drift, builds Java artifacts, assembles the Java plugin
bundle, builds the Compose services, and waits for them to become healthy.
embeddings. The local production build fetches and checks out the latest commit
from the frontend submodule's configured `main` branch, rejects local frontend
drift, recreates the two isolated Python 3.11 CI environments, and runs the same
Python, plugin-boundary, Maven `verify`, and observable-image Buildx gates as
CI/CD. Only after every gate passes does it replace the local Compose services
with those validated images and wait for health checks.

```bash
cd deployment
Expand All @@ -246,8 +251,9 @@ cd deployment
| Gate | Command or CI Behavior |
| :------------------ | :-------------------------------------------------------------------------------------------------------------------------------- |
| Java | `cd java-ecosystem && mvn clean verify` |
| Python | `deployment/ci/python-tests.sh` runs plugin-contract, RAG unit/integration, inference unit/integration, and review-quality suites |
| Python | CI and `production-build.sh` install each service's requirements separately, then run plugin-contract, RAG unit/integration, inference unit/integration, and review-quality suites |
| Plugin Boundary | `python3 tools/validate_plugin_boundaries.py` prevents concrete implementations from leaking into generic hosts |
| Docker Images | CI pushes and the local build loads images from the same contexts and observable Dockerfiles |
| Production Workflow | Manual dispatch; deployment waits for both Java/build and full Python test jobs unless explicitly deploying existing images |

## Contributing
Expand Down
10 changes: 10 additions & 0 deletions analysis-plugins/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,13 @@ applies it before full/incremental file loading and PR overlay construction; and
inference maps generated/excluded files to explicit non-reviewable hunk
dispositions before Stage 0. Empty registries keep the generic fallback, while a
failed policy contribution aborts instead of silently changing the disposition.

Magento classifies XML as module configuration only when it is directly below an
`etc` directory or directly below a known Magento area such as `etc/frontend`.
Custom descendants such as `etc/samples/*.xml` stay `full` project content and
are handled by the generic indexing path. DTDs and entities in those ordinary
documents are not resolved by the Magento repository analyzer. Actual Magento
configuration XML remains architecture input, but a malformed file or one with a
DTD/entity declaration is quarantined from the architecture snapshot instead of
failing the repository index. Other valid plugin inputs continue to contribute
deterministic context. Runtime/plugin contract failures remain fatal.
14 changes: 14 additions & 0 deletions analysis-plugins/contracts/python/codecrow_plugins/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,18 @@ class PluginDiagnostic:
code: str
message: str
plugin_id: str | None = None
path: str | None = None
recoverable: bool = False

def __post_init__(self) -> None:
_non_blank(self.code, "diagnostic code")
_non_blank(self.message, "diagnostic message")
if self.plugin_id is not None:
_plugin_id(self.plugin_id)
if self.path is not None:
normalized = normalize_path(self.path)
if normalized != self.path:
raise ValueError("diagnostic path must already be normalized")


T = TypeVar("T")
Expand Down Expand Up @@ -537,12 +543,20 @@ class RepositoryAnalysis:
packets: tuple[ArchitecturePacket, ...] = ()
snapshots: tuple[RepositorySnapshot, ...] = ()
contexts: tuple[RepositoryContext, ...] = ()
diagnostics: tuple[PluginDiagnostic, ...] = ()

def __post_init__(self) -> None:
_sorted_unique(self.symbols, "repository symbols")
_sorted_unique(self.packets, "architecture packets")
_sorted_unique(self.snapshots, "repository snapshots")
_sorted_unique(self.contexts, "repository contexts")
if any(
not isinstance(diagnostic, PluginDiagnostic)
for diagnostic in self.diagnostics
):
raise ValueError(
"repository diagnostics must contain PluginDiagnostic values"
)


@dataclass(frozen=True, order=True)
Expand Down
22 changes: 13 additions & 9 deletions analysis-plugins/contracts/python/codecrow_plugins/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,15 +401,18 @@ def ingest(self, artifacts: tuple[FileArtifact, ...]) -> None:
raise ValueError("repository artifacts must be path-sorted")
retained: list[tuple[str, object]] = []
for plugin_id, session in self._sessions:
try:
session.ingest(artifacts)
retained.append((plugin_id, session))
except Exception as exception:
self._diagnostics.append(PluginDiagnostic(
code="plugin-repository-ingest-exception",
message=f"{type(exception).__name__}: {exception}",
plugin_id=plugin_id,
))
for artifact in artifacts:
try:
session.ingest((artifact,))
except Exception as exception:
self._diagnostics.append(PluginDiagnostic(
code="plugin-repository-file-skipped",
message=f"{type(exception).__name__}: {exception}",
plugin_id=plugin_id,
path=artifact.path,
recoverable=True,
))
retained.append((plugin_id, session))
self._sessions = retained

def finish(self) -> tuple[RepositoryAnalysis, tuple[PluginDiagnostic, ...]]:
Expand Down Expand Up @@ -444,6 +447,7 @@ def finish(self) -> tuple[RepositoryAnalysis, tuple[PluginDiagnostic, ...]]:
plugin_id=plugin_id,
))
continue
self._diagnostics.extend(contribution.diagnostics)
symbols.update(contribution.symbols)
if len(symbols) > self._runtime.MAX_REPOSITORY_SYMBOLS:
self._diagnostics.append(PluginDiagnostic(
Expand Down
52 changes: 50 additions & 2 deletions analysis-plugins/contracts/python/tests/test_builtin_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ def test_magento_emits_effective_di_from_repository_state_only():
)


def test_magento_rejects_xml_entities_without_resolution():
def test_magento_quarantines_unsafe_config_without_failing_repository_analysis():
catalog = PluginCatalog.discover(PLUGINS_ROOT)
runtime = PluginRuntime(catalog)
capabilities = ProjectSelector(catalog.registry).select(_facts())
Expand All @@ -685,8 +685,56 @@ def test_magento_rejects_xml_entities_without_resolution():
), key=lambda value: value.path)))
analysis, diagnostics = handle.finish()

assert analysis.packets == ()
assert analysis.snapshots
assert all(
artifact.path not in packet.paths
for packet in analysis.packets
)
assert [diagnostic.code for diagnostic in diagnostics] == ["magento-unsafe-xml"]
assert diagnostics[0].recoverable is True
assert diagnostics[0].path == artifact.path


def test_magento_keeps_custom_entity_bearing_xml_outside_architecture_analysis():
catalog = PluginCatalog.discover(PLUGINS_ROOT)
runtime = PluginRuntime(catalog)
capabilities = ProjectSelector(catalog.registry).select(_facts())
path = "app/code/Punchout/Gateway/etc/samples/cxml_inv_po.xml"
artifact = FileArtifact(
path,
"""<?xml version="1.0"?>
<!DOCTYPE cXML SYSTEM "http://xml.cxml.org/schemas/cXML/1.2.014/InvoiceDetail.dtd">
<cXML payloadID="sample"><Response /></cXML>
""",
)

assert runtime.file_disposition(
path,
capabilities,
) is FileDisposition.FULL

handle = runtime.start_repository_analysis(
capabilities,
"0123456789abcdef",
)
handle.ingest(tuple(sorted((
artifact,
FileArtifact(
"app/code/Punchout/Gateway/etc/module.xml",
'<config><module name="Punchout_Gateway" /></config>',
),
FileArtifact(
"app/etc/config.php",
"<?php return ['modules' => ['Punchout_Gateway' => 1]];",
),
), key=lambda value: value.path)))
analysis, diagnostics = handle.finish()

assert diagnostics == ()
assert all(
path not in packet.paths
for packet in analysis.packets
)


def test_magento_file_policy_keeps_architecture_without_vendor_test_vectors():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2046,7 +2046,7 @@ def test_magento_di_emits_effective_virtual_and_php_inherited_object_arguments()
} <= child_packet_paths


def test_magento_di_virtual_type_argument_cycle_fails_closed():
def test_magento_di_virtual_type_argument_cycle_is_quarantined():
catalog = PluginCatalog.discover(PLUGINS_ROOT)
plugin = catalog.implementation("magento")
started = plugin.start_repository_analysis("virtual-cycle")
Expand All @@ -2071,10 +2071,11 @@ def test_magento_di_virtual_type_argument_cycle_fails_closed():

outcome = started.value.finish(RepositoryAnalysis())

assert outcome.status is OutcomeStatus.FAILED
assert outcome.diagnostic.code == (
assert outcome.status is OutcomeStatus.HANDLED
assert [diagnostic.code for diagnostic in outcome.value.diagnostics] == [
"magento-di-argument-inheritance-cycle"
)
]
assert outcome.value.diagnostics[0].recoverable is True


def test_magento_base_view_configuration_is_related_to_area_variant():
Expand Down
Loading
Loading