agentleFS
canyonlabzcanyonlabz/perfpilot-hub/.cursor/skills/perfmemory/SKILL.md

perfmemory

Skill5 starsChanged 6 months agoMITView on GitHub

Manage the PerfMemory lessons-learned layer — search for past fixes before debugging, store debug sessions and attempts during debugging, and ingest existing knowledge from debug manifests or lessons-learned documents. Use when the user mentions perfmemory, lessons learned, debug memory, ingesting debug manifests, or searching for past fixes.

We split this skill into the rules it gives an agent

44 rules. About 0 are common, 20 are distinctive, and 24 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈20

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 24

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Before storing any debug session or attempt, agents MUST check taxonomy.yaml for canonical values.

    line 25
  2. Distinctive:

    If the error/value clearly fits an existing category but uses different wording, use the existing canonical name (the alias system handles …

    line 51
  3. Very specific:

    If the value is genuinely new and doesn't fit any existing category, inform the user: "This value is not in the taxonomy.

    line 53
  4. Distinctive:

    NEVER silently insert a non-canonical value without checking.

    line 56
  5. Distinctive:

    Instead of starting every debug workflow from scratch, agents check memory first and apply known fixes proactively.

    line 67
  6. Distinctive:

    When a new symptom is encountered, it is embedded and compared against stored vectors

    line 76
  7. Very specific:

    When graph is enabled, each match includes a source field:

    line 120
  8. Distinctive:

    Use this workflow when an agent encounters an error and wants to check if a similar issue has been solved before.

    line 138
  9. Very specific:

    If confirmed_count > 1 and is_verified = true: apply the fix directly

    line 170
  10. Very specific:

    If confirmed_count = 1 or is_verified = false: present to user for confirmation

    line 171
  11. Very specific:

    Check the source field: matches found by "both" vector and graph are highest confidence

    line 172
  12. Very specific:

    If the user approves a fix, apply it and pass the attempt_id as matched_attempt_id when storing the new attempt (Workflow B)

    line 177
  13. Distinctive:

    If still no results, proceed with normal debugging workflow

    line 182
  14. Very specific:

    If vector search returned no_match but you have an error_category, check the knowledge graph for cross-project patterns:

    line 186
  15. Distinctive:

    If matches are returned:

    line 198
  16. Very specific:

    When enrich = true (default), each match includes symptom_text, diagnosis, fix_description, sampler_name, api_endpoint, confirmed_count, …

    line 201
  17. Very specific:

    If a match looks promising, use get_related_issues (Step 4) to explore its neighborhood for additional related fixes

    line 205
  18. Distinctive:

    When a match from Step 2 or Step 3 looks relevant, explore its graph neighborhood to discover additional related fixes that may not have …

    line 210
  19. Very specific:

    Use this workflow during an active debug session to record what was tried and what worked.

    line 236
  20. Very specific:

    Before calling: Check taxonomy.yaml for the correct canonical values:

    line 243
  21. Very specific:

    Before calling: Check taxonomy.yaml → error_categories for an existing canonical name or alias that matches the error.

    line 293
  22. Distinctive:

    If genuinely new, ask the user before proceeding.

    line 296
  23. Very specific:

    If matched_attempt_id was provided, the response will also include confirmed_match_id and new_confirmed_count showing the updated …

    line 329
  24. Distinctive:

    Use this workflow to bulk-load lessons from existing documents into perfmemory.

    line 351
  25. Distinctive:

    Read the manifest file

    line 361
  26. Very specific:

    Call store_debug_session with the parsed metadata

    line 366
  27. Very specific:

    Call store_debug_attempt for each iteration

    line 376
  28. Very specific:

    Call close_debug_session with the final outcome

    line 377
  29. Distinctive:

    Read the lessons-learned document

    line 401
  30. Distinctive:

    Create a single debug session to group all lessons:

    line 402
  31. Very specific:

    Call close_debug_session with final_outcome = "resolved"

    line 421
  32. Distinctive:

    When a human confirms that a stored lesson is correct and reliable:

    line 442
  33. Distinctive:

    When a lesson becomes outdated (API changed, issue no longer occurs):

    line 450
  34. Distinctive:

    Use this after finding a match to discover additional related fixes, or to understand the structural context of a known issue before …

    line 495
  35. Very specific:

    When enrich = true, each neighbor includes symptom_text, diagnosis, fix_description, sampler_name, api_endpoint, confirmed_count, …

    line 512
  36. Distinctive:

    Use this as a fallback when vector search returns no matches, or proactively when starting work on a new project to check for known …

    line 519
  37. Very specific:

    When enrich = true, each match includes symptom_text, diagnosis, fix_description, sampler_name, api_endpoint, confirmed_count, is_verified, …

    line 534
  38. Very specific:

    If store_debug_session or store_debug_attempt fails, report the error immediately.

    line 542
  39. Very specific:

    Do NOT retry — these are code-based MCP tools (per mcp-error-handling.mdc).

    line 543
  40. Very specific:

    If find_similar_attempts fails, proceed with normal debugging (memory is advisory).

    line 544
  41. Very specific:

    If graph tools (find_cross_project_patterns, get_related_issues) fail or return "Graph layer is not enabled", fall back to vector-only …

    line 545
  42. Distinctive:

    If the database is unreachable, skip all perfmemory operations and inform the user.

    line 547
  43. Distinctive:

    Do not block the debugging workflow because memory is unavailable.

    line 548
  44. Distinctive:

    Never modify the perfmemory MCP source code to work around errors.

    line 549

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

PerfMemory — Lessons Learned Memory Layer

When to Use This Skill

  • User asks to search memory for past fixes before debugging
  • User asks to store a debug session or attempt in memory
  • User asks to ingest a debug manifest or lessons-learned document into memory
  • User asks to review, verify, or archive stored lessons
  • User asks for memory statistics or session details
  • The jmeter-debugging skill references PerfMemory integration steps

Taxonomy Compliance

Before storing any debug session or attempt, agents MUST check taxonomy.yaml for canonical values. Do NOT invent new freeform values for taxonomy-controlled fields.

Rules

  1. error_category — Check taxonomy.yaml → error_categories for an existing canonical name or alias that matches the error. Use the canonical name. If the error fits an existing category (even approximately), use it. Do NOT create a new category name without confirming with the user first.
  2. environment — Accepts either a specific environment name from taxonomy.yaml → environments (e.g., "QA1", "STG-East") or a canonical type from environment_types (e.g., qa, staging). The MCP resolves the input via a 3-step process: (1) match specific environment name, (2) match canonical type, (3) literal fallback. The env_type column is auto-derived — do NOT pass it manually to store_debug_session.
  3. auth_flow_type — Use values from taxonomy.yaml → auth_flow_types. Common values: none, oauth_pkce, entra_id, saml, token_chain, custom_sso. If unsure, check the taxonomy aliases list before guessing.
  4. system_under_test — Use the canonical application name from taxonomy.yaml → applications[].name. If the application is registered, use its exact canonical name.

What to Do When a Value Doesn't Match

  • If the error/value clearly fits an existing category but uses different wording, use the existing canonical name (the alias system handles resolution).
  • If the value is genuinely new and doesn't fit any existing category, inform the user: "This value is not in the taxonomy. Should I use [closest existing category] or would you like to add a new entry to taxonomy.yaml first?"
  • NEVER silently insert a non-canonical value without checking.

Reference

What PerfMemory Does

PerfMemory is a persistent memory layer backed by PostgreSQL + pgvector + Apache AGE. It stores structured debug sessions, attempts, and vector embeddings of symptoms so AI agents can recall past fixes via semantic similarity search and graph traversal. Instead of starting every debug workflow from scratch, agents check memory first and apply known fixes proactively.

How It Works

  1. Symptom text is embedded into a vector using the configured embedding provider
  2. The vector is stored alongside structured metadata (diagnosis, fix, outcome, etc.)
  3. Graph nodes and edges are created in the Apache AGE knowledge graph, linking attempts to projects, error patterns, and fix patterns
  4. When a new symptom is encountered, it is embedded and compared against stored vectors
  5. Graph traversal finds structurally related issues across projects — even when the symptom text is phrased differently
  6. Matches are ranked by a combined vector + graph score and returned with their fix details

Structured Symptom Text Template

The symptom_text field is the ONLY field that gets embedded as a vector. Its structure directly affects search quality. Always use this format:

[Error Category] on [Sampler Name] — [Response Code].
[Error Message / Symptom Description].
[Brief Diagnosis Context].

Good example:

Missing Correlations (OAuth Parameters) on TC01_S02_GET /oauth2/authorize
— java.net.URISyntaxException. Illegal character in query caused by unresolved
${oauth_redirect_uri}, ${oauth_client_id}, ${oauth_response_mode}, ${oauth_nonce},
${oauth_state}. The redirect URL from GET /home contains all OAuth parameters
URL-encoded in the goto query parameter but no extractors existed.

Bad example (too vague):

OAuth error on login page. Variables not found.

Similarity Score Interpretation

Score RangeRecommendationAction
> 0.85 or source: "both"apply_known_fixApply the fix directly, especially if confirmed_count > 1
0.60 - 0.85review_suggestionsPresent matches to user or review before applying
< 0.60no_matchNo useful matches — proceed with normal debugging

The default threshold is set in perfmemory-mcp/config.yaml under search.similarity_threshold.

Match Source Field

When graph is enabled, each match includes a source field:

SourceMeaning
vectorFound via pgvector cosine similarity only
graphFound via Apache AGE graph traversal only
bothFound by both vector search and graph traversal (highest confidence)

Related Rules

  • prerequisites.mdc — test_run_id is required for all workflows
  • skill-execution-rules.mdc — Follow steps in order, do not skip
  • mcp-error-handling.mdc — MCP tool error handling (no retry for code-based tools)

Workflow A — Search Memory Before Debugging

Use this workflow when an agent encounters an error and wants to check if a similar issue has been solved before. This is called BEFORE starting a debug loop.

Collect Inputs

REQUIRED:
  symptom_text = [the current error symptom — use the structured template above]

OPTIONAL:
  system_under_test = [filter to a specific system, e.g. "Shopping Cart"]
  system_alias      = [filter by app alias/short name, e.g. "OSP", "CART"]
  service_name      = [filter by microservice name, e.g. "cart-service"]
  error_category    = [filter to a specific category, e.g. "Missing Correlations"]

Step 1 — Search for Similar Attempts

find_similar_attempts(
  symptom_text      = {symptom_text},
  system_under_test = {system_under_test},      # optional
  system_alias      = {system_alias},           # optional — resolves to system_under_test
  service_name      = {service_name},           # optional
  error_category    = {error_category}          # optional — aliases resolved automatically
)

Step 2 — Interpret Results

If recommendation = apply_known_fix (similarity > 0.85 or source = "both"):

  • Review the top match's diagnosis and fix_description
  • If confirmed_count > 1 and is_verified = true: apply the fix directly
  • If confirmed_count = 1 or is_verified = false: present to user for confirmation
  • Check the source field: matches found by "both" vector and graph are highest confidence

If recommendation = review_suggestions (similarity 0.60 - 0.85):

  • Present the top 2-3 matches to the user with their diagnosis and fix
  • Let the user decide which (if any) to apply
  • If the user approves a fix, apply it and pass the attempt_id as matched_attempt_id when storing the new attempt (Workflow B)

If recommendation = no_match:

  • Try find_cross_project_patterns if error_category is known (Step 3)
  • If still no results, proceed with normal debugging workflow

Step 3 — Cross-Project Pattern Search (Graph)

If vector search returned no_match but you have an error_category, check the knowledge graph for cross-project patterns:

find_cross_project_patterns(
  error_category    = {error_category},
  current_project   = {system_under_test},     # optional — excludes own project
  response_code     = {response_code},         # optional — omit to match all response codes
  enrich            = true                     # optional — fetches full attempt details
)

If matches are returned:

  • Review the graph_path field to understand how the match was found (e.g., ErrorPattern(Missing Correlations/*) or SIMILAR_TO(hops<=2))
  • When enrich = true (default), each match includes symptom_text, diagnosis, fix_description, sampler_name, api_endpoint, confirmed_count, is_verified, system_alias, service_name, test_case_id, and test_case_name from the relational store — enough context to decide whether to apply the fix
  • If a match looks promising, use get_related_issues (Step 4) to explore its neighborhood for additional related fixes

Step 4 — Explore Graph Neighborhood (Optional)

When a match from Step 2 or Step 3 looks relevant, explore its graph neighborhood to discover additional related fixes that may not have surfaced in the initial search:

get_related_issues(
  attempt_id           = {matched_attempt_id},
  include_same_project = true,                 # set false for cross-project only
  enrich               = true                  # optional — fetches full neighbor details
)

This returns:

  • error_patterns: The error categories and response codes linked to this attempt
  • fix_patterns: The fix types and component types that resolved it
  • neighbors: Other attempts connected via SIMILAR_TO edges, with full details when enrich = true (symptom, diagnosis, fix, confidence signals)

Use this when:

  • A match was found but you want to see if there are related fixes for the same class of issue
  • You want to understand the full context of an error pattern before applying a fix
  • You want to check if the same fix type has been applied successfully in other projects

Workflow B — Store Lessons During Debugging

Use this workflow during an active debug session to record what was tried and what worked. This is integrated into the jmeter-debugging skill at specific steps.

Step 1 — Open a Debug Session

Called once at the start of debugging.

Before calling: Check taxonomy.yaml for the correct canonical values:

  • system_under_test → use applications[].name if the app is registered
  • environment → use a specific name from environments (e.g., "QA1", "STG-East") or a canonical type from environment_types (e.g., "qa", "staging"). The MCP auto-derives env_type from the input — do NOT pass env_type manually.
  • auth_flow_type → use a value from auth_flow_types (none, oauth_pkce, entra_id, saml, etc.)
REQUIRED:
  system_under_test = [what is being tested, e.g. "Shopping Cart"]
  test_run_id       = [the artifact test run ID]

OPTIONAL:
  system_alias      = [short name/alias, e.g. "OSP"]
  service_name      = [microservice name, e.g. "cart-service"]
  script_name       = [the JMX filename]
  auth_flow_type    = [none, oauth_pkce, oauth_auth_code, saml, token_chain,
                       custom_sso, entra_id, msal_pkce, other]
  auth_alias        = [human-readable auth label, e.g. "Corporate SSO"]
  environment       = [specific env name (e.g. "QA1", "STG-East") or
                       canonical type (e.g. "qa", "staging") — env_type is
                       auto-derived via taxonomy resolution]
  created_by        = [PTE name or "cursor"]
  notes             = [freeform session notes]
  strict_taxonomy   = [true/false — override config-level strictness]
store_debug_session(
  system_under_test = {system_under_test},
  test_run_id       = {test_run_id},
  system_alias      = {system_alias},
  service_name      = {service_name},
  script_name       = {script_name},
  auth_flow_type    = {auth_flow_type},
  auth_alias        = {auth_alias},
  environment       = {environment},
  created_by        = {created_by},
  notes             = {notes},
  strict_taxonomy   = {strict_taxonomy}
)

Save: session_id from the response. Check: taxonomy_warnings in the response — if present, review and note for future taxonomy updates.

Step 2 — Store Each Debug Attempt

Called after each debug iteration (after applying a fix and observing the result).

Before calling: Check taxonomy.yaml → error_categories for an existing canonical name or alias that matches the error. Use the canonical name. Do NOT invent new error category names — if the error fits an existing category (even approximately), use it. If genuinely new, ask the user before proceeding.

store_debug_attempt(
  session_id        = {session_id},
  iteration_number  = {iteration_count},
  symptom_text      = {structured symptom — use the template},
  outcome           = {resolved | failed | environment_issue | test_data_issue |
                       authentication_issue | needs_investigation},
  error_category    = {from taxonomy.yaml error_categories — use canonical name},
  severity          = {Critical | High | Medium},
  response_code     = {HTTP status code or exception name},
  hostname          = {host where the error occurred},
  sampler_name      = {the failing JMeter sampler},
  api_endpoint      = {the failing URL/endpoint},
  diagnosis         = {root cause determination — plain language},
  fix_description   = {what fix was applied — plain language},
  fix_type          = {add_extractor | move_extractor | edit_request_body |
                       edit_header | edit_correlation | other},
  component_type    = {json_extractor | regex_extractor | jsr223_postprocessor |
                       jsr223_preprocessor | http_sampler | test_plan | other},
  test_case_id      = {optional — e.g. "TC01"},
  test_case_name    = {optional — e.g. "User Login Flow"},
  test_step_id      = {optional — e.g. "S03"},
  test_step_name    = {optional — e.g. "Submit Credentials"},
  manifest_excerpt  = {optional — raw manifest iteration text},
  matched_attempt_id = {optional — UUID of a memory match that was applied}
)

Save: attempt_id from the response. Check: taxonomy_warnings in the response for unrecognized error categories.

If matched_attempt_id was provided, the response will also include confirmed_match_id and new_confirmed_count showing the updated confidence.

Step 3 — Close the Debug Session

Called once when debugging completes (resolved or not).

close_debug_session(
  session_id            = {session_id},
  final_outcome         = {resolved | unresolved | environment_issue |
                           test_data_issue | authentication_issue |
                           iteration_limit_reached | needs_investigation},
  resolution_attempt_id = {attempt_id of the fix that resolved the issue},
  notes                 = {summary of remaining issues if unresolved}
)

Workflow C — Ingest Existing Knowledge

Use this workflow to bulk-load lessons from existing documents into perfmemory. Supports two source formats.

Source 1: Debug Manifests

Debug manifests are markdown files at artifacts/{test_run_id}/analysis/debug_manifest.md generated by the jmeter-debugging skill. Each iteration maps to one attempt.

Ingestion Steps:

  1. Read the manifest file
  2. Parse the header for session metadata:
    • Test Run ID → test_run_id
    • Script → script_name
    • Status → infer final_outcome
  3. Call store_debug_session with the parsed metadata
    • system_under_test: ask the user if not obvious from the manifest
    • auth_flow_type: infer from the content (OAuth, SAML, CDSSO references)
    • environment: infer from hostnames (e.g., -stg. = "STG-East" or "staging")
  4. For each ## Iteration N section, parse:
    • Error Identified → error_category, sampler_name, response_code, error_message
    • Diagnosis → diagnosis
    • Fix Applied → fix_description, fix_type, component_type
    • Result After Fix → outcome (resolved if it improved, failed if it didn't)
  5. Build symptom_text using the structured template from the parsed fields
  6. Call store_debug_attempt for each iteration
  7. Call close_debug_session with the final outcome

Field Mapping Reference:

Manifest FieldPerfMemory Field
Iteration numberiteration_number
Sampler name from "Error Identified"sampler_name
Response Code from "Error Identified"response_code
Error Category from "Error Identified"error_category
"Diagnosis" sectiondiagnosis
"Fix Applied" sectionfix_description
Fix component type (JSR223, JSON Extractor, etc.)component_type
Fix action (add extractor, edit body, etc.)fix_type
"Result After Fix" sectionoutcome

Source 2: Lessons-Learned Documents

Lessons-learned documents are curated pattern files (e.g. .md files) that contain numbered lessons with symptoms, causes, and fixes. These represent human-reviewed knowledge and are stored as verified attempts.

Ingestion Steps:

  1. Read the lessons-learned document
  2. Create a single debug session to group all lessons:
    store_debug_session(
      system_under_test = {inferred or ask user},
      test_run_id       = {document filename or ask user},
      notes             = "Ingested from lessons-learned document: {filename}"
    )
  3. For each numbered lesson (e.g., ## 1. Always Use follow_redirects...):
    • Parse the lesson title as the error_category
    • Parse the description for symptom_text (the problem pattern)
    • Parse the Fix section for fix_description
    • Infer fix_type and component_type from the fix content
    • Set outcome = "resolved" (these are known-good patterns)
    • Call store_debug_attempt
  4. For pre-debug checklist items (e.g., "Prerequisites: Pre-Debug Validation"):
    • Each checklist item becomes a separate attempt
    • Set error_category = "Pre-Debug Validation"
    • Set outcome = "resolved"
  5. Call close_debug_session with final_outcome = "resolved"
  6. Mark all attempts as human-verified:
    verify_attempt(attempt_id = {each_attempt_id})

Symptom text for lessons-learned entries should capture the general pattern, not a specific sampler or test case:

JMeter Cookie/Redirect Handling — auto_redirects=true delegates redirect handling
to Java's HttpURLConnection which does not use JMeter's CookieManager. SSO cookies
are lost during redirect chains, causing authentication to fail silently.

Maintenance Operations

Verify an Attempt (Human Review)

When a human confirms that a stored lesson is correct and reliable:

verify_attempt(attempt_id = {attempt_id})

Archive an Attempt (Outdated Lesson)

When a lesson becomes outdated (API changed, issue no longer occurs):

archive_attempt(
  attempt_id = {attempt_id},
  reason     = {why this lesson is no longer valid}
)

Archived attempts remain in the database for audit but are excluded from search results.

View Memory Stats

get_memory_stats(
  system_under_test = {optional — filter to a specific system}
)

Review a Session

get_session_detail(session_id = {session_id})

Browse Sessions

list_sessions(
  system_under_test = {optional},
  system_alias      = {optional — filter by app alias},
  service_name      = {optional — filter by microservice},
  environment       = {optional — specific name (e.g. "QA1") or canonical type
                       (e.g. "qa"). Smart resolution: checks specific names first,
                       then canonical types, then literal match.},
  env_type          = {optional — direct filter on canonical type (e.g. "qa",
                       "staging"). No resolution — exact column match.},
  final_outcome     = {optional},
  limit             = 20
)

Explore Graph Neighborhood

View the graph connections for a specific attempt (requires graph.enabled: true). Use this after finding a match to discover additional related fixes, or to understand the structural context of a known issue before applying a fix.

get_related_issues(
  attempt_id           = {attempt_id},
  max_hops             = 2,                    # optional — graph traversal depth
  include_same_project = true,                 # optional — include same-project neighbors
  enrich               = true                  # optional — fetches full neighbor details
)

Returns:

  • error_patterns: list of {error_category, response_code} linked to this attempt
  • fix_patterns: list of {fix_type, component_type} that resolved it
  • neighbors: other attempts connected via SIMILAR_TO edges

When enrich = true, each neighbor includes symptom_text, diagnosis, fix_description, sampler_name, api_endpoint, confirmed_count, is_verified, system_alias, service_name, test_case_id, and test_case_name.

Cross-Project Pattern Search

Find if an error class has been resolved in other projects (requires graph.enabled: true). Use this as a fallback when vector search returns no matches, or proactively when starting work on a new project to check for known patterns.

find_cross_project_patterns(
  error_category  = {error_category},
  current_project = {system_under_test},       # optional — excludes own project
  response_code   = {response_code},           # optional — omit to match all response codes
  fix_type        = {fix_type},                # optional
  max_hops        = 2,                         # optional
  limit           = 5,                         # optional
  enrich          = true                       # optional — fetches full attempt details
)

When enrich = true, each match includes symptom_text, diagnosis, fix_description, sampler_name, api_endpoint, confirmed_count, is_verified, system_alias, service_name, test_case_id, and test_case_name.


Error Handling

  • If store_debug_session or store_debug_attempt fails, report the error immediately. Do NOT retry — these are code-based MCP tools (per mcp-error-handling.mdc).
  • If find_similar_attempts fails, proceed with normal debugging (memory is advisory).
  • If graph tools (find_cross_project_patterns, get_related_issues) fail or return "Graph layer is not enabled", fall back to vector-only results. Graph is supplementary.
  • If the database is unreachable, skip all perfmemory operations and inform the user. Do not block the debugging workflow because memory is unavailable.
  • Never modify the perfmemory MCP source code to work around errors.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.