Skip to content

feat: migrate project customization to .rootcoz/ folder (#152)#158

Open
myakove wants to merge 3 commits into
mainfrom
feat/issue-152-rootcoz-folder
Open

feat: migrate project customization to .rootcoz/ folder (#152)#158
myakove wants to merge 3 commits into
mainfrom
feat/issue-152-rootcoz-folder

Conversation

@myakove

@myakove myakove commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Migrate all project-level customization files from the repo root to a .rootcoz/ folder. Rename legacy JOB_INSIGHT_* filenames to ROOTCOZ_* and add support for custom pi agents, skills, and extensions provided by the analyzed project.

Breaking change: The old JOB_INSIGHT_*.md filenames in the repo root are no longer supported.

Closes #152

Changes

  • Rename JOB_INSIGHT_* constants to ROOTCOZ_* in engine/core.py and engine/__init__.py
  • Update build_resources_section() and build_prompt_sections() to scan <repo>/.rootcoz/
  • New copy_rootcoz_pi_resources() copies .rootcoz/{agents,skills,extensions}/ to workspace .pi/
  • Update issue prompt fetch path to .rootcoz/ROOTCOZ_ISSUE_PROMPT.md via GitHub Contents API
  • Update all references in FAILURE_HISTORY_ANALYSIS.md, models.py, cli/main.py
  • Update AGENTS.md documentation
  • 3 new tests + 3 updated tests, all 2593 tests pass

@myakove myakove force-pushed the feat/issue-152-rootcoz-folder branch from 2436186 to 682827f Compare July 1, 2026 11:11
@myakove-bot

Copy link
Copy Markdown
Collaborator

Report bugs in Issues

Welcome! 🎉

This pull request will be automatically processed with the following features:

🔄 Automatic Actions

  • Reviewer Assignment: Reviewers are automatically assigned based on the OWNERS file in the repository root
  • Size Labeling: PR size labels (XS, S, M, L, XL, XXL) are automatically applied based on changes
  • Issue Creation: Disabled for this repository
  • Branch Labeling: Branch-specific labels are applied to track the target branch
  • Auto-verification: Auto-verified users have their PRs automatically marked as verified
  • Labels: All label categories are enabled (default configuration)

📋 Available Commands

PR Status Management

  • /wip - Mark PR as work in progress (adds WIP: prefix to title)
  • /wip cancel - Remove work in progress status
  • /hold - Block PR merging (approvers only)
  • /hold cancel - Unblock PR merging
  • /verified - Mark PR as verified
  • /verified cancel - Remove verification status
  • /reprocess - Trigger complete PR workflow reprocessing (useful if webhook failed or configuration changed)
  • /regenerate-welcome - Regenerate this welcome message
  • /security-override - Set security check runs to pass (maintainers only)
  • /security-override cancel - Re-run security checks

Review & Approval

  • /lgtm - Approve changes (looks good to me)
  • /approve - Approve PR (approvers only)
  • /automerge - Enable automatic merging when all requirements are met (maintainers and approvers only)
  • /assign-reviewers - Assign reviewers based on OWNERS file
  • /assign-reviewer @username - Assign specific reviewer
  • /check-can-merge - Check if PR meets merge requirements

Testing & Validation

  • /retest tox - Run Python test suite with tox
  • /retest build-container - Rebuild and test container image
  • /retest python-module-install - Test Python package installation
  • /retest all - Run all available tests

Container Operations

  • /build-and-push-container - Build and push container image (tagged with PR number)
    • Supports additional build arguments: /build-and-push-container --build-arg KEY=value

Cherry-pick Operations

  • /cherry-pick <branch> - Schedule cherry-pick to target branch when PR is merged
    • Multiple branches: /cherry-pick branch1 branch2 branch3
  • /cherry-pick-retry <branch> - Retry a failed cherry-pick (merged PRs only)

Branch Management

  • /rebase - Rebase this PR branch onto its base branch

Label Management

  • /<label-name> - Add a label to the PR
  • /<label-name> cancel - Remove a label from the PR

✅ Merge Requirements

This PR will be automatically approved when the following conditions are met:

  1. Approval: /approve from at least one approver
  2. Status Checks: All required status checks must pass
  3. No Blockers: No wip, hold, has-conflicts labels and PR must be mergeable (no conflicts)
  4. Verified: PR must be marked as verified

📊 Review Process

Approvers and Reviewers

Approvers:

  • myakove

Reviewers:

  • myakove
  • rnetser
Available Labels
  • hold
  • verified
  • wip
  • lgtm
  • approve
  • automerge
AI Features
  • Conventional Title: Mode: fix (claude/claude-opus-4-6-1m)
  • Cherry-Pick Conflict Resolution: Enabled (claude/claude-opus-4-6-1m)
Security Checks
  • Suspicious Path Detection: Monitors paths: .claude/, .vscode/, .cursor/, .devcontainer/, .pi/, .github/workflows/, .github/actions/
  • Committer Identity Check: Verifies last committer matches PR author
  • Mandatory: Security checks block merge (use /security-override to bypass — maintainers only)

💡 Tips

  • WIP Status: Use /wip when your PR is not ready for review
  • Verification: The verified label is removed on new commits unless the push is detected as a clean rebase
  • Cherry-picking: Cherry-pick labels are processed when the PR is merged
  • Container Builds: Container images are automatically tagged with the PR number
  • Permission Levels: Some commands require approver permissions
  • Auto-verified Users: Certain users have automatic verification and merge privileges

For more information, please refer to the project documentation or contact the maintainers.

@myakove-bot

Copy link
Copy Markdown
Collaborator

Clean rebase detected — no code changes compared to previous head (2436186).

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Migrate repo customization to .rootcoz/ and add project-provided pi resources

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Move project customization prompts from repo root into .rootcoz/ (breaking change).
• Rename JOB_INSIGHT_* prompt constants to ROOTCOZ_* and update prompt discovery paths.
• Copy project-provided .rootcoz/{agents,skills,extensions} into workspace .pi/ for pi loading.
Diagram

graph TD
  A["Entry points (main/jenkins/chat)"] --> B["engine/core.py"] --> C["Scan .rootcoz prompts"]
  A --> D["copy_rootcoz_pi_resources"] --> E["workspace .pi/"] --> F["pi DefaultResourceLoader"]
  A --> G{{"GitHub Contents API"}} --> H[".rootcoz/ROOTCOZ_ISSUE_PROMPT.md"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Deprecation fallback for legacy root files
  • ➕ Avoids immediate breakage for existing repos still using JOB_INSIGHT_*.md at repo root
  • ➕ Allows a measured migration with warnings and a removal timeline
  • ➖ Adds conditional complexity and extra test surface
  • ➖ Can mask misconfigurations if both old/new files exist
2. Make customization root configurable (env/CLI)
  • ➕ Supports non-standard repo layouts and easier experimentation
  • ➕ Reduces need for future breaking renames
  • ➖ Increases configuration/UX complexity
  • ➖ Harder to document and support consistently

Recommendation: The .rootcoz/ consolidation is a clear improvement and enables richer project-provided resources; keep this approach. If downstream repos are numerous, consider a short deprecation window by optionally falling back to legacy root-level JOB_INSIGHT_*.md with an explicit warning to ease migration; otherwise the current breaking change is acceptable and simpler to maintain.

Files changed (9) +187 / -33

Enhancement (3) +81 / -13
core.pySwitch prompt discovery to '.rootcoz/' and add pi resource copying +41/-10

Switch prompt discovery to '.rootcoz/' and add pi resource copying

• Renames legacy 'JOB_INSIGHT_*' filename constants to 'ROOTCOZ_*' and updates 'build_resources_section()' / 'build_prompt_sections()' to look under '<repo>/.rootcoz/'. Introduces 'copy_rootcoz_pi_resources()' which copies '.rootcoz/{agents,skills,extensions}' into '<workspace>/.pi/' for pi discovery.

src/rootcoz/engine/core.py

main.pyCopy '.rootcoz' pi resources into '.pi/' and fetch issue prompt from '.rootcoz/' +35/-3

Copy '.rootcoz' pi resources into '.pi/' and fetch issue prompt from '.rootcoz/'

• Invokes 'copy_rootcoz_pi_resources()' after cloning repos for analysis and re-analysis flows, and adds a chat-specific helper to apply the same logic for chat workspaces. Updates issue prompt retrieval to request '.rootcoz/ROOTCOZ_ISSUE_PROMPT.md' from the GitHub Contents API.

src/rootcoz/main.py

jenkins_source.pyCopy project-provided '.rootcoz' pi resources during Jenkins analysis +5/-0

Copy project-provided '.rootcoz' pi resources during Jenkins analysis

• Calls 'copy_rootcoz_pi_resources()' after cloning repos in the Jenkins analysis path so project-specific agents/skills/extensions are available in the workspace '.pi/' directory.

src/rootcoz/sources/jenkins_source.py

Refactor (1) +8 / -6
__init__.pyExport renamed ROOTCOZ prompt constants and new resource-copy helper +8/-6

Export renamed ROOTCOZ prompt constants and new resource-copy helper

• Replaces exported 'JOB_INSIGHT_*' prompt filename constants with 'ROOTCOZ_*' equivalents and exposes 'copy_rootcoz_pi_resources' as part of the engine public API.

src/rootcoz/engine/init.py

Tests (1) +75 / -10
test_analyzer.pyUpdate prompt path tests and add coverage for pi resource copying +75/-10

Update prompt path tests and add coverage for pi resource copying

• Updates existing tests to create and assert '.rootcoz/ROOTCOZ_PROMPT.md' and '.rootcoz/ROOTCOZ_HISTORY_PROMPT.md' instead of legacy root files. Adds a new test class validating 'copy_rootcoz_pi_resources()' behavior for full, partial, and missing '.rootcoz/' cases.

tests/test_analyzer.py

Documentation (4) +23 / -4
AGENTS.mdDocument new '.rootcoz/' customization layout and breaking change +19/-0

Document new '.rootcoz/' customization layout and breaking change

• Adds documentation describing the '.rootcoz/' directory structure, the new ROOTCOZ prompt filenames, and how pi resources are copied into workspace '.pi/'. Explicitly calls out the breaking removal of legacy root-level 'JOB_INSIGHT_*.md' support.

AGENTS.md

FAILURE_HISTORY_ANALYSIS.mdUpdate guidance to look for '.rootcoz/ROOTCOZ_PROMPT.md' +1/-1

Update guidance to look for '.rootcoz/ROOTCOZ_PROMPT.md'

• Replaces the legacy instruction to check for 'JOB_INSIGHT_PROMPT.md' in the repo root with the new '.rootcoz/ROOTCOZ_PROMPT.md' path.

src/rootcoz/ai-prompts/FAILURE_HISTORY_ANALYSIS.md

main.pyUpdate CLI help text to reference '.rootcoz/ROOTCOZ_ISSUE_PROMPT.md' +1/-1

Update CLI help text to reference '.rootcoz/ROOTCOZ_ISSUE_PROMPT.md'

• Adjusts the '--issue-prompt' option help text to reflect the new issue prompt location and filename under '.rootcoz/'.

src/rootcoz/cli/main.py

models.pyUpdate API field descriptions for new '.rootcoz/' prompt override paths +2/-2

Update API field descriptions for new '.rootcoz/' prompt override paths

• Rewords request model field descriptions to reflect that raw and issue prompts override '.rootcoz/ROOTCOZ_*.md' files rather than legacy root-level 'JOB_INSIGHT_*.md' files.

src/rootcoz/models.py

Comment thread src/rootcoz/main.py Outdated
Comment thread src/rootcoz/engine/core.py Outdated
Comment thread src/rootcoz/engine/core.py Outdated
Comment thread src/rootcoz/engine/core.py Outdated
@qodo-code-review

qodo-code-review Bot commented Jul 1, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 40 rules

Grey Divider


Action required

1. Unhandled rglob exceptions ✓ Resolved 🐞 Bug ☼ Reliability
Description
copy_rootcoz_pi_resources() traverses src.rglob("*") outside its try/except, so a permission/broken
entry under .rootcoz/{agents,skills,extensions} can raise and crash setup despite the docstring
claiming failures are swallowed. This can abort analysis/chat setup before any AI work starts.
Code

src/rootcoz/engine/core.py[R189-202]

+            # Warn about files that will be overwritten by a later repo
+            if dest.is_dir():
+                for item in src.rglob("*"):
+                    if item.is_file():
+                        relative = item.relative_to(src)
+                        existing = dest / relative
+                        if existing.exists():
+                            logger.warning(
+                                ".rootcoz/%s/%s from '%s' overwrites existing file",
+                                subdir,
+                                relative,
+                                repo_name,
+                            )
+            try:
Relevance

⭐⭐⭐ High

Repo frequently accepts “don’t crash on filesystem/OSError; log+swallow” fixes (e.g., PR #115,
#131).

PR-#115
PR-#131

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The function promises failures are swallowed, but the overwrite-warning traversal happens before the
try/except and can raise, escaping the function and crashing callers.

src/rootcoz/engine/core.py[162-174]
src/rootcoz/engine/core.py[189-216]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`copy_rootcoz_pi_resources()` performs an overwrite-warning scan using `src.rglob("*")` before the `try:` that wraps `shutil.copytree(...)`. Any filesystem error during traversal (e.g., unreadable directory, broken entry) will raise and crash the caller, contradicting the function’s stated behavior that failures are logged and swallowed.

### Issue Context
This helper is invoked during workspace setup; it should be best-effort and never crash the analysis/chat flow due to project-provided `.rootcoz/` contents.

### Fix Focus Areas
- src/rootcoz/engine/core.py[189-216]

### Implementation notes
- Wrap the overwrite-detection traversal in its own `try/except OSError` (or move it inside the existing `try:`) and log+continue on failure.
- Keep the warning behavior best-effort: if traversal fails, skip overwrite warnings but still attempt the copy (or vice-versa, depending on desired semantics).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Symlink dereference during copy ✓ Resolved 🐞 Bug ⛨ Security
Description
copy_rootcoz_pi_resources() uses shutil.copytree() with default symlink handling, which dereferences
symlinks; a malicious cloned repo can place symlinks under .rootcoz/{agents,skills,extensions} and
cause the server to read/copy files outside the repo into the workspace. This is inconsistent with
existing code that treats symlink targets as a security hazard during cleanup.
Code

src/rootcoz/engine/core.py[R175-182]

+        rootcoz_dir = repo_path / ".rootcoz"
+        if not rootcoz_dir.is_dir():
+            continue
+        pi_dir = workspace / ".pi"
+        for subdir in _ROOTCOZ_PI_SUBDIRS:
+            src = rootcoz_dir / subdir
+            if src.is_dir():
+                shutil.copytree(src, pi_dir / subdir, dirs_exist_ok=True)
Relevance

⭐⭐⭐ High

Symlink risks are recognized; reviewers pushed symlink-hardening in workspace ops (partially
accepted) (PR #115).

PR-#115

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The copy helper copies from .rootcoz/ in cloned repos using shutil.copytree without any symlink
filtering, and cloned repos are sourced from user-provided repo URLs. The chat cleanup code
explicitly guards against symlink targets outside /tmp, demonstrating that symlink manipulation is
an acknowledged risk in this codebase.

src/rootcoz/engine/core.py[174-183]
src/rootcoz/main.py[3232-3264]
src/rootcoz/engine/chat.py[753-769]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The `.rootcoz/` copy step may dereference symlinks found inside untrusted repositories, potentially copying data from outside the repository tree into the workspace.

### Issue Context
Repos are cloned from user-provided URLs, and `.rootcoz/` is explicitly meant to be project-provided content. The codebase already has defensive logic around symlinks in chat workspace cleanup, indicating symlink targets are considered dangerous.

### Fix Focus Areas
- src/rootcoz/engine/core.py[162-188]
- src/rootcoz/engine/chat.py[753-769]
- src/rootcoz/main.py[3232-3264]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. monkeypatch unused parameter ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The new test test_copytree_oserror_swallowed declares a monkeypatch fixture parameter but never
uses it. This is an unused code artifact and may trigger lint/type-check failures in CI.
Code

tests/test_analyzer.py[1842]

+    def test_copytree_oserror_swallowed(self, tmp_path, monkeypatch) -> None:
Relevance

⭐⭐ Medium

No historical evidence found on removing unused pytest fixture parameters; similar test-style
cleanups are mixed.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Compliance ID 804990 requires removing or using newly introduced artifacts that are unused. The
function test_copytree_oserror_swallowed includes monkeypatch in its signature, but the body
uses only patch(...) and never references monkeypatch.

Rule 804990: Remove or use all unused code artifacts
tests/test_analyzer.py[1842-1863]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new test function accepts `monkeypatch` but does not use it, creating an unused parameter.

## Issue Context
This violates the requirement to remove or use unused code artifacts and can fail linting in CI.

## Fix Focus Areas
- tests/test_analyzer.py[1842-1863]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Copytree errors abort run ✓ Resolved 🐞 Bug ☼ Reliability
Description
copy_rootcoz_pi_resources() calls shutil.copytree() without handling OSError, so a single unreadable
file or disk/permission issue can crash analysis/chat setup after cloning. This creates a new single
point of failure in the main analysis and Jenkins analysis flows that call it before doing any AI
work.
Code

src/rootcoz/engine/core.py[R174-182]

+    for repo_name, repo_path in cloned_repos.items():
+        rootcoz_dir = repo_path / ".rootcoz"
+        if not rootcoz_dir.is_dir():
+            continue
+        pi_dir = workspace / ".pi"
+        for subdir in _ROOTCOZ_PI_SUBDIRS:
+            src = rootcoz_dir / subdir
+            if src.is_dir():
+                shutil.copytree(src, pi_dir / subdir, dirs_exist_ok=True)
Relevance

⭐⭐⭐ High

Team previously accepted wrapping filesystem writes in try/except to avoid aborting workflows (PR
#115).

PR-#115

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new helper performs copytree without try/except, and it is called directly from analysis flows
immediately after cloning; any exception will propagate and fail the job before AI
preflight/analysis begins.

src/rootcoz/engine/core.py[162-188]
src/rootcoz/main.py[3226-3264]
src/rootcoz/sources/jenkins_source.py[1060-1070]
PR-#115

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`copy_rootcoz_pi_resources()` performs `shutil.copytree(...)` directly; any `OSError` (permissions, ENOSPC, long paths, etc.) will bubble up and can abort the entire request.

### Issue Context
This function runs on untrusted, user-provided cloned repositories and is invoked in multiple request paths (main analysis and Jenkins source analysis). It should be best-effort: failures should be logged and skipped, not crash the whole job.

### Fix Focus Areas
- src/rootcoz/engine/core.py[162-188]
- src/rootcoz/main.py[3257-3264]
- src/rootcoz/sources/jenkins_source.py[1062-1070]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Silent .pi resource overwrites ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
copy_rootcoz_pi_resources() merges multiple repos into the same workspace
.pi/{agents,skills,extensions} with dirs_exist_ok=True, so later copies can silently overwrite
earlier files. Because cloned_repos is populated in-order (test repo added, then additional repos
updated), collisions become order-dependent and hard to debug.
Code

src/rootcoz/engine/core.py[R174-182]

+    for repo_name, repo_path in cloned_repos.items():
+        rootcoz_dir = repo_path / ".rootcoz"
+        if not rootcoz_dir.is_dir():
+            continue
+        pi_dir = workspace / ".pi"
+        for subdir in _ROOTCOZ_PI_SUBDIRS:
+            src = rootcoz_dir / subdir
+            if src.is_dir():
+                shutil.copytree(src, pi_dir / subdir, dirs_exist_ok=True)
Relevance

⭐⭐⭐ High

They’ve accepted changes preventing silent overwrites/collisions via unique outputs to avoid
nondeterminism (PR #131).

PR-#131

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The implementation always targets the same destination directories (workspace/.pi/<subdir>) for
every repo and uses dirs_exist_ok=True, which merges into existing directories; main.py shows test
repo is inserted into cloned_repos before additional repos are added, making any collisions
effectively last-writer-wins without visibility.

src/rootcoz/engine/core.py[174-183]
src/rootcoz/main.py[3238-3264]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Multiple cloned repos can contribute the same filenames under `.rootcoz/{agents,skills,extensions}`. Current copying merges everything into `<workspace>/.pi/<subdir>` and can overwrite existing files without warning, making behavior depend on clone/insertion order.

### Issue Context
In the main analysis flow, `cloned_repos` is populated by cloning the test repo first and then `update()`-ing with additional repos, which establishes the overwrite order when iterating.

### Fix Focus Areas
- src/rootcoz/engine/core.py[162-188]
- src/rootcoz/main.py[3238-3264]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. clone_chat_repos lacks .rootcoz copy ✓ Resolved 📎 Requirement gap ⚙ Maintainability
Description
The chat repo cloning logic in src/rootcoz/engine/chat.py does not itself perform the required
copy of .rootcoz/{agents,skills,extensions}/ into <workspace>/.pi/, so chat workflows that call
clone_chat_repos() outside of main.py would miss these resources. This violates the requirement
that the chat path performs the copy after cloning completes in src/rootcoz/engine/chat.py.
Code

src/rootcoz/main.py[R8706-8719]

+def _copy_chat_rootcoz_resources(workspace: Path) -> None:
+    """Copy .rootcoz/ pi resources from cloned repos in a chat workspace.
+
+    Scans workspace subdirectories (cloned repos) for ``.rootcoz/`` and
+    delegates to :func:`copy_rootcoz_pi_resources`.
+    """
+    cloned_repos = {
+        item.name: item
+        for item in workspace.iterdir()
+        if item.is_dir() and not item.name.startswith(".")
+    }
+    if cloned_repos:
+        copy_rootcoz_pi_resources(cloned_repos, workspace)
+
Relevance

⭐⭐⭐ High

Team accepts moving chat/workflow responsibilities to correct layer and removing architectural drift
(PR #115).

PR-#115

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1527097 requires the chat workflow to copy .rootcoz/{agents,skills,extensions}/
into <workspace>/.pi/ in src/rootcoz/engine/chat.py after clone_chat_repos completes. The PR
instead adds the copy helper in src/rootcoz/main.py, while clone_chat_repos in
src/rootcoz/engine/chat.py still ends without performing any .rootcoz.pi copy, making the
behavior dependent on main.py call sites.

Copy .rootcoz agents/skills/extensions into workspace .pi/ after cloning (chat path)
src/rootcoz/main.py[8706-8719]
src/rootcoz/engine/chat.py[77-182]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Chat workflows must copy project-provided pi resources from `.rootcoz/{agents,skills,extensions}/` into `<workspace>/.pi/` **within** `src/rootcoz/engine/chat.py` after `clone_chat_repos` completes, per the compliance requirement. Currently the copy is implemented in `src/rootcoz/main.py`, which means any future or alternate chat entrypoint that calls `clone_chat_repos()` directly could omit the required copy.

## Issue Context
The PR added `_copy_chat_rootcoz_resources()` in `src/rootcoz/main.py` and calls it after `clone_chat_repos()`, but `src/rootcoz/engine/chat.py` does not enforce this behavior.

## Fix Focus Areas
- src/rootcoz/main.py[8706-8719]
- src/rootcoz/engine/chat.py[77-182]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

7. Symlink dirs treated as repos ✓ Resolved 🐞 Bug ⛨ Security
Description
clone_chat_repos() builds cloned_repos by including every non-hidden workspace directory via
item.is_dir(), which also includes symlink-to-directory entries; these can be incorrectly treated as
repos and scanned for .rootcoz resources. This can cause unintended copying from symlinked workspace
entries (e.g., artifact links) instead of only actual cloned repos.
Code

src/rootcoz/engine/chat.py[R185-189]

+        cloned_repos = {
+            item.name: item
+            for item in workspace.iterdir()
+            if item.is_dir() and not item.name.startswith(".")
+        }
Relevance

⭐⭐⭐ High

Team has acted on symlink-hardening in workspace handling (PR #115); likely to accept excluding
symlink dirs.

PR-#115

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new code includes all non-hidden directories in the workspace; the same module documents and
handles symlinked workspace entries, so symlinks are plausible candidates to be incorrectly included
and processed.

src/rootcoz/engine/chat.py[181-192]
src/rootcoz/engine/chat.py[218-224]
src/rootcoz/engine/chat.py[784-799]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`clone_chat_repos()` discovers cloned repos by scanning `workspace.iterdir()` and selecting entries where `item.is_dir()` and not hidden. `is_dir()` returns true for symlinks to directories, so non-repo symlinked entries in the workspace can be misclassified as cloned repos and passed to `.rootcoz` copying.

### Issue Context
This file already treats symlinks as special (cleanup and workspace docs mention symlinked artifact dirs), so symlinks are expected to exist in the chat workspace.

### Fix Focus Areas
- src/rootcoz/engine/chat.py[181-192]

### Implementation notes
- Filter out symlinks in the comprehension (e.g., `and not item.is_symlink()`).
- Optionally, tighten repo detection (e.g., require a `.git` directory, or track the exact `target` paths created during cloning instead of re-scanning the workspace).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Previous review results

Review updated until commit af99fe8

Results up to commit 682827f


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. Symlink dereference during copy ✓ Resolved 🐞 Bug ⛨ Security
Description
copy_rootcoz_pi_resources() uses shutil.copytree() with default symlink handling, which dereferences
symlinks; a malicious cloned repo can place symlinks under .rootcoz/{agents,skills,extensions} and
cause the server to read/copy files outside the repo into the workspace. This is inconsistent with
existing code that treats symlink targets as a security hazard during cleanup.
Code

src/rootcoz/engine/core.py[R175-182]

+        rootcoz_dir = repo_path / ".rootcoz"
+        if not rootcoz_dir.is_dir():
+            continue
+        pi_dir = workspace / ".pi"
+        for subdir in _ROOTCOZ_PI_SUBDIRS:
+            src = rootcoz_dir / subdir
+            if src.is_dir():
+                shutil.copytree(src, pi_dir / subdir, dirs_exist_ok=True)
Relevance

⭐⭐⭐ High

Symlink risks are recognized; reviewers pushed symlink-hardening in workspace ops (partially
accepted) (PR #115).

PR-#115

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The copy helper copies from .rootcoz/ in cloned repos using shutil.copytree without any symlink
filtering, and cloned repos are sourced from user-provided repo URLs. The chat cleanup code
explicitly guards against symlink targets outside /tmp, demonstrating that symlink manipulation is
an acknowledged risk in this codebase.

src/rootcoz/engine/core.py[174-183]
src/rootcoz/main.py[3232-3264]
src/rootcoz/engine/chat.py[753-769]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The `.rootcoz/` copy step may dereference symlinks found inside untrusted repositories, potentially copying data from outside the repository tree into the workspace.

### Issue Context
Repos are cloned from user-provided URLs, and `.rootcoz/` is explicitly meant to be project-provided content. The codebase already has defensive logic around symlinks in chat workspace cleanup, indicating symlink targets are considered dangerous.

### Fix Focus Areas
- src/rootcoz/engine/core.py[162-188]
- src/rootcoz/engine/chat.py[753-769]
- src/rootcoz/main.py[3232-3264]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
2. clone_chat_repos lacks .rootcoz copy ✓ Resolved 📎 Requirement gap ⚙ Maintainability
Description
The chat repo cloning logic in src/rootcoz/engine/chat.py does not itself perform the required
copy of .rootcoz/{agents,skills,extensions}/ into <workspace>/.pi/, so chat workflows that call
clone_chat_repos() outside of main.py would miss these resources. This violates the requirement
that the chat path performs the copy after cloning completes in src/rootcoz/engine/chat.py.
Code

src/rootcoz/main.py[R8706-8719]

+def _copy_chat_rootcoz_resources(workspace: Path) -> None:
+    """Copy .rootcoz/ pi resources from cloned repos in a chat workspace.
+
+    Scans workspace subdirectories (cloned repos) for ``.rootcoz/`` and
+    delegates to :func:`copy_rootcoz_pi_resources`.
+    """
+    cloned_repos = {
+        item.name: item
+        for item in workspace.iterdir()
+        if item.is_dir() and not item.name.startswith(".")
+    }
+    if cloned_repos:
+        copy_rootcoz_pi_resources(cloned_repos, workspace)
+
Relevance

⭐⭐⭐ High

Team accepts moving chat/workflow responsibilities to correct layer and removing architectural drift
(PR #115).

PR-#115

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1527097 requires the chat workflow to copy .rootcoz/{agents,skills,extensions}/
into <workspace>/.pi/ in src/rootcoz/engine/chat.py after clone_chat_repos completes. The PR
instead adds the copy helper in src/rootcoz/main.py, while clone_chat_repos in
src/rootcoz/engine/chat.py still ends without performing any .rootcoz.pi copy, making the
behavior dependent on main.py call sites.

Copy .rootcoz agents/skills/extensions into workspace .pi/ after cloning (chat path)
src/rootcoz/main.py[8706-8719]
src/rootcoz/engine/chat.py[77-182]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Chat workflows must copy project-provided pi resources from `.rootcoz/{agents,skills,extensions}/` into `<workspace>/.pi/` **within** `src/rootcoz/engine/chat.py` after `clone_chat_repos` completes, per the compliance requirement. Currently the copy is implemented in `src/rootcoz/main.py`, which means any future or alternate chat entrypoint that calls `clone_chat_repos()` directly could omit the required copy.

## Issue Context
The PR added `_copy_chat_rootcoz_resources()` in `src/rootcoz/main.py` and calls it after `clone_chat_repos()`, but `src/rootcoz/engine/chat.py` does not enforce this behavior.

## Fix Focus Areas
- src/rootcoz/main.py[8706-8719]
- src/rootcoz/engine/chat.py[77-182]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Silent .pi resource overwrites ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
copy_rootcoz_pi_resources() merges multiple repos into the same workspace
.pi/{agents,skills,extensions} with dirs_exist_ok=True, so later copies can silently overwrite
earlier files. Because cloned_repos is populated in-order (test repo added, then additional repos
updated), collisions become order-dependent and hard to debug.
Code

src/rootcoz/engine/core.py[R174-182]

+    for repo_name, repo_path in cloned_repos.items():
+        rootcoz_dir = repo_path / ".rootcoz"
+        if not rootcoz_dir.is_dir():
+            continue
+        pi_dir = workspace / ".pi"
+        for subdir in _ROOTCOZ_PI_SUBDIRS:
+            src = rootcoz_dir / subdir
+            if src.is_dir():
+                shutil.copytree(src, pi_dir / subdir, dirs_exist_ok=True)
Relevance

⭐⭐⭐ High

They’ve accepted changes preventing silent overwrites/collisions via unique outputs to avoid
nondeterminism (PR #131).

PR-#131

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The implementation always targets the same destination directories (workspace/.pi/<subdir>) for
every repo and uses dirs_exist_ok=True, which merges into existing directories; main.py shows test
repo is inserted into cloned_repos before additional repos are added, making any collisions
effectively last-writer-wins without visibility.

src/rootcoz/engine/core.py[174-183]
src/rootcoz/main.py[3238-3264]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Multiple cloned repos can contribute the same filenames under `.rootcoz/{agents,skills,extensions}`. Current copying merges everything into `<workspace>/.pi/<subdir>` and can overwrite existing files without warning, making behavior depend on clone/insertion order.

### Issue Context
In the main analysis flow, `cloned_repos` is populated by cloning the test repo first and then `update()`-ing with additional repos, which establishes the overwrite order when iterating.

### Fix Focus Areas
- src/rootcoz/engine/core.py[162-188]
- src/rootcoz/main.py[3238-3264]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Copytree errors abort run ✓ Resolved 🐞 Bug ☼ Reliability
Description
copy_rootcoz_pi_resources() calls shutil.copytree() without handling OSError, so a single unreadable
file or disk/permission issue can crash analysis/chat setup after cloning. This creates a new single
point of failure in the main analysis and Jenkins analysis flows that call it before doing any AI
work.
Code

src/rootcoz/engine/core.py[R174-182]

+    for repo_name, repo_path in cloned_repos.items():
+        rootcoz_dir = repo_path / ".rootcoz"
+        if not rootcoz_dir.is_dir():
+            continue
+        pi_dir = workspace / ".pi"
+        for subdir in _ROOTCOZ_PI_SUBDIRS:
+            src = rootcoz_dir / subdir
+            if src.is_dir():
+                shutil.copytree(src, pi_dir / subdir, dirs_exist_ok=True)
Relevance

⭐⭐⭐ High

Team previously accepted wrapping filesystem writes in try/except to avoid aborting workflows (PR
#115).

PR-#115

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new helper performs copytree without try/except, and it is called directly from analysis flows
immediately after cloning; any exception will propagate and fail the job before AI
preflight/analysis begins.

src/rootcoz/engine/core.py[162-188]
src/rootcoz/main.py[3226-3264]
src/rootcoz/sources/jenkins_source.py[1060-1070]
PR-#115

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`copy_rootcoz_pi_resources()` performs `shutil.copytree(...)` directly; any `OSError` (permissions, ENOSPC, long paths, etc.) will bubble up and can abort the entire request.

### Issue Context
This function runs on untrusted, user-provided cloned repositories and is invoked in multiple request paths (main analysis and Jenkins source analysis). It should be best-effort: failures should be logged and skipped, not crash the whole job.

### Fix Focus Areas
- src/rootcoz/engine/core.py[162-188]
- src/rootcoz/main.py[3257-3264]
- src/rootcoz/sources/jenkins_source.py[1062-1070]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit 1a3aa80


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. Unhandled rglob exceptions ✓ Resolved 🐞 Bug ☼ Reliability
Description
copy_rootcoz_pi_resources() traverses src.rglob("*") outside its try/except, so a permission/broken
entry under .rootcoz/{agents,skills,extensions} can raise and crash setup despite the docstring
claiming failures are swallowed. This can abort analysis/chat setup before any AI work starts.
Code

src/rootcoz/engine/core.py[R189-202]

+            # Warn about files that will be overwritten by a later repo
+            if dest.is_dir():
+                for item in src.rglob("*"):
+                    if item.is_file():
+                        relative = item.relative_to(src)
+                        existing = dest / relative
+                        if existing.exists():
+                            logger.warning(
+                                ".rootcoz/%s/%s from '%s' overwrites existing file",
+                                subdir,
+                                relative,
+                                repo_name,
+                            )
+            try:
Relevance

⭐⭐⭐ High

Repo frequently accepts “don’t crash on filesystem/OSError; log+swallow” fixes (e.g., PR #115,
#131).

PR-#115
PR-#131

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The function promises failures are swallowed, but the overwrite-warning traversal happens before the
try/except and can raise, escaping the function and crashing callers.

src/rootcoz/engine/core.py[162-174]
src/rootcoz/engine/core.py[189-216]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`copy_rootcoz_pi_resources()` performs an overwrite-warning scan using `src.rglob("*")` before the `try:` that wraps `shutil.copytree(...)`. Any filesystem error during traversal (e.g., unreadable directory, broken entry) will raise and crash the caller, contradicting the function’s stated behavior that failures are logged and swallowed.

### Issue Context
This helper is invoked during workspace setup; it should be best-effort and never crash the analysis/chat flow due to project-provided `.rootcoz/` contents.

### Fix Focus Areas
- src/rootcoz/engine/core.py[189-216]

### Implementation notes
- Wrap the overwrite-detection traversal in its own `try/except OSError` (or move it inside the existing `try:`) and log+continue on failure.
- Keep the warning behavior best-effort: if traversal fails, skip overwrite warnings but still attempt the copy (or vice-versa, depending on desired semantics).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. monkeypatch unused parameter ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The new test test_copytree_oserror_swallowed declares a monkeypatch fixture parameter but never
uses it. This is an unused code artifact and may trigger lint/type-check failures in CI.
Code

tests/test_analyzer.py[1842]

+    def test_copytree_oserror_swallowed(self, tmp_path, monkeypatch) -> None:
Relevance

⭐⭐ Medium

No historical evidence found on removing unused pytest fixture parameters; similar test-style
cleanups are mixed.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Compliance ID 804990 requires removing or using newly introduced artifacts that are unused. The
function test_copytree_oserror_swallowed includes monkeypatch in its signature, but the body
uses only patch(...) and never references monkeypatch.

Rule 804990: Remove or use all unused code artifacts
tests/test_analyzer.py[1842-1863]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new test function accepts `monkeypatch` but does not use it, creating an unused parameter.

## Issue Context
This violates the requirement to remove or use unused code artifacts and can fail linting in CI.

## Fix Focus Areas
- tests/test_analyzer.py[1842-1863]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational
3. Symlink dirs treated as repos ✓ Resolved 🐞 Bug ⛨ Security
Description
clone_chat_repos() builds cloned_repos by including every non-hidden workspace directory via
item.is_dir(), which also includes symlink-to-directory entries; these can be incorrectly treated as
repos and scanned for .rootcoz resources. This can cause unintended copying from symlinked workspace
entries (e.g., artifact links) instead of only actual cloned repos.
Code

src/rootcoz/engine/chat.py[R185-189]

+        cloned_repos = {
+            item.name: item
+            for item in workspace.iterdir()
+            if item.is_dir() and not item.name.startswith(".")
+        }
Relevance

⭐⭐⭐ High

Team has acted on symlink-hardening in workspace handling (PR #115); likely to accept excluding
symlink dirs.

PR-#115

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new code includes all non-hidden directories in the workspace; the same module documents and
handles symlinked workspace entries, so symlinks are plausible candidates to be incorrectly included
and processed.

src/rootcoz/engine/chat.py[181-192]
src/rootcoz/engine/chat.py[218-224]
src/rootcoz/engine/chat.py[784-799]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`clone_chat_repos()` discovers cloned repos by scanning `workspace.iterdir()` and selecting entries where `item.is_dir()` and not hidden. `is_dir()` returns true for symlinks to directories, so non-repo symlinked entries in the workspace can be misclassified as cloned repos and passed to `.rootcoz` copying.

### Issue Context
This file already treats symlinks as special (cleanup and workspace docs mention symlinked artifact dirs), so symlinks are expected to exist in the chat workspace.

### Fix Focus Areas
- src/rootcoz/engine/chat.py[181-192]

### Implementation notes
- Filter out symlinks in the comprehension (e.g., `and not item.is_symlink()`).
- Optionally, tighten repo detection (e.g., require a `.git` directory, or track the exact `target` paths created during cloning instead of re-scanning the workspace).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

Comment thread tests/test_analyzer.py Outdated
Comment thread src/rootcoz/engine/core.py Outdated
Comment thread src/rootcoz/engine/chat.py
@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 4f36069

myakove added 3 commits July 2, 2026 15:02
BREAKING CHANGE: JOB_INSIGHT_*.md files in the repo root are no longer
supported. All project customization files must now be placed under
.rootcoz/ in the analyzed repository.

Changes:
- Rename constants: JOB_INSIGHT_PROMPT_FILENAME → ROOTCOZ_PROMPT_FILENAME,
  JOB_INSIGHT_ISSUE_PROMPT_FILENAME → ROOTCOZ_ISSUE_PROMPT_FILENAME,
  JOB_INSIGHT_FAILURE_HISTORY_PROMPT_FILENAME → ROOTCOZ_HISTORY_PROMPT_FILENAME
- Update build_resources_section() and build_prompt_sections() to scan
  <repo>/.rootcoz/ instead of <repo>/ for prompt files
- Add copy_rootcoz_pi_resources() to copy .rootcoz/{agents,skills,extensions}/
  into workspace .pi/ after cloning (analysis, re-analysis, chat paths)
- Update GitHub Contents API path to .rootcoz/ROOTCOZ_ISSUE_PROMPT.md
- Update description strings in models.py, cli/main.py help text,
  FAILURE_HISTORY_ANALYSIS.md
- Document .rootcoz/ convention in AGENTS.md
- Add tests for copy_rootcoz_pi_resources, update existing prompt tests
@myakove myakove force-pushed the feat/issue-152-rootcoz-folder branch from 4f36069 to af99fe8 Compare July 2, 2026 12:06
@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit af99fe8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: migrate project customization to .rootcoz/ folder with agent/skill support

2 participants