{"owner":"apache","repo":"nifi","hasSkills":true,"totalSkillsCount":15,"totalTokensCount":8423,"categories":["cursor-rule","root-instruction","plugin-manifest","anthropic-skill"],"hasMcp":false,"mcpConfig":null,"found":[".cursor/rules/ending-conditions.mdc",".cursor/rules/building.mdc",".cursor/rules/best-practices.mdc","AGENTS.md",".cursor/rules/comments.mdc",".cursor/rules/extension-development.mdc",".cursor/rules/code-style.mdc",".cursor/rules/extension-testing.mdc",".cursor/rules/system-test-troubleshooting-archive.mdc",".cursor/rules/framework-testing.mdc",".cursor/rules/persona.mdc",".cursor/rules/testing-standards.mdc","nifi-framework-bundle/nifi-framework-extensions/nifi-py4j-framework-bundle/README.md","nifi-registry/nifi-registry-extensions/nifi-registry-aws/nifi-registry-aws-assembly/README.md","nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/resources/extensions/ConsumeKafkaRecord_1_0.json"],"skills":{".cursor/rules/ending-conditions.mdc":"---\ndescription: Task completion checklist that must be verified before considering any task done\nalwaysApply: true\n---\n\n# Ending Conditions\n\nWhen you have completed a task, ensure that you have verified the following:\n\n1. All code compiles and builds successfully using `mvn`.\n2. All relevant unit tests pass successfully using `mvn`.\n3. All code adheres to the Code Style rules.\n4. Checkstyle and PMD pass successfully using\n   `mvn checkstyle:check pmd:check -T 1C` from the appropriate directory.\n5. Unit tests have been added to verify the functionality of any sufficiently complex method.\n6. A system test or an integration test has been added if the change makes significant\n   changes to the framework and the interaction between a significant number of classes.\n7. You have performed a full review of the code to ensure that there are no logical errors\n   and that the code is not duplicative or difficult to understand. If you find any code that\n   is in need of refactoring due to clarity or duplication, you should report this to the user\n   and offer to make those changes as well.\n8. If creating a new Processor or Controller Service, ensure that all relevant annotations\n   have been added, including `@Tags`, `@CapabilityDescription`, `@UseCase`, and\n   `@MultiProcessorUseCase` as appropriate.\n\n\nDo not consider the task complete until all of the above conditions have been met. When you\ndo consider the task complete, provide a summary of what you changed and which tests were\nadded or modified and what the behavior is that they verify. Additionally, provide any feedback\nabout your work that may need further review or that is not entirely complete.\n",".cursor/rules/building.mdc":"---\ndescription: Maven build instructions for the NiFi codebase\nalwaysApply: true\n---\n\n# Building\n\nNiFi is a complex Maven codebase. Never build code (testing or otherwise) using javac.\nAlways use `mvn` instead, or preferably the `.mvnw` wrapper script.\n\nAdditionally, building a maven module using the also-make flag (`-am`) is often very\nexpensive and slow. Instead, only build the specific module you are modifying. Assume that\nthe user has already built the entire codebase and that only the specific module you are\nmodifying needs to be built again. If this fails, you can prompt the user to build the entire\ncodebase, but only after you have attempted to build the relevant modules yourself first.\nIt is important not to run `mvn clean` at the root level or at the `nifi-assembly` level without\nthe user's express permission, as this may delete a running instance of NiFi, causing permanent\nloss of flows and configuration.\n\nWhen preparing to run system tests, packaged runtime artifacts must be refreshed using\n`mvn clean install` on the relevant packaging modules. Do not rely on `mvn install` or\n`mvn package` alone for system tests, as stale NARs or assemblies may be reused.\n\nIf a change affects code packaged into a NAR, rebuild the packaging module that delivers that\ncode before running system tests. For example, changes under\n`nifi-framework-bundle/nifi-framework/nifi-web/...` require rebuilding\n`nifi-framework-bundle/nifi-server-nar`, because the runtime uses `nifi-server-nar` to deliver\nthe updated web application classes.\n\nBefore running tests in `nifi-system-tests/nifi-system-test-suite`, refresh its runtime assembly\nwith `mvn clean install` for that module as well. The system test runtime uses\n`target/nifi-lib-assembly`, which is populated in `pre-integration-test` and later phases, so\nrunning `mvn test` or `mvn package` alone may leave stale runtime contents in place.\n",".cursor/rules/best-practices.mdc":"---\ndescription: General coding best practices for maintainable NiFi changes\nalwaysApply: true\n---\n\n# Best Practices\n\n- Reuse expensive, thread-safe helpers instead of constructing them ad hoc in methods or tests.\n- For example, when a class needs an `ObjectMapper`, prefer a shared field such as `private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();` unless a special configuration requires a dedicated instance.\n","AGENTS.md":"<!-- SPDX-License-Identifier: Apache-2.0 -->\n\n# Agent Guide for Apache NiFi\n\nThis file is read by automated agents (security scanners, code\nanalyzers, AI assistants) operating on this repository.\n\n## Security\n\nSecurity model: [SECURITY.md](./SECURITY.md)\n\nAgents that scan this repository should consult `SECURITY.md` and the\nthreat model it links before reporting issues.\n\nThe project security model is published at https://nifi.apache.org/documentation/security/#security-model linked in SECURITY.md.\n",".cursor/rules/comments.mdc":"---\ndescription: Keep source and test comments durable and code-focused\nalwaysApply: true\n---\n\n# Comment Guidance\n\nWrite comments as durable explanations of the code or test contract itself.\n\n- Do not write temporary commentary such as \"without the fix\", \"with the fix\", \"before this change\", or \"after this change\".\n- Do not describe a code path relative to the current patch or bugfix timeline.\n- In tests, describe the scenario, setup, and behavior being verified, not how the test behaved before or after a fix.\n- Do not add redundant assertion messages that simply restate the assertion or expected value. Use an assertion message only when it adds unique diagnostic context that the assertion itself does not already provide.\n- If historical context matters, put it in commit messages, pull request descriptions, or issue tracking, not in source comments.\n",".cursor/rules/extension-development.mdc":"---\ndescription: Development patterns for NiFi extensions (Processors, Controller Services, Connectors). Covers Property Descriptors, Relationships, and common patterns.\nalwaysApply: false\n---\n\n# Extension Development\n\nThis rule applies when developing NiFi extensions: Processors, Controller Services, and Connectors.\n\n## Property Descriptors\n\nProperty Descriptors are defined as `static final` fields on the component class using\n`PropertyDescriptor.Builder`.\n\n- **Naming:** Use clear, descriptive names. The `displayName` field should never be used. Make the\n  name itself clear and concise. Use Title Case for property names.\n- **Required vs. optional:** Mark properties as `.required(true)` when the component cannot\n  function without them. Prefer sensible defaults via `.defaultValue(...)` when possible.\n  When a default value is provided, the property will always have a value. The `required` flag in this\n  case is more of a documentation aid to indicate the importance of the property.\n- **Validators:** Always attach an appropriate `Validator` (e.g., `StandardValidators.NON_EMPTY_VALIDATOR`,\n  `StandardValidators.POSITIVE_INTEGER_VALIDATOR`). The Validator can be left off only when Allowable Values\n  are provided. In this case, do not include a Validator because it is redundant and confusing.\n- **Expression Language:** If a property should support Expression Language, add\n  `.expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES)` or the\n  appropriate scope. Always document when Expression Language is supported in the property\n  description. Some developers tend to go overboard here and feel like Expression Language should be supported\n  everywhere, but this is a mistake! The default assumption should be that Expression Language is not supported\n  unless the value is expected to be different for every FlowFile that is processed.\n- **Dependencies:** Use `.dependsOn(...)` to conditionally show properties based on the\n  values of other properties. This keeps the configuration UI clean and avoids exposing\n  irrelevant properties. If there is a dependency, it is important to understand that `.required(true)` means that\n  this property is required IF AND ONLY IF the dependency condition is met.\n\n## Processors\n\n- The `onTrigger` method should be focused on processing FlowFiles. Keep setup and teardown\n  logic in lifecycle methods when possible.\n- Prefer `session.read()` and `session.write()` with callbacks over directly working with\n  streams to ensure proper resource management.\n- Prefer `session.commitAsync()` over `session.commit()`. The `commit` method was the original implementation,\n  but it has now been deprecated in favor of `commitAsync`. The `commitAsync` call provide a clearer, cleaner\n  interface for handling post-commit actions including success and failure callbacks. In addition, the async\n  method allows Processors to be used much more efficiently in a Stateless NiFi flow.\n\n### Processor Lifecycle Annotations\n\n- Use `@OnScheduled` for setup that should happen once before the processor starts\n  running (e.g., creating clients, compiling patterns).\n- Use `@OnStopped` for cleanup (e.g., closing clients, releasing resources).\n- `@OnUnscheduled` is rarely used but can be used to interrupt long-running processes when the Processor is stopped.\n  Generally, though, it is preferable to write the Processor in such a way that long-running processes check `isScheduled()`\n  and stop gracefully if the return value is `false`.\n\n### Relationships\n- **Declaration**: Relationships are defined as `static final` fields using `new Relationship.Builder()`.\n  Relationship names should generally be lowercase.\n- **Success and Failure:** Most processors define at least a `success` and `failure`\n  relationship. Use `REL_SUCCESS` and `REL_FAILURE` as constant names.\n- **Original relationship:** Processors that enrich or fork FlowFiles often include an\n  `original` relationship for the unmodified input FlowFile.\n\n### Use Case Documentation\nThe `@UseCase` and `@MultiProcessorUseCase` annotations help document common usage patterns for Processors.\nThis is helpful for users to understand when and how to use the component effectively. It is equally important\nfor Agents that can determine which components should be used for a given task.\n\n- Use `@UseCase` to document common use cases for the Processor. This helps users understand\n  when and how to use the component effectively. This is unnecessary for Processors that serve a single use case\n  that is clearly described by the component name and description. For example, a Processor that consumes messages\n  from a specific service likely does not need a `@UseCase` annotation because its purpose is clear.\n- Use `@MultiProcessorUseCase` to document well-known patterns that involve multiple Processors working\n  together to achieve a common goal. Examples include List/Fetch patterns, Fork/Join patterns, etc.\n  The `@MultiProcessorUseCase` annotation should not be added to each individual Processor involved in the pattern.\n  Rather, the convention is to add the annotation to the last Processor in the flow that completes the pattern.\n  Some Processors will have one or more `@UseCase` annotations and no `@MultiProcessorUseCase` annotations,\n  while some will have one or more `@MultiProcessorUseCase` annotations and no `@UseCase` annotations.\n\n\n## Controller Services\n\nController Services are objects that can be shared across multiple components. This is typically done for\nclients that connect to external systems in order to avoid creating many connections, or in order to share\nconfiguration across multiple components without the user having to duplicate configuration. Controller Services\ncan also be helpful for abstracting away some piece of functionality into a separate extension point so that the\nimplementation can be swapped out by the user. For example, Record Readers and Writers are implemented as Controller\nServices so that the user can simply choose which format they want to read and write in a flexible and reusable way.\n\nThat said, Controller Services can be more onerous to configure and maintain for the user, so they should\nbe used sparingly and only when there is a clear benefit to doing so.\n\n### Controller Service Lifecycle Annotations\n\n- Use `@OnScheduled` for setup that should happen once before the service is enabled (e.g., creating clients, compiling patterns).\n- Use `@OnDisabled` for cleanup (e.g., closing clients, releasing resources).\n\n\n## General Patterns\n\n- Use `ComponentLog` (obtained via `getLogger()`) for all logging, not SLF4J directly.\n  This ensures log messages are associated with the component instance and that they generate Bulletins.\n- Use `@CapabilityDescription` to provide a clear and concise description of what the component does. This should not\n  be used for configuration details.\n- Use `@Tags` to provide relevant keywords that help users find the component.\n- Use `@SeeAlso` to reference related components.\n- Use `@WritesAttributes` and `@ReadsAttributes` to document which FlowFile attributes are read and written by the component.\n- Use `@DynamicProperty` to document any dynamic properties supported by the component.\n",".cursor/rules/code-style.mdc":"---\ndescription: Java code style conventions for the NiFi codebase\nglobs: \"**/*.java\"\nalwaysApply: false\n---\n\n# Code Style\n\nNiFi adheres to a few code styles that are not necessarily common. Please ensure that you\nobserve these code styles.\n\n1. Any variable that can be marked `final` must be marked `final`. This includes\n   declarations of Exceptions, method arguments, local variables, member variables, etc.\n2. Short-hand is highly discouraged in names of variables, classes, methods, etc., as well\n   as in documentation. Exceptions to this include in the framework, you may see references to\n   `procNode` for `ProcessorNode` or other such short-hand that is very difficult to confuse with\n   other terms, and it is used only when clearly defined such as `final ProcessorNode procNode = ...`.\n   Even though, however, we would not abbreviate `ControllerService` as `cs` because `cs` is too vague\n   and easily misunderstood. Instead, a value of `serviceNode` might be used.\n3. Private / helper methods should not be placed before the first public/protected method\n   that calls it.\n4. Unless the method is to be heavily reused, avoid creating trivial 1-2 line methods and\n   instead just place the code inline.\n5. Code is allowed to be up to 200 characters wide. Avoid breaking lines into many short lines.\n6. Avoid creating private methods that are called only once unless they are at least 10\n   lines long or are complex.\n7. It is never acceptable to use star imports. Import each individual class that is to be used.\n8. Never use underscores in class names, variables, or filenames.\n9. Never use System.out.println but instead use SLF4J Loggers.\n10. Avoid excessive whitespace in method invocations. For example, instead of writing:\n\n```java\nmyObject.doSomething(\n    arg1,\n    arg2,\n    arg3,\n    arg4,\n    arg5\n);\n```\n\nWrite this instead:\n\n```java\nmyObject.doSomething(arg1, arg2, arg3, arg4, arg5);\n```\n\nIt is okay to use many newlines in a builder pattern, such as:\n```java\nfinal MyObject myObject = MyObject.builder()\n    .arg1(arg1)\n    .arg2(arg2)\n    .arg3(arg3)\n    .build();\n```\n\nIt is also acceptable when chaining methods in a functional style such as:\n```java\nfinal List<String> result = myList.stream()\n    .filter(s -> s.startsWith(\"A\"))\n    .map(String::toUpperCase)\n    .toList();\n```\n\n11. When possible, prefer importing a class, rather than using fully qualified classname\n    inline in the code.\n12. Avoid statically importing methods, except in methods that are frequently used in testing\n    frameworks, such as the `Assertions` and `Mockito` classes.\n13. Avoid trailing whitespace at the end of lines, especially in blank lines.\n14. The `var` keyword is never allowed in the codebase. Always explicitly declare the type of variables.\n15. Prefer procedural code over functional code. For example, prefer using a for loop instead of a stream\n    when the logic is not simple and straightforward. The stream API is powerful but can be difficult to\n    read when overused or used in complex scenarios. Functional style is best used when the logic is simple\n    and chains together no more than 3-4 operations.\n16. Never use single-element arrays such as `final int[] counter = new int[]{0}` or\n    `final boolean[] flag = new boolean[]{false}` as a workaround for capturing a mutable primitive in a\n    lambda or anonymous class. This pattern is unidiomatic and confusing. When a lambda needs to mutate a\n    captured primitive, use the appropriate `java.util.concurrent.atomic` type (`AtomicInteger`,\n    `AtomicLong`, `AtomicBoolean`, `AtomicReference`, etc.). When a lambda needs to mutate a captured\n    object, hold the object in a final reference and mutate the object's state. The same applies to\n    `final Object[] holder = new Object[1]` for capturing a single reference; use `AtomicReference`\n    instead.\n17. Always place a blank line after a closing brace (`}`) that ends a control-flow construct (such as an\n    `if`, `else`, `for`, `while`, `switch`, or `try` / `catch` / `finally` block) when the next line is a\n    new statement or another control-flow construct at the same indentation level. This also applies to\n    the line following the closing brace of a nested block within a method. The goal is to clearly\n    separate logical sections of code. Exceptions:\n      - No blank line is required immediately before a closing brace of the enclosing block.\n      - No blank line is required between `} else {`, `} else if (...) {`, `} catch (...) {`, or\n        `} finally {` on the same chain.\n\n    Bad:\n    ```java\n    for (final Connector connector : connectors) {\n        if (connector.getCurrentState() != ConnectorState.TROUBLESHOOTING) {\n            continue;\n        }\n        final FrameworkFlowContext flowContext = connector.getActiveFlowContext();\n        if (flowContext != null && flowContext.getManagedProcessGroup().findFunnel(funnelId) != null) {\n            return true;\n        }\n    }\n    return false;\n    ```\n\n    Good:\n    ```java\n    for (final Connector connector : connectors) {\n        if (connector.getCurrentState() != ConnectorState.TROUBLESHOOTING) {\n            continue;\n        }\n\n        final FrameworkFlowContext flowContext = connector.getActiveFlowContext();\n        if (flowContext != null && flowContext.getManagedProcessGroup().findFunnel(funnelId) != null) {\n            return true;\n        }\n    }\n\n    return false;\n    ```\n18. Rule #11 (prefer importing a class rather than using a fully qualified classname inline) is not\n    optional. Even when a class is referenced only a single time, add an `import` statement rather than\n    referring to it by fully qualified name inline. The only acceptable use of a fully qualified\n    classname is when there is an unavoidable naming conflict with another imported class in the same\n    file. Fully qualified references scattered throughout code make it much harder to read and maintain.\n19. Never combine a negative condition such as `if (x != null)`, `if (!collection.isEmpty())`, or\n    `if (!flag)` with an `else` clause. Negated conditions are harder to read, and pairing them with an\n    `else` branch forces the reader to mentally invert the predicate for the \"happy path\". Instead,\n    rewrite the condition in the positive form (for example, swap the branches so the `if` tests the\n    positive condition), or use an early `return`/`continue`/`throw` to eliminate the `else` entirely.\n\n    Bad:\n    ```java\n    if (persistedManagedGroup != null) {\n        restore(persistedManagedGroup);\n    } else {\n        logger.warn(\"No snapshot was persisted; leaving Managed Process Group unchanged\");\n    }\n    ```\n\n    Good (positive predicate):\n    ```java\n    if (persistedManagedGroup == null) {\n        logger.warn(\"No snapshot was persisted; leaving Managed Process Group unchanged\");\n    } else {\n        restore(persistedManagedGroup);\n    }\n    ```\n\n    Or, preferably, use an early action and drop the `else`:\n    ```java\n    if (persistedManagedGroup == null) {\n        logger.warn(\"No snapshot was persisted; leaving Managed Process Group unchanged\");\n        return;\n    }\n\n    restore(persistedManagedGroup);\n    ```\n",".cursor/rules/extension-testing.mdc":"---\ndescription: Testing guidance for NiFi extensions (Processors, Controller Services, Connectors). Covers nifi-mock and TestRunner usage.\nalwaysApply: false\n---\n\n# Extension Testing\n\nThis rule applies when writing tests for NiFi extensions: Processors, Controller Services, and Connectors.\n\n## Unit Tests\n\nUnit tests should be used to test individual classes and methods in isolation. This often\nwill result in mocking dependency classes. However, if there already exists a Mock\nimplementation of an interface or dependency class, it is preferred to use the existing\nMock implementation. Similarly, for simple classes, it is preferable to make use of the\nreal implementation of a class rather than creating a Mock implementation. We are infinitely\nmore interested in having tests that are fast, reliable, correct, and easy to maintain than\nwe are in having tests that adhere to strict and arbitrary definitions of what constitutes\na \"unit test.\"\n\n## Use nifi-mock\n\nTests for extensions should always make use of the `nifi-mock` mocking framework. This is\ndone through the `TestRunner` interface and its standard implementation, obtained via\n`TestRunners.newTestRunner(processor)`.\n\nThe `TestRunner` provides methods for:\n- Setting property values (`setProperty`)\n- Enqueueing FlowFiles (`enqueue`)\n- Running the processor (`run`)\n- Asserting transfer to relationships (`assertTransferCount`, `assertAllFlowFilesTransferred`)\n- Validating processor configuration (`assertValid`, `assertNotValid`)\n- Asserting content and attributes of FlowFiles (`assertContentEquals`, `assertAttributeEquals`, etc.)\n\n## No System Tests for Extensions\n\nSystem tests are not expected for extensions. Extensions are tested at the unit level using\n`nifi-mock`. The `nifi-mock` framework provides sufficient isolation and simulation of the\nNiFi runtime environment.\n\n## What to Test\n\n- **Property validation:** If the extension has a custom Validator, it\n- **customValidate:** If the extension overrides the `customValidate` method, test that it correctly\n  validates the configuration and produces appropriate validation results.\n- **Relationship routing:** Verify that FlowFiles are routed to the correct relationship\n  based on input and configuration.\n- **Content transformation:** For processors that modify FlowFile content, verify that\n  output content matches expectations.\n- **Attribute handling:** Verify that expected attributes are set on output FlowFiles.\n- **Error handling:** Verify that error conditions (bad input, misconfiguration, simulated\n  failures) are handled correctly, typically by routing to a failure relationship.\n\n## What NOT to Test\n\n- **NiFi framework behavior:** Do not attempt to test the behavior of the NiFi framework itself.\n  For example, do not test that `session.commitAsync()` actually commits a transaction. Instead,\n  focus on testing that your extension behaves correctly when `commitAsync` is called, and trust\n  that the NiFi framework will handle the commit correctly.\n- **Validator behavior:** If a custom validator is used by an extension, that custom validator should\n  be tested separate as a unit test for the validator itself. However, if the extension point provides\n  a `customValidate` method, that should absolutely be tested as part of the extension's unit tests.\n- **The PropertyDescriptors that are returned:** Do not test that the `getSupportedPropertyDescriptors`\n  method returns the expected PropertyDescriptors. This is an anti-pattern because it does not properly\n  test that the extension abides by the contract of the API. For example, if a new PropertyDescriptor is\n  added whose default is to behave the same way as the old behavior, the test should absolutely pass.\n  However, if the test is written to expect a specific set of PropertyDescriptors, then the test will fail,\n  leading to confusion and unnecessary maintenance.\n\n## Controller Service Testing\n\nWhen a processor depends on a Controller Service, use `TestRunner.addControllerService`\nand `TestRunner.enableControllerService` to wire up either a real or mock implementation\nof the service for testing.\n\n## TestContainers\n\nFor Processors that interact with external systems, it can be helpful to use TestContainers to spin up\na temporary instance of the external system for testing. This allows for more realistic integration tests\nwithout requiring the user to have the external system installed and running on their machine.\n",".cursor/rules/system-test-troubleshooting-archive.mdc":"---\ndescription: Explains that every CI run of NiFi system tests uploads a per-test troubleshooting archive containing each cluster node's logs/ and conf/ directories, and how to download it when investigating a system test failure.\nalwaysApply: true\n---\n\n# System Test Troubleshooting Archive\n\nWhen a NiFi system test (anything under `nifi-system-tests/`) fails in CI, the\n`system-tests` workflow uploads a `*-troubleshooting-logs` artifact for each\nOS/JDK combination that ran. This archive is far more useful than the surefire/\nfailsafe console output alone and **must** be retrieved before drawing\nconclusions about a system test failure.\n\n## What the archive contains\n\nFor every failed test in that job, the archive includes:\n\n```\ntroubleshooting/\n  <TestClass>-<testMethod>/\n    node-1/\n      logs/        full nifi-app.log, nifi-bootstrap.log, etc. for that node\n      conf/        nifi.properties, flow definition, state-management.xml, etc.\n    node-2/\n      logs/\n      conf/\n    ...\n```\n\nThat is, for each failed test, the running NiFi instance(s) used by the test\nare captured with the same `logs/` and `conf/` you would inspect on a real\ndeployment. For clustered tests, every node is captured separately. The\narchive also typically contains the matching `failsafe-reports/`.\n\nThis is critical because a system test failure surfaces in CI as a single\nexception message, but the actual root cause is almost always visible only in\n`nifi-app.log` of one of the cluster nodes (cluster join issues, replication\nerrors, repository corruption, processor scheduling issues, etc.).\n\n## How to fetch the archive\n\n1. Identify the failing GitHub Actions run id. For a PR, list its checks:\n   ```\n   gh pr checks <PR_NUMBER> --repo apache/nifi\n   gh run list --repo apache/nifi --limit 30\n   ```\n2. List the artifacts attached to that run to see which OS/JDK combos are\n   available and confirm none have expired:\n   ```\n   gh api repos/apache/nifi/actions/runs/<RUN_ID>/artifacts \\\n     --jq '.artifacts[] | \"\\(.id)\\t\\(.name)\\t\\(.size_in_bytes)\\t\\(.expired)\"'\n   ```\n3. Download into a directory:\n   ```\n   gh run download <RUN_ID> --repo apache/nifi --dir <local-dir>\n   ```\n   Each `*-troubleshooting-logs` artifact becomes a subdirectory under\n   `<local-dir>`.\n4. Inspect the per-node logs for the specific failing test:\n   ```\n   <local-dir>/<os-jdk>-troubleshooting-logs/troubleshooting/<TestClass>-<testMethod>/node-1/logs/nifi-app.log\n   ```\n\nGitHub Actions artifacts expire (currently 7 days for these workflows), so\nfetch them as soon as a failure is observed; once expired they cannot be\nrecovered.\n\n## When this rule applies\n\n- Investigating any failed test under `nifi-system-tests/` in CI.\n- Triaging suspected flaky system tests across multiple PRs.\n- Reviewing a PR whose `system-tests` check has failed.\n\nIt does **not** apply to unit tests (surefire) or integration tests\n(failsafe outside `nifi-system-tests/`); those workflows do not produce a\ntroubleshooting archive, and only the surefire/failsafe console output is\navailable via `gh run view <RUN_ID> --log-failed`.\n",".cursor/rules/framework-testing.mdc":"---\ndescription: Testing guidance for NiFi framework code (not extensions). Covers when to use unit, integration, and system tests for framework classes.\nalwaysApply: false\n---\n\n# Framework Testing\n\nThis rule applies when working on NiFi framework code (not Processors, Controller\nServices, or Connectors).\n\n## Unit Tests\n\nUnit tests should be used to test individual classes and methods in isolation. This often\nwill result in mocking dependency classes. However, if there already exists a Mock\nimplementation of an interface or dependency class, it is preferred to use the existing\nMock implementation. Similarly, for simple classes, it is preferable to make use of the\nreal implementation of a class rather than creating a Mock implementation. We are infinitely\nmore interested in having tests that are fast, reliable, correct, and easy to maintain than\nwe are in having tests that adhere to strict and arbitrary definitions of what constitutes\na \"unit test.\"\n\n## Integration Tests\n\nWhen working in the framework, unit tests are still important, but integration tests and\nsystem tests are often more important. Integration tests are still allowed to use mocks but\ntypically we prefer to use real implementations of classes in order to ensure a more\nrealistic and holistic test.\n\n## System Tests\n\nSystem tests live in the `nifi-system-tests` module and should be used for any changes\nthat make significant changes to the framework and the interaction between a significant\nnumber of classes. They should also be used for any changes that may be fairly isolated but\nwhich are in a critical path of the framework, especially those that affect how data is\npersisted, processed, or accessed; or those that affect how components are created,\nconfigured, scheduled, or executed.\n\nGood candidates for system tests include changes to `ProcessScheduler`, `ProcessorNode`,\n`ControllerServiceNode`, `FlowController`, `FlowManager`, how Parameters are handled, flow\nsynchronization, the repositories, etc.\n\nWhen a system test switches the client to a specific cluster node using `switchClientToNode(...)`\nand then needs to verify the state of that node specifically, the request must bypass request\nreplication by passing `NiFiSystemIT.DO_NOT_REPLICATE` to the client call. Otherwise, the request\nmay be replicated or merged across the cluster and will not reliably reflect the state of the\nselected node.\n\n## Escalation\n\nAny unit test that ends up requiring a large number of mocks is a good candidate for an\nintegration test, and any integration test that ends up requiring a large number of mocks\nis a good candidate for a system test.\n",".cursor/rules/persona.mdc":"---\ndescription: AI persona and general approach for working on the Apache NiFi codebase\nalwaysApply: true\n---\n\n# AI Persona\n\nAct as an experienced Java software engineer. When considering how to implement a task,\nfirst consider the big picture of what is being asked. Then determine which classes will\nneed to be updated.\n\nQuite often, a single request will require manipulating many different classes. Generally\nspeaking, it is best to avoid changing established interfaces, especially those in nifi-api.\nIt is acceptable when necessary, but any change in nifi-api needs to be backward compatible.\nFor example, you might introduce a new method with a default implementation, or add a new method\nand deprecate an old one without removing it.\n",".cursor/rules/testing-standards.mdc":"---\ndescription: Shared test coding conventions for all NiFi automated tests (framework and extensions)\nglobs: \"**/src/test/**/*.java\"\nalwaysApply: false\n---\n\n# Testing Standards\n\nIn addition to the general rules defined in the Code Style rule, follow these rules when\ncreating or manipulating automated tests.\n\n## Test Code Conventions\n\n1. NEVER add comments such as `// Given`, `// When`, `// Then`. These comments are\n   considered an anti-pattern and should be removed or replaced whenever they are\n   encountered. Instead, leave them out all together (preferred) or use comments that\n   clearly articulate what is happening, such as `// Setup`, `// Invoke method`,\n   `// Assert expected results`. One of the reasons that this is considered an anti-pattern\n   (in addition to the fact that the given/when/then nomenclature itself provides no\n   meaning) is that it assumes a very specific pattern in unit tests, that we will create a\n   bunch of objects, invoke the method we care about, make assertions, and then end. This\n   often results in many tests that are extremely repetitive. Instead, whenever it makes\n   sense to do so, create the prerequisite objects, invoke the method we care about with\n   appropriate arguments, make assertions, and then invoke again with a different set of\n   arguments, make assertions, etc. There is no need to have many repetitive methods that\n   each create many repetitive objects.\n\n2. Unit tests are Java. They are not English. As such, they should be written like Java.\n   Frameworks such as assertj that strive to make the unit tests look more \"English-like\"\n   should be avoided. Use of these frameworks sometimes works well but often quickly\n   devolves into automated tests that read like neither English nor Java.\n\n3. Like any other code, unit tests should be created using reusable methods where\n   appropriate. Do not create 15 methods that are all very similar and repetitive. Instead,\n   create reusable methods that can be called from each of the methods.\n\n4. Never use the `assert` keyword. Use JUnit assertions instead.\n\n5. Never create a test file that is centered around testing a method or capability. Unit\n   tests must always be named after the class they are testing. It is okay if a given unit\n   test class is very, very long.\n\n6. This is a Java project using the Maven structure. Java test files must always fall under\n   src/test/java of the appropriate sub-module.\n\n7. Never use pointless assertions such as assertDoesNotThrow. This adds nothing but\n   complexity. Just call the method, and if it throws an Exception, the test will fail. It\n   is assumed by default that each line does not throw an Exception.\n\n8. Avoid providing messages in assert statements when they do not offer benefits over the default message.\n   For example, rather than:\n```\n   assertEquals(3, processors.size(), \"Expected 3 processors in the initial flow\");\n```\n   Just use:\n```\n   assertEquals(3, processors.size());\n```\nBecause the expectation is already clear from assertion, and this message is actually harmful because it hides the size\nof the `processors` Collection.\n\n## General Testing Philosophy\n\n- Unit tests should be used to verify any sufficiently complex method in a class. We should\n  *NOT* have unit tests for trivial methods such as getters and setters, or methods that are\n  only a few lines long and are not complex. A good general rule of thumb is that if a\n  person can understand a method and verify that it is correct in a few seconds, then it is\n  not necessary to have a unit test for that method.\n\n- Avoid adding multiple tests that are redundant. For example, if you have a method that\n  takes a single `String` argument it may make sense to test with `null`, a 0-length\n  string, and a long String. But do not test with 4 different Strings each with a different\n  value but which test the same lines of code. Avoid over-testing by adding multiple tests\n  whose differences are conditions that do not truly affect the behavior of the code.\n\n- Unit tests should always focus on the \"contract\" of the method. That is, given a certain\n  input, the test should assert that it receives the correct output. The test should NOT be\n  focused on the inner details of how the method works. Focusing on the inner details of\n  how the method works is an anti-pattern because it results in tests that are more brittle,\n  more difficult to maintain, and tests that fail when the method is improved or refactored\n  but still adheres to the same contract.\n","nifi-framework-bundle/nifi-framework-extensions/nifi-py4j-framework-bundle/README.md":"# nifi-py4j-framework-bundle Documentation\n\nPath: `nifi-framework-bundle/nifi-framework-extensions/nifi-py4j-framework-bundle/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/apache/nifi/HEAD/nifi-framework-bundle/nifi-framework-extensions/nifi-py4j-framework-bundle/README.md)","nifi-registry/nifi-registry-extensions/nifi-registry-aws/nifi-registry-aws-assembly/README.md":"# nifi-registry-aws-assembly Documentation\n\nPath: `nifi-registry/nifi-registry-extensions/nifi-registry-aws/nifi-registry-aws-assembly/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/apache/nifi/HEAD/nifi-registry/nifi-registry-extensions/nifi-registry-aws/nifi-registry-aws-assembly/README.md)","nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/resources/extensions/ConsumeKafkaRecord_1_0.json":"# Extensions Skill\n\nPath: `nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/resources/extensions/ConsumeKafkaRecord_1_0.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/apache/nifi/HEAD/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/resources/extensions/ConsumeKafkaRecord_1_0.json)"},"files":{".cursor/rules/ending-conditions.mdc":"---\ndescription: Task completion checklist that must be verified before considering any task done\nalwaysApply: true\n---\n\n# Ending Conditions\n\nWhen you have completed a task, ensure that you have verified the following:\n\n1. All code compiles and builds successfully using `mvn`.\n2. All relevant unit tests pass successfully using `mvn`.\n3. All code adheres to the Code Style rules.\n4. Checkstyle and PMD pass successfully using\n   `mvn checkstyle:check pmd:check -T 1C` from the appropriate directory.\n5. Unit tests have been added to verify the functionality of any sufficiently complex method.\n6. A system test or an integration test has been added if the change makes significant\n   changes to the framework and the interaction between a significant number of classes.\n7. You have performed a full review of the code to ensure that there are no logical errors\n   and that the code is not duplicative or difficult to understand. If you find any code that\n   is in need of refactoring due to clarity or duplication, you should report this to the user\n   and offer to make those changes as well.\n8. If creating a new Processor or Controller Service, ensure that all relevant annotations\n   have been added, including `@Tags`, `@CapabilityDescription`, `@UseCase`, and\n   `@MultiProcessorUseCase` as appropriate.\n\n\nDo not consider the task complete until all of the above conditions have been met. When you\ndo consider the task complete, provide a summary of what you changed and which tests were\nadded or modified and what the behavior is that they verify. Additionally, provide any feedback\nabout your work that may need further review or that is not entirely complete.\n",".cursor/rules/building.mdc":"---\ndescription: Maven build instructions for the NiFi codebase\nalwaysApply: true\n---\n\n# Building\n\nNiFi is a complex Maven codebase. Never build code (testing or otherwise) using javac.\nAlways use `mvn` instead, or preferably the `.mvnw` wrapper script.\n\nAdditionally, building a maven module using the also-make flag (`-am`) is often very\nexpensive and slow. Instead, only build the specific module you are modifying. Assume that\nthe user has already built the entire codebase and that only the specific module you are\nmodifying needs to be built again. If this fails, you can prompt the user to build the entire\ncodebase, but only after you have attempted to build the relevant modules yourself first.\nIt is important not to run `mvn clean` at the root level or at the `nifi-assembly` level without\nthe user's express permission, as this may delete a running instance of NiFi, causing permanent\nloss of flows and configuration.\n\nWhen preparing to run system tests, packaged runtime artifacts must be refreshed using\n`mvn clean install` on the relevant packaging modules. Do not rely on `mvn install` or\n`mvn package` alone for system tests, as stale NARs or assemblies may be reused.\n\nIf a change affects code packaged into a NAR, rebuild the packaging module that delivers that\ncode before running system tests. For example, changes under\n`nifi-framework-bundle/nifi-framework/nifi-web/...` require rebuilding\n`nifi-framework-bundle/nifi-server-nar`, because the runtime uses `nifi-server-nar` to deliver\nthe updated web application classes.\n\nBefore running tests in `nifi-system-tests/nifi-system-test-suite`, refresh its runtime assembly\nwith `mvn clean install` for that module as well. The system test runtime uses\n`target/nifi-lib-assembly`, which is populated in `pre-integration-test` and later phases, so\nrunning `mvn test` or `mvn package` alone may leave stale runtime contents in place.\n",".cursor/rules/best-practices.mdc":"---\ndescription: General coding best practices for maintainable NiFi changes\nalwaysApply: true\n---\n\n# Best Practices\n\n- Reuse expensive, thread-safe helpers instead of constructing them ad hoc in methods or tests.\n- For example, when a class needs an `ObjectMapper`, prefer a shared field such as `private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();` unless a special configuration requires a dedicated instance.\n","AGENTS.md":"<!-- SPDX-License-Identifier: Apache-2.0 -->\n\n# Agent Guide for Apache NiFi\n\nThis file is read by automated agents (security scanners, code\nanalyzers, AI assistants) operating on this repository.\n\n## Security\n\nSecurity model: [SECURITY.md](./SECURITY.md)\n\nAgents that scan this repository should consult `SECURITY.md` and the\nthreat model it links before reporting issues.\n\nThe project security model is published at https://nifi.apache.org/documentation/security/#security-model linked in SECURITY.md.\n",".cursor/rules/comments.mdc":"---\ndescription: Keep source and test comments durable and code-focused\nalwaysApply: true\n---\n\n# Comment Guidance\n\nWrite comments as durable explanations of the code or test contract itself.\n\n- Do not write temporary commentary such as \"without the fix\", \"with the fix\", \"before this change\", or \"after this change\".\n- Do not describe a code path relative to the current patch or bugfix timeline.\n- In tests, describe the scenario, setup, and behavior being verified, not how the test behaved before or after a fix.\n- Do not add redundant assertion messages that simply restate the assertion or expected value. Use an assertion message only when it adds unique diagnostic context that the assertion itself does not already provide.\n- If historical context matters, put it in commit messages, pull request descriptions, or issue tracking, not in source comments.\n",".cursor/rules/extension-development.mdc":"---\ndescription: Development patterns for NiFi extensions (Processors, Controller Services, Connectors). Covers Property Descriptors, Relationships, and common patterns.\nalwaysApply: false\n---\n\n# Extension Development\n\nThis rule applies when developing NiFi extensions: Processors, Controller Services, and Connectors.\n\n## Property Descriptors\n\nProperty Descriptors are defined as `static final` fields on the component class using\n`PropertyDescriptor.Builder`.\n\n- **Naming:** Use clear, descriptive names. The `displayName` field should never be used. Make the\n  name itself clear and concise. Use Title Case for property names.\n- **Required vs. optional:** Mark properties as `.required(true)` when the component cannot\n  function without them. Prefer sensible defaults via `.defaultValue(...)` when possible.\n  When a default value is provided, the property will always have a value. The `required` flag in this\n  case is more of a documentation aid to indicate the importance of the property.\n- **Validators:** Always attach an appropriate `Validator` (e.g., `StandardValidators.NON_EMPTY_VALIDATOR`,\n  `StandardValidators.POSITIVE_INTEGER_VALIDATOR`). The Validator can be left off only when Allowable Values\n  are provided. In this case, do not include a Validator because it is redundant and confusing.\n- **Expression Language:** If a property should support Expression Language, add\n  `.expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES)` or the\n  appropriate scope. Always document when Expression Language is supported in the property\n  description. Some developers tend to go overboard here and feel like Expression Language should be supported\n  everywhere, but this is a mistake! The default assumption should be that Expression Language is not supported\n  unless the value is expected to be different for every FlowFile that is processed.\n- **Dependencies:** Use `.dependsOn(...)` to conditionally show properties based on the\n  values of other properties. This keeps the configuration UI clean and avoids exposing\n  irrelevant properties. If there is a dependency, it is important to understand that `.required(true)` means that\n  this property is required IF AND ONLY IF the dependency condition is met.\n\n## Processors\n\n- The `onTrigger` method should be focused on processing FlowFiles. Keep setup and teardown\n  logic in lifecycle methods when possible.\n- Prefer `session.read()` and `session.write()` with callbacks over directly working with\n  streams to ensure proper resource management.\n- Prefer `session.commitAsync()` over `session.commit()`. The `commit` method was the original implementation,\n  but it has now been deprecated in favor of `commitAsync`. The `commitAsync` call provide a clearer, cleaner\n  interface for handling post-commit actions including success and failure callbacks. In addition, the async\n  method allows Processors to be used much more efficiently in a Stateless NiFi flow.\n\n### Processor Lifecycle Annotations\n\n- Use `@OnScheduled` for setup that should happen once before the processor starts\n  running (e.g., creating clients, compiling patterns).\n- Use `@OnStopped` for cleanup (e.g., closing clients, releasing resources).\n- `@OnUnscheduled` is rarely used but can be used to interrupt long-running processes when the Processor is stopped.\n  Generally, though, it is preferable to write the Processor in such a way that long-running processes check `isScheduled()`\n  and stop gracefully if the return value is `false`.\n\n### Relationships\n- **Declaration**: Relationships are defined as `static final` fields using `new Relationship.Builder()`.\n  Relationship names should generally be lowercase.\n- **Success and Failure:** Most processors define at least a `success` and `failure`\n  relationship. Use `REL_SUCCESS` and `REL_FAILURE` as constant names.\n- **Original relationship:** Processors that enrich or fork FlowFiles often include an\n  `original` relationship for the unmodified input FlowFile.\n\n### Use Case Documentation\nThe `@UseCase` and `@MultiProcessorUseCase` annotations help document common usage patterns for Processors.\nThis is helpful for users to understand when and how to use the component effectively. It is equally important\nfor Agents that can determine which components should be used for a given task.\n\n- Use `@UseCase` to document common use cases for the Processor. This helps users understand\n  when and how to use the component effectively. This is unnecessary for Processors that serve a single use case\n  that is clearly described by the component name and description. For example, a Processor that consumes messages\n  from a specific service likely does not need a `@UseCase` annotation because its purpose is clear.\n- Use `@MultiProcessorUseCase` to document well-known patterns that involve multiple Processors working\n  together to achieve a common goal. Examples include List/Fetch patterns, Fork/Join patterns, etc.\n  The `@MultiProcessorUseCase` annotation should not be added to each individual Processor involved in the pattern.\n  Rather, the convention is to add the annotation to the last Processor in the flow that completes the pattern.\n  Some Processors will have one or more `@UseCase` annotations and no `@MultiProcessorUseCase` annotations,\n  while some will have one or more `@MultiProcessorUseCase` annotations and no `@UseCase` annotations.\n\n\n## Controller Services\n\nController Services are objects that can be shared across multiple components. This is typically done for\nclients that connect to external systems in order to avoid creating many connections, or in order to share\nconfiguration across multiple components without the user having to duplicate configuration. Controller Services\ncan also be helpful for abstracting away some piece of functionality into a separate extension point so that the\nimplementation can be swapped out by the user. For example, Record Readers and Writers are implemented as Controller\nServices so that the user can simply choose which format they want to read and write in a flexible and reusable way.\n\nThat said, Controller Services can be more onerous to configure and maintain for the user, so they should\nbe used sparingly and only when there is a clear benefit to doing so.\n\n### Controller Service Lifecycle Annotations\n\n- Use `@OnScheduled` for setup that should happen once before the service is enabled (e.g., creating clients, compiling patterns).\n- Use `@OnDisabled` for cleanup (e.g., closing clients, releasing resources).\n\n\n## General Patterns\n\n- Use `ComponentLog` (obtained via `getLogger()`) for all logging, not SLF4J directly.\n  This ensures log messages are associated with the component instance and that they generate Bulletins.\n- Use `@CapabilityDescription` to provide a clear and concise description of what the component does. This should not\n  be used for configuration details.\n- Use `@Tags` to provide relevant keywords that help users find the component.\n- Use `@SeeAlso` to reference related components.\n- Use `@WritesAttributes` and `@ReadsAttributes` to document which FlowFile attributes are read and written by the component.\n- Use `@DynamicProperty` to document any dynamic properties supported by the component.\n",".cursor/rules/code-style.mdc":"---\ndescription: Java code style conventions for the NiFi codebase\nglobs: \"**/*.java\"\nalwaysApply: false\n---\n\n# Code Style\n\nNiFi adheres to a few code styles that are not necessarily common. Please ensure that you\nobserve these code styles.\n\n1. Any variable that can be marked `final` must be marked `final`. This includes\n   declarations of Exceptions, method arguments, local variables, member variables, etc.\n2. Short-hand is highly discouraged in names of variables, classes, methods, etc., as well\n   as in documentation. Exceptions to this include in the framework, you may see references to\n   `procNode` for `ProcessorNode` or other such short-hand that is very difficult to confuse with\n   other terms, and it is used only when clearly defined such as `final ProcessorNode procNode = ...`.\n   Even though, however, we would not abbreviate `ControllerService` as `cs` because `cs` is too vague\n   and easily misunderstood. Instead, a value of `serviceNode` might be used.\n3. Private / helper methods should not be placed before the first public/protected method\n   that calls it.\n4. Unless the method is to be heavily reused, avoid creating trivial 1-2 line methods and\n   instead just place the code inline.\n5. Code is allowed to be up to 200 characters wide. Avoid breaking lines into many short lines.\n6. Avoid creating private methods that are called only once unless they are at least 10\n   lines long or are complex.\n7. It is never acceptable to use star imports. Import each individual class that is to be used.\n8. Never use underscores in class names, variables, or filenames.\n9. Never use System.out.println but instead use SLF4J Loggers.\n10. Avoid excessive whitespace in method invocations. For example, instead of writing:\n\n```java\nmyObject.doSomething(\n    arg1,\n    arg2,\n    arg3,\n    arg4,\n    arg5\n);\n```\n\nWrite this instead:\n\n```java\nmyObject.doSomething(arg1, arg2, arg3, arg4, arg5);\n```\n\nIt is okay to use many newlines in a builder pattern, such as:\n```java\nfinal MyObject myObject = MyObject.builder()\n    .arg1(arg1)\n    .arg2(arg2)\n    .arg3(arg3)\n    .build();\n```\n\nIt is also acceptable when chaining methods in a functional style such as:\n```java\nfinal List<String> result = myList.stream()\n    .filter(s -> s.startsWith(\"A\"))\n    .map(String::toUpperCase)\n    .toList();\n```\n\n11. When possible, prefer importing a class, rather than using fully qualified classname\n    inline in the code.\n12. Avoid statically importing methods, except in methods that are frequently used in testing\n    frameworks, such as the `Assertions` and `Mockito` classes.\n13. Avoid trailing whitespace at the end of lines, especially in blank lines.\n14. The `var` keyword is never allowed in the codebase. Always explicitly declare the type of variables.\n15. Prefer procedural code over functional code. For example, prefer using a for loop instead of a stream\n    when the logic is not simple and straightforward. The stream API is powerful but can be difficult to\n    read when overused or used in complex scenarios. Functional style is best used when the logic is simple\n    and chains together no more than 3-4 operations.\n16. Never use single-element arrays such as `final int[] counter = new int[]{0}` or\n    `final boolean[] flag = new boolean[]{false}` as a workaround for capturing a mutable primitive in a\n    lambda or anonymous class. This pattern is unidiomatic and confusing. When a lambda needs to mutate a\n    captured primitive, use the appropriate `java.util.concurrent.atomic` type (`AtomicInteger`,\n    `AtomicLong`, `AtomicBoolean`, `AtomicReference`, etc.). When a lambda needs to mutate a captured\n    object, hold the object in a final reference and mutate the object's state. The same applies to\n    `final Object[] holder = new Object[1]` for capturing a single reference; use `AtomicReference`\n    instead.\n17. Always place a blank line after a closing brace (`}`) that ends a control-flow construct (such as an\n    `if`, `else`, `for`, `while`, `switch`, or `try` / `catch` / `finally` block) when the next line is a\n    new statement or another control-flow construct at the same indentation level. This also applies to\n    the line following the closing brace of a nested block within a method. The goal is to clearly\n    separate logical sections of code. Exceptions:\n      - No blank line is required immediately before a closing brace of the enclosing block.\n      - No blank line is required between `} else {`, `} else if (...) {`, `} catch (...) {`, or\n        `} finally {` on the same chain.\n\n    Bad:\n    ```java\n    for (final Connector connector : connectors) {\n        if (connector.getCurrentState() != ConnectorState.TROUBLESHOOTING) {\n            continue;\n        }\n        final FrameworkFlowContext flowContext = connector.getActiveFlowContext();\n        if (flowContext != null && flowContext.getManagedProcessGroup().findFunnel(funnelId) != null) {\n            return true;\n        }\n    }\n    return false;\n    ```\n\n    Good:\n    ```java\n    for (final Connector connector : connectors) {\n        if (connector.getCurrentState() != ConnectorState.TROUBLESHOOTING) {\n            continue;\n        }\n\n        final FrameworkFlowContext flowContext = connector.getActiveFlowContext();\n        if (flowContext != null && flowContext.getManagedProcessGroup().findFunnel(funnelId) != null) {\n            return true;\n        }\n    }\n\n    return false;\n    ```\n18. Rule #11 (prefer importing a class rather than using a fully qualified classname inline) is not\n    optional. Even when a class is referenced only a single time, add an `import` statement rather than\n    referring to it by fully qualified name inline. The only acceptable use of a fully qualified\n    classname is when there is an unavoidable naming conflict with another imported class in the same\n    file. Fully qualified references scattered throughout code make it much harder to read and maintain.\n19. Never combine a negative condition such as `if (x != null)`, `if (!collection.isEmpty())`, or\n    `if (!flag)` with an `else` clause. Negated conditions are harder to read, and pairing them with an\n    `else` branch forces the reader to mentally invert the predicate for the \"happy path\". Instead,\n    rewrite the condition in the positive form (for example, swap the branches so the `if` tests the\n    positive condition), or use an early `return`/`continue`/`throw` to eliminate the `else` entirely.\n\n    Bad:\n    ```java\n    if (persistedManagedGroup != null) {\n        restore(persistedManagedGroup);\n    } else {\n        logger.warn(\"No snapshot was persisted; leaving Managed Process Group unchanged\");\n    }\n    ```\n\n    Good (positive predicate):\n    ```java\n    if (persistedManagedGroup == null) {\n        logger.warn(\"No snapshot was persisted; leaving Managed Process Group unchanged\");\n    } else {\n        restore(persistedManagedGroup);\n    }\n    ```\n\n    Or, preferably, use an early action and drop the `else`:\n    ```java\n    if (persistedManagedGroup == null) {\n        logger.warn(\"No snapshot was persisted; leaving Managed Process Group unchanged\");\n        return;\n    }\n\n    restore(persistedManagedGroup);\n    ```\n",".cursor/rules/extension-testing.mdc":"---\ndescription: Testing guidance for NiFi extensions (Processors, Controller Services, Connectors). Covers nifi-mock and TestRunner usage.\nalwaysApply: false\n---\n\n# Extension Testing\n\nThis rule applies when writing tests for NiFi extensions: Processors, Controller Services, and Connectors.\n\n## Unit Tests\n\nUnit tests should be used to test individual classes and methods in isolation. This often\nwill result in mocking dependency classes. However, if there already exists a Mock\nimplementation of an interface or dependency class, it is preferred to use the existing\nMock implementation. Similarly, for simple classes, it is preferable to make use of the\nreal implementation of a class rather than creating a Mock implementation. We are infinitely\nmore interested in having tests that are fast, reliable, correct, and easy to maintain than\nwe are in having tests that adhere to strict and arbitrary definitions of what constitutes\na \"unit test.\"\n\n## Use nifi-mock\n\nTests for extensions should always make use of the `nifi-mock` mocking framework. This is\ndone through the `TestRunner` interface and its standard implementation, obtained via\n`TestRunners.newTestRunner(processor)`.\n\nThe `TestRunner` provides methods for:\n- Setting property values (`setProperty`)\n- Enqueueing FlowFiles (`enqueue`)\n- Running the processor (`run`)\n- Asserting transfer to relationships (`assertTransferCount`, `assertAllFlowFilesTransferred`)\n- Validating processor configuration (`assertValid`, `assertNotValid`)\n- Asserting content and attributes of FlowFiles (`assertContentEquals`, `assertAttributeEquals`, etc.)\n\n## No System Tests for Extensions\n\nSystem tests are not expected for extensions. Extensions are tested at the unit level using\n`nifi-mock`. The `nifi-mock` framework provides sufficient isolation and simulation of the\nNiFi runtime environment.\n\n## What to Test\n\n- **Property validation:** If the extension has a custom Validator, it\n- **customValidate:** If the extension overrides the `customValidate` method, test that it correctly\n  validates the configuration and produces appropriate validation results.\n- **Relationship routing:** Verify that FlowFiles are routed to the correct relationship\n  based on input and configuration.\n- **Content transformation:** For processors that modify FlowFile content, verify that\n  output content matches expectations.\n- **Attribute handling:** Verify that expected attributes are set on output FlowFiles.\n- **Error handling:** Verify that error conditions (bad input, misconfiguration, simulated\n  failures) are handled correctly, typically by routing to a failure relationship.\n\n## What NOT to Test\n\n- **NiFi framework behavior:** Do not attempt to test the behavior of the NiFi framework itself.\n  For example, do not test that `session.commitAsync()` actually commits a transaction. Instead,\n  focus on testing that your extension behaves correctly when `commitAsync` is called, and trust\n  that the NiFi framework will handle the commit correctly.\n- **Validator behavior:** If a custom validator is used by an extension, that custom validator should\n  be tested separate as a unit test for the validator itself. However, if the extension point provides\n  a `customValidate` method, that should absolutely be tested as part of the extension's unit tests.\n- **The PropertyDescriptors that are returned:** Do not test that the `getSupportedPropertyDescriptors`\n  method returns the expected PropertyDescriptors. This is an anti-pattern because it does not properly\n  test that the extension abides by the contract of the API. For example, if a new PropertyDescriptor is\n  added whose default is to behave the same way as the old behavior, the test should absolutely pass.\n  However, if the test is written to expect a specific set of PropertyDescriptors, then the test will fail,\n  leading to confusion and unnecessary maintenance.\n\n## Controller Service Testing\n\nWhen a processor depends on a Controller Service, use `TestRunner.addControllerService`\nand `TestRunner.enableControllerService` to wire up either a real or mock implementation\nof the service for testing.\n\n## TestContainers\n\nFor Processors that interact with external systems, it can be helpful to use TestContainers to spin up\na temporary instance of the external system for testing. This allows for more realistic integration tests\nwithout requiring the user to have the external system installed and running on their machine.\n",".cursor/rules/system-test-troubleshooting-archive.mdc":"---\ndescription: Explains that every CI run of NiFi system tests uploads a per-test troubleshooting archive containing each cluster node's logs/ and conf/ directories, and how to download it when investigating a system test failure.\nalwaysApply: true\n---\n\n# System Test Troubleshooting Archive\n\nWhen a NiFi system test (anything under `nifi-system-tests/`) fails in CI, the\n`system-tests` workflow uploads a `*-troubleshooting-logs` artifact for each\nOS/JDK combination that ran. This archive is far more useful than the surefire/\nfailsafe console output alone and **must** be retrieved before drawing\nconclusions about a system test failure.\n\n## What the archive contains\n\nFor every failed test in that job, the archive includes:\n\n```\ntroubleshooting/\n  <TestClass>-<testMethod>/\n    node-1/\n      logs/        full nifi-app.log, nifi-bootstrap.log, etc. for that node\n      conf/        nifi.properties, flow definition, state-management.xml, etc.\n    node-2/\n      logs/\n      conf/\n    ...\n```\n\nThat is, for each failed test, the running NiFi instance(s) used by the test\nare captured with the same `logs/` and `conf/` you would inspect on a real\ndeployment. For clustered tests, every node is captured separately. The\narchive also typically contains the matching `failsafe-reports/`.\n\nThis is critical because a system test failure surfaces in CI as a single\nexception message, but the actual root cause is almost always visible only in\n`nifi-app.log` of one of the cluster nodes (cluster join issues, replication\nerrors, repository corruption, processor scheduling issues, etc.).\n\n## How to fetch the archive\n\n1. Identify the failing GitHub Actions run id. For a PR, list its checks:\n   ```\n   gh pr checks <PR_NUMBER> --repo apache/nifi\n   gh run list --repo apache/nifi --limit 30\n   ```\n2. List the artifacts attached to that run to see which OS/JDK combos are\n   available and confirm none have expired:\n   ```\n   gh api repos/apache/nifi/actions/runs/<RUN_ID>/artifacts \\\n     --jq '.artifacts[] | \"\\(.id)\\t\\(.name)\\t\\(.size_in_bytes)\\t\\(.expired)\"'\n   ```\n3. Download into a directory:\n   ```\n   gh run download <RUN_ID> --repo apache/nifi --dir <local-dir>\n   ```\n   Each `*-troubleshooting-logs` artifact becomes a subdirectory under\n   `<local-dir>`.\n4. Inspect the per-node logs for the specific failing test:\n   ```\n   <local-dir>/<os-jdk>-troubleshooting-logs/troubleshooting/<TestClass>-<testMethod>/node-1/logs/nifi-app.log\n   ```\n\nGitHub Actions artifacts expire (currently 7 days for these workflows), so\nfetch them as soon as a failure is observed; once expired they cannot be\nrecovered.\n\n## When this rule applies\n\n- Investigating any failed test under `nifi-system-tests/` in CI.\n- Triaging suspected flaky system tests across multiple PRs.\n- Reviewing a PR whose `system-tests` check has failed.\n\nIt does **not** apply to unit tests (surefire) or integration tests\n(failsafe outside `nifi-system-tests/`); those workflows do not produce a\ntroubleshooting archive, and only the surefire/failsafe console output is\navailable via `gh run view <RUN_ID> --log-failed`.\n",".cursor/rules/framework-testing.mdc":"---\ndescription: Testing guidance for NiFi framework code (not extensions). Covers when to use unit, integration, and system tests for framework classes.\nalwaysApply: false\n---\n\n# Framework Testing\n\nThis rule applies when working on NiFi framework code (not Processors, Controller\nServices, or Connectors).\n\n## Unit Tests\n\nUnit tests should be used to test individual classes and methods in isolation. This often\nwill result in mocking dependency classes. However, if there already exists a Mock\nimplementation of an interface or dependency class, it is preferred to use the existing\nMock implementation. Similarly, for simple classes, it is preferable to make use of the\nreal implementation of a class rather than creating a Mock implementation. We are infinitely\nmore interested in having tests that are fast, reliable, correct, and easy to maintain than\nwe are in having tests that adhere to strict and arbitrary definitions of what constitutes\na \"unit test.\"\n\n## Integration Tests\n\nWhen working in the framework, unit tests are still important, but integration tests and\nsystem tests are often more important. Integration tests are still allowed to use mocks but\ntypically we prefer to use real implementations of classes in order to ensure a more\nrealistic and holistic test.\n\n## System Tests\n\nSystem tests live in the `nifi-system-tests` module and should be used for any changes\nthat make significant changes to the framework and the interaction between a significant\nnumber of classes. They should also be used for any changes that may be fairly isolated but\nwhich are in a critical path of the framework, especially those that affect how data is\npersisted, processed, or accessed; or those that affect how components are created,\nconfigured, scheduled, or executed.\n\nGood candidates for system tests include changes to `ProcessScheduler`, `ProcessorNode`,\n`ControllerServiceNode`, `FlowController`, `FlowManager`, how Parameters are handled, flow\nsynchronization, the repositories, etc.\n\nWhen a system test switches the client to a specific cluster node using `switchClientToNode(...)`\nand then needs to verify the state of that node specifically, the request must bypass request\nreplication by passing `NiFiSystemIT.DO_NOT_REPLICATE` to the client call. Otherwise, the request\nmay be replicated or merged across the cluster and will not reliably reflect the state of the\nselected node.\n\n## Escalation\n\nAny unit test that ends up requiring a large number of mocks is a good candidate for an\nintegration test, and any integration test that ends up requiring a large number of mocks\nis a good candidate for a system test.\n",".cursor/rules/persona.mdc":"---\ndescription: AI persona and general approach for working on the Apache NiFi codebase\nalwaysApply: true\n---\n\n# AI Persona\n\nAct as an experienced Java software engineer. When considering how to implement a task,\nfirst consider the big picture of what is being asked. Then determine which classes will\nneed to be updated.\n\nQuite often, a single request will require manipulating many different classes. Generally\nspeaking, it is best to avoid changing established interfaces, especially those in nifi-api.\nIt is acceptable when necessary, but any change in nifi-api needs to be backward compatible.\nFor example, you might introduce a new method with a default implementation, or add a new method\nand deprecate an old one without removing it.\n",".cursor/rules/testing-standards.mdc":"---\ndescription: Shared test coding conventions for all NiFi automated tests (framework and extensions)\nglobs: \"**/src/test/**/*.java\"\nalwaysApply: false\n---\n\n# Testing Standards\n\nIn addition to the general rules defined in the Code Style rule, follow these rules when\ncreating or manipulating automated tests.\n\n## Test Code Conventions\n\n1. NEVER add comments such as `// Given`, `// When`, `// Then`. These comments are\n   considered an anti-pattern and should be removed or replaced whenever they are\n   encountered. Instead, leave them out all together (preferred) or use comments that\n   clearly articulate what is happening, such as `// Setup`, `// Invoke method`,\n   `// Assert expected results`. One of the reasons that this is considered an anti-pattern\n   (in addition to the fact that the given/when/then nomenclature itself provides no\n   meaning) is that it assumes a very specific pattern in unit tests, that we will create a\n   bunch of objects, invoke the method we care about, make assertions, and then end. This\n   often results in many tests that are extremely repetitive. Instead, whenever it makes\n   sense to do so, create the prerequisite objects, invoke the method we care about with\n   appropriate arguments, make assertions, and then invoke again with a different set of\n   arguments, make assertions, etc. There is no need to have many repetitive methods that\n   each create many repetitive objects.\n\n2. Unit tests are Java. They are not English. As such, they should be written like Java.\n   Frameworks such as assertj that strive to make the unit tests look more \"English-like\"\n   should be avoided. Use of these frameworks sometimes works well but often quickly\n   devolves into automated tests that read like neither English nor Java.\n\n3. Like any other code, unit tests should be created using reusable methods where\n   appropriate. Do not create 15 methods that are all very similar and repetitive. Instead,\n   create reusable methods that can be called from each of the methods.\n\n4. Never use the `assert` keyword. Use JUnit assertions instead.\n\n5. Never create a test file that is centered around testing a method or capability. Unit\n   tests must always be named after the class they are testing. It is okay if a given unit\n   test class is very, very long.\n\n6. This is a Java project using the Maven structure. Java test files must always fall under\n   src/test/java of the appropriate sub-module.\n\n7. Never use pointless assertions such as assertDoesNotThrow. This adds nothing but\n   complexity. Just call the method, and if it throws an Exception, the test will fail. It\n   is assumed by default that each line does not throw an Exception.\n\n8. Avoid providing messages in assert statements when they do not offer benefits over the default message.\n   For example, rather than:\n```\n   assertEquals(3, processors.size(), \"Expected 3 processors in the initial flow\");\n```\n   Just use:\n```\n   assertEquals(3, processors.size());\n```\nBecause the expectation is already clear from assertion, and this message is actually harmful because it hides the size\nof the `processors` Collection.\n\n## General Testing Philosophy\n\n- Unit tests should be used to verify any sufficiently complex method in a class. We should\n  *NOT* have unit tests for trivial methods such as getters and setters, or methods that are\n  only a few lines long and are not complex. A good general rule of thumb is that if a\n  person can understand a method and verify that it is correct in a few seconds, then it is\n  not necessary to have a unit test for that method.\n\n- Avoid adding multiple tests that are redundant. For example, if you have a method that\n  takes a single `String` argument it may make sense to test with `null`, a 0-length\n  string, and a long String. But do not test with 4 different Strings each with a different\n  value but which test the same lines of code. Avoid over-testing by adding multiple tests\n  whose differences are conditions that do not truly affect the behavior of the code.\n\n- Unit tests should always focus on the \"contract\" of the method. That is, given a certain\n  input, the test should assert that it receives the correct output. The test should NOT be\n  focused on the inner details of how the method works. Focusing on the inner details of\n  how the method works is an anti-pattern because it results in tests that are more brittle,\n  more difficult to maintain, and tests that fail when the method is improved or refactored\n  but still adheres to the same contract.\n","nifi-framework-bundle/nifi-framework-extensions/nifi-py4j-framework-bundle/README.md":"# nifi-py4j-framework-bundle Documentation\n\nPath: `nifi-framework-bundle/nifi-framework-extensions/nifi-py4j-framework-bundle/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/apache/nifi/HEAD/nifi-framework-bundle/nifi-framework-extensions/nifi-py4j-framework-bundle/README.md)","nifi-registry/nifi-registry-extensions/nifi-registry-aws/nifi-registry-aws-assembly/README.md":"# nifi-registry-aws-assembly Documentation\n\nPath: `nifi-registry/nifi-registry-extensions/nifi-registry-aws/nifi-registry-aws-assembly/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/apache/nifi/HEAD/nifi-registry/nifi-registry-extensions/nifi-registry-aws/nifi-registry-aws-assembly/README.md)","nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/resources/extensions/ConsumeKafkaRecord_1_0.json":"# Extensions Skill\n\nPath: `nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/resources/extensions/ConsumeKafkaRecord_1_0.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/apache/nifi/HEAD/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/resources/extensions/ConsumeKafkaRecord_1_0.json)"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/apache/nifi/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"<!-- SPDX-License-Identifier: Apache-2.0 -->\n\n# Agent Guide for Apache NiFi\n\nThis file is read by automated agents (security scanners, code\nanalyzers, AI assistants) operating on this repository.\n\n## Security\n\nSecurity model: [SECURITY.md](./SECURITY.md)\n\nAgents that scan this repository should consult `SECURITY.md` and the\nthreat model it links before reporting issues.\n\nThe project security model is published at https://nifi.apache.org/documentation/security/#security-model linked in SECURITY.md.\n","isInternal":false,"tokens":105,"sizeBytes":503},{"name":"ConsumeKafkaRecord_1_0.json","path":"nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/resources/extensions/ConsumeKafkaRecord_1_0.json","rawUrl":"https://raw.githubusercontent.com/apache/nifi/HEAD/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/resources/extensions/ConsumeKafkaRecord_1_0.json","title":"Extensions Skill","category":"anthropic-skill","format":"json","content":"# Extensions Skill\n\nPath: `nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/resources/extensions/ConsumeKafkaRecord_1_0.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/apache/nifi/HEAD/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/resources/extensions/ConsumeKafkaRecord_1_0.json)","tokens":300,"sizeBytes":339},{"name":"best-practices.mdc","path":".cursor/rules/best-practices.mdc","rawUrl":"https://raw.githubusercontent.com/apache/nifi/HEAD/.cursor/rules/best-practices.mdc","title":"Cursor Rule: best-practices","category":"cursor-rule","format":"mdc","content":"---\ndescription: General coding best practices for maintainable NiFi changes\nalwaysApply: true\n---\n\n# Best Practices\n\n- Reuse expensive, thread-safe helpers instead of constructing them ad hoc in methods or tests.\n- For example, when a class needs an `ObjectMapper`, prefer a shared field such as `private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();` unless a special configuration requires a dedicated instance.\n","frontmatter":{"description":"General coding best practices for maintainable NiFi changes","alwaysApply":true},"isInternal":false,"tokens":84,"sizeBytes":431},{"name":"building.mdc","path":".cursor/rules/building.mdc","rawUrl":"https://raw.githubusercontent.com/apache/nifi/HEAD/.cursor/rules/building.mdc","title":"Cursor Rule: building","category":"cursor-rule","format":"mdc","content":"---\ndescription: Maven build instructions for the NiFi codebase\nalwaysApply: true\n---\n\n# Building\n\nNiFi is a complex Maven codebase. Never build code (testing or otherwise) using javac.\nAlways use `mvn` instead, or preferably the `.mvnw` wrapper script.\n\nAdditionally, building a maven module using the also-make flag (`-am`) is often very\nexpensive and slow. Instead, only build the specific module you are modifying. Assume that\nthe user has already built the entire codebase and that only the specific module you are\nmodifying needs to be built again. If this fails, you can prompt the user to build the entire\ncodebase, but only after you have attempted to build the relevant modules yourself first.\nIt is important not to run `mvn clean` at the root level or at the `nifi-assembly` level without\nthe user's express permission, as this may delete a running instance of NiFi, causing permanent\nloss of flows and configuration.\n\nWhen preparing to run system tests, packaged runtime artifacts must be refreshed using\n`mvn clean install` on the relevant packaging modules. Do not rely on `mvn install` or\n`mvn package` alone for system tests, as stale NARs or assemblies may be reused.\n\nIf a change affects code packaged into a NAR, rebuild the packaging module that delivers that\ncode before running system tests. For example, changes under\n`nifi-framework-bundle/nifi-framework/nifi-web/...` require rebuilding\n`nifi-framework-bundle/nifi-server-nar`, because the runtime uses `nifi-server-nar` to deliver\nthe updated web application classes.\n\nBefore running tests in `nifi-system-tests/nifi-system-test-suite`, refresh its runtime assembly\nwith `mvn clean install` for that module as well. The system test runtime uses\n`target/nifi-lib-assembly`, which is populated in `pre-integration-test` and later phases, so\nrunning `mvn test` or `mvn package` alone may leave stale runtime contents in place.\n","frontmatter":{"description":"Maven build instructions for the NiFi codebase","alwaysApply":true},"isInternal":false,"tokens":434,"sizeBytes":1901},{"name":"code-style.mdc","path":".cursor/rules/code-style.mdc","rawUrl":"https://raw.githubusercontent.com/apache/nifi/HEAD/.cursor/rules/code-style.mdc","title":"Cursor Rule: code-style","category":"cursor-rule","format":"mdc","content":"---\ndescription: Java code style conventions for the NiFi codebase\nglobs: \"**/*.java\"\nalwaysApply: false\n---\n\n# Code Style\n\nNiFi adheres to a few code styles that are not necessarily common. Please ensure that you\nobserve these code styles.\n\n1. Any variable that can be marked `final` must be marked `final`. This includes\n   declarations of Exceptions, method arguments, local variables, member variables, etc.\n2. Short-hand is highly discouraged in names of variables, classes, methods, etc., as well\n   as in documentation. Exceptions to this include in the framework, you may see references to\n   `procNode` for `ProcessorNode` or other such short-hand that is very difficult to confuse with\n   other terms, and it is used only when clearly defined such as `final ProcessorNode procNode = ...`.\n   Even though, however, we would not abbreviate `ControllerService` as `cs` because `cs` is too vague\n   and easily misunderstood. Instead, a value of `serviceNode` might be used.\n3. Private / helper methods should not be placed before the first public/protected method\n   that calls it.\n4. Unless the method is to be heavily reused, avoid creating trivial 1-2 line methods and\n   instead just place the code inline.\n5. Code is allowed to be up to 200 characters wide. Avoid breaking lines into many short lines.\n6. Avoid creating private methods that are called only once unless they are at least 10\n   lines long or are complex.\n7. It is never acceptable to use star imports. Import each individual class that is to be used.\n8. Never use underscores in class names, variables, or filenames.\n9. Never use System.out.println but instead use SLF4J Loggers.\n10. Avoid excessive whitespace in method invocations. For example, instead of writing:\n\n```java\nmyObject.doSomething(\n    arg1,\n    arg2,\n    arg3,\n    arg4,\n    arg5\n);\n```\n\nWrite this instead:\n\n```java\nmyObject.doSomething(arg1, arg2, arg3, arg4, arg5);\n```\n\nIt is okay to use many newlines in a builder pattern, such as:\n```java\nfinal MyObject myObject = MyObject.builder()\n    .arg1(arg1)\n    .arg2(arg2)\n    .arg3(arg3)\n    .build();\n```\n\nIt is also acceptable when chaining methods in a functional style such as:\n```java\nfinal List<String> result = myList.stream()\n    .filter(s -> s.startsWith(\"A\"))\n    .map(String::toUpperCase)\n    .toList();\n```\n\n11. When possible, prefer importing a class, rather than using fully qualified classname\n    inline in the code.\n12. Avoid statically importing methods, except in methods that are frequently used in testing\n    frameworks, such as the `Assertions` and `Mockito` classes.\n13. Avoid trailing whitespace at the end of lines, especially in blank lines.\n14. The `var` keyword is never allowed in the codebase. Always explicitly declare the type of variables.\n15. Prefer procedural code over functional code. For example, prefer using a for loop instead of a stream\n    when the logic is not simple and straightforward. The stream API is powerful but can be difficult to\n    read when overused or used in complex scenarios. Functional style is best used when the logic is simple\n    and chains together no more than 3-4 operations.\n16. Never use single-element arrays such as `final int[] counter = new int[]{0}` or\n    `final boolean[] flag = new boolean[]{false}` as a workaround for capturing a mutable primitive in a\n    lambda or anonymous class. This pattern is unidiomatic and confusing. When a lambda needs to mutate a\n    captured primitive, use the appropriate `java.util.concurrent.atomic` type (`AtomicInteger`,\n    `AtomicLong`, `AtomicBoolean`, `AtomicReference`, etc.). When a lambda needs to mutate a captured\n    object, hold the object in a final reference and mutate the object's state. The same applies to\n    `final Object[] holder = new Object[1]` for capturing a single reference; use `AtomicReference`\n    instead.\n17. Always place a blank line after a closing brace (`}`) that ends a control-flow construct (such as an\n    `if`, `else`, `for`, `while`, `switch`, or `try` / `catch` / `finally` block) when the next line is a\n    new statement or another control-flow construct at the same indentation level. This also applies to\n    the line following the closing brace of a nested block within a method. The goal is to clearly\n    separate logical sections of code. Exceptions:\n      - No blank line is required immediately before a closing brace of the enclosing block.\n      - No blank line is required between `} else {`, `} else if (...) {`, `} catch (...) {`, or\n        `} finally {` on the same chain.\n\n    Bad:\n    ```java\n    for (final Connector connector : connectors) {\n        if (connector.getCurrentState() != ConnectorState.TROUBLESHOOTING) {\n            continue;\n        }\n        final FrameworkFlowContext flowContext = connector.getActiveFlowContext();\n        if (flowContext != null && flowContext.getManagedProcessGroup().findFunnel(funnelId) != null) {\n            return true;\n        }\n    }\n    return false;\n    ```\n\n    Good:\n    ```java\n    for (final Connector connector : connectors) {\n        if (connector.getCurrentState() != ConnectorState.TROUBLESHOOTING) {\n            continue;\n        }\n\n        final FrameworkFlowContext flowContext = connector.getActiveFlowContext();\n        if (flowContext != null && flowContext.getManagedProcessGroup().findFunnel(funnelId) != null) {\n            return true;\n        }\n    }\n\n    return false;\n    ```\n18. Rule #11 (prefer importing a class rather than using a fully qualified classname inline) is not\n    optional. Even when a class is referenced only a single time, add an `import` statement rather than\n    referring to it by fully qualified name inline. The only acceptable use of a fully qualified\n    classname is when there is an unavoidable naming conflict with another imported class in the same\n    file. Fully qualified references scattered throughout code make it much harder to read and maintain.\n19. Never combine a negative condition such as `if (x != null)`, `if (!collection.isEmpty())`, or\n    `if (!flag)` with an `else` clause. Negated conditions are harder to read, and pairing them with an\n    `else` branch forces the reader to mentally invert the predicate for the \"happy path\". Instead,\n    rewrite the condition in the positive form (for example, swap the branches so the `if` tests the\n    positive condition), or use an early `return`/`continue`/`throw` to eliminate the `else` entirely.\n\n    Bad:\n    ```java\n    if (persistedManagedGroup != null) {\n        restore(persistedManagedGroup);\n    } else {\n        logger.warn(\"No snapshot was persisted; leaving Managed Process Group unchanged\");\n    }\n    ```\n\n    Good (positive predicate):\n    ```java\n    if (persistedManagedGroup == null) {\n        logger.warn(\"No snapshot was persisted; leaving Managed Process Group unchanged\");\n    } else {\n        restore(persistedManagedGroup);\n    }\n    ```\n\n    Or, preferably, use an early action and drop the `else`:\n    ```java\n    if (persistedManagedGroup == null) {\n        logger.warn(\"No snapshot was persisted; leaving Managed Process Group unchanged\");\n        return;\n    }\n\n    restore(persistedManagedGroup);\n    ```\n","frontmatter":{"description":"Java code style conventions for the NiFi codebase","globs":"**/*.java","alwaysApply":false},"isInternal":false,"tokens":1631,"sizeBytes":7168},{"name":"comments.mdc","path":".cursor/rules/comments.mdc","rawUrl":"https://raw.githubusercontent.com/apache/nifi/HEAD/.cursor/rules/comments.mdc","title":"Cursor Rule: comments","category":"cursor-rule","format":"mdc","content":"---\ndescription: Keep source and test comments durable and code-focused\nalwaysApply: true\n---\n\n# Comment Guidance\n\nWrite comments as durable explanations of the code or test contract itself.\n\n- Do not write temporary commentary such as \"without the fix\", \"with the fix\", \"before this change\", or \"after this change\".\n- Do not describe a code path relative to the current patch or bugfix timeline.\n- In tests, describe the scenario, setup, and behavior being verified, not how the test behaved before or after a fix.\n- Do not add redundant assertion messages that simply restate the assertion or expected value. Use an assertion message only when it adds unique diagnostic context that the assertion itself does not already provide.\n- If historical context matters, put it in commit messages, pull request descriptions, or issue tracking, not in source comments.\n","frontmatter":{"description":"Keep source and test comments durable and code-focused","alwaysApply":true},"isInternal":false,"tokens":170,"sizeBytes":862},{"name":"ending-conditions.mdc","path":".cursor/rules/ending-conditions.mdc","rawUrl":"https://raw.githubusercontent.com/apache/nifi/HEAD/.cursor/rules/ending-conditions.mdc","title":"Cursor Rule: ending-conditions","category":"cursor-rule","format":"mdc","content":"---\ndescription: Task completion checklist that must be verified before considering any task done\nalwaysApply: true\n---\n\n# Ending Conditions\n\nWhen you have completed a task, ensure that you have verified the following:\n\n1. All code compiles and builds successfully using `mvn`.\n2. All relevant unit tests pass successfully using `mvn`.\n3. All code adheres to the Code Style rules.\n4. Checkstyle and PMD pass successfully using\n   `mvn checkstyle:check pmd:check -T 1C` from the appropriate directory.\n5. Unit tests have been added to verify the functionality of any sufficiently complex method.\n6. A system test or an integration test has been added if the change makes significant\n   changes to the framework and the interaction between a significant number of classes.\n7. You have performed a full review of the code to ensure that there are no logical errors\n   and that the code is not duplicative or difficult to understand. If you find any code that\n   is in need of refactoring due to clarity or duplication, you should report this to the user\n   and offer to make those changes as well.\n8. If creating a new Processor or Controller Service, ensure that all relevant annotations\n   have been added, including `@Tags`, `@CapabilityDescription`, `@UseCase`, and\n   `@MultiProcessorUseCase` as appropriate.\n\n\nDo not consider the task complete until all of the above conditions have been met. When you\ndo consider the task complete, provide a summary of what you changed and which tests were\nadded or modified and what the behavior is that they verify. Additionally, provide any feedback\nabout your work that may need further review or that is not entirely complete.\n","frontmatter":{"description":"Task completion checklist that must be verified before considering any task done","alwaysApply":true},"isInternal":false,"tokens":356,"sizeBytes":1670},{"name":"extension-development.mdc","path":".cursor/rules/extension-development.mdc","rawUrl":"https://raw.githubusercontent.com/apache/nifi/HEAD/.cursor/rules/extension-development.mdc","title":"Cursor Rule: extension-development","category":"cursor-rule","format":"mdc","content":"---\ndescription: Development patterns for NiFi extensions (Processors, Controller Services, Connectors). Covers Property Descriptors, Relationships, and common patterns.\nalwaysApply: false\n---\n\n# Extension Development\n\nThis rule applies when developing NiFi extensions: Processors, Controller Services, and Connectors.\n\n## Property Descriptors\n\nProperty Descriptors are defined as `static final` fields on the component class using\n`PropertyDescriptor.Builder`.\n\n- **Naming:** Use clear, descriptive names. The `displayName` field should never be used. Make the\n  name itself clear and concise. Use Title Case for property names.\n- **Required vs. optional:** Mark properties as `.required(true)` when the component cannot\n  function without them. Prefer sensible defaults via `.defaultValue(...)` when possible.\n  When a default value is provided, the property will always have a value. The `required` flag in this\n  case is more of a documentation aid to indicate the importance of the property.\n- **Validators:** Always attach an appropriate `Validator` (e.g., `StandardValidators.NON_EMPTY_VALIDATOR`,\n  `StandardValidators.POSITIVE_INTEGER_VALIDATOR`). The Validator can be left off only when Allowable Values\n  are provided. In this case, do not include a Validator because it is redundant and confusing.\n- **Expression Language:** If a property should support Expression Language, add\n  `.expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES)` or the\n  appropriate scope. Always document when Expression Language is supported in the property\n  description. Some developers tend to go overboard here and feel like Expression Language should be supported\n  everywhere, but this is a mistake! The default assumption should be that Expression Language is not supported\n  unless the value is expected to be different for every FlowFile that is processed.\n- **Dependencies:** Use `.dependsOn(...)` to conditionally show properties based on the\n  values of other properties. This keeps the configuration UI clean and avoids exposing\n  irrelevant properties. If there is a dependency, it is important to understand that `.required(true)` means that\n  this property is required IF AND ONLY IF the dependency condition is met.\n\n## Processors\n\n- The `onTrigger` method should be focused on processing FlowFiles. Keep setup and teardown\n  logic in lifecycle methods when possible.\n- Prefer `session.read()` and `session.write()` with callbacks over directly working with\n  streams to ensure proper resource management.\n- Prefer `session.commitAsync()` over `session.commit()`. The `commit` method was the original implementation,\n  but it has now been deprecated in favor of `commitAsync`. The `commitAsync` call provide a clearer, cleaner\n  interface for handling post-commit actions including success and failure callbacks. In addition, the async\n  method allows Processors to be used much more efficiently in a Stateless NiFi flow.\n\n### Processor Lifecycle Annotations\n\n- Use `@OnScheduled` for setup that should happen once before the processor starts\n  running (e.g., creating clients, compiling patterns).\n- Use `@OnStopped` for cleanup (e.g., closing clients, releasing resources).\n- `@OnUnscheduled` is rarely used but can be used to interrupt long-running processes when the Processor is stopped.\n  Generally, though, it is preferable to write the Processor in such a way that long-running processes check `isScheduled()`\n  and stop gracefully if the return value is `false`.\n\n### Relationships\n- **Declaration**: Relationships are defined as `static final` fields using `new Relationship.Builder()`.\n  Relationship names should generally be lowercase.\n- **Success and Failure:** Most processors define at least a `success` and `failure`\n  relationship. Use `REL_SUCCESS` and `REL_FAILURE` as constant names.\n- **Original relationship:** Processors that enrich or fork FlowFiles often include an\n  `original` relationship for the unmodified input FlowFile.\n\n### Use Case Documentation\nThe `@UseCase` and `@MultiProcessorUseCase` annotations help document common usage patterns for Processors.\nThis is helpful for users to understand when and how to use the component effectively. It is equally important\nfor Agents that can determine which components should be used for a given task.\n\n- Use `@UseCase` to document common use cases for the Processor. This helps users understand\n  when and how to use the component effectively. This is unnecessary for Processors that serve a single use case\n  that is clearly described by the component name and description. For example, a Processor that consumes messages\n  from a specific service likely does not need a `@UseCase` annotation because its purpose is clear.\n- Use `@MultiProcessorUseCase` to document well-known patterns that involve multiple Processors working\n  together to achieve a common goal. Examples include List/Fetch patterns, Fork/Join patterns, etc.\n  The `@MultiProcessorUseCase` annotation should not be added to each individual Processor involved in the pattern.\n  Rather, the convention is to add the annotation to the last Processor in the flow that completes the pattern.\n  Some Processors will have one or more `@UseCase` annotations and no `@MultiProcessorUseCase` annotations,\n  while some will have one or more `@MultiProcessorUseCase` annotations and no `@UseCase` annotations.\n\n\n## Controller Services\n\nController Services are objects that can be shared across multiple components. This is typically done for\nclients that connect to external systems in order to avoid creating many connections, or in order to share\nconfiguration across multiple components without the user having to duplicate configuration. Controller Services\ncan also be helpful for abstracting away some piece of functionality into a separate extension point so that the\nimplementation can be swapped out by the user. For example, Record Readers and Writers are implemented as Controller\nServices so that the user can simply choose which format they want to read and write in a flexible and reusable way.\n\nThat said, Controller Services can be more onerous to configure and maintain for the user, so they should\nbe used sparingly and only when there is a clear benefit to doing so.\n\n### Controller Service Lifecycle Annotations\n\n- Use `@OnScheduled` for setup that should happen once before the service is enabled (e.g., creating clients, compiling patterns).\n- Use `@OnDisabled` for cleanup (e.g., closing clients, releasing resources).\n\n\n## General Patterns\n\n- Use `ComponentLog` (obtained via `getLogger()`) for all logging, not SLF4J directly.\n  This ensures log messages are associated with the component instance and that they generate Bulletins.\n- Use `@CapabilityDescription` to provide a clear and concise description of what the component does. This should not\n  be used for configuration details.\n- Use `@Tags` to provide relevant keywords that help users find the component.\n- Use `@SeeAlso` to reference related components.\n- Use `@WritesAttributes` and `@ReadsAttributes` to document which FlowFile attributes are read and written by the component.\n- Use `@DynamicProperty` to document any dynamic properties supported by the component.\n","frontmatter":{"description":"Development patterns for NiFi extensions (Processors, Controller Services, Connectors). Covers Property Descriptors, Relationships, and common patterns.","alwaysApply":false},"isInternal":false,"tokens":1457,"sizeBytes":7217},{"name":"extension-testing.mdc","path":".cursor/rules/extension-testing.mdc","rawUrl":"https://raw.githubusercontent.com/apache/nifi/HEAD/.cursor/rules/extension-testing.mdc","title":"Cursor Rule: extension-testing","category":"cursor-rule","format":"mdc","content":"---\ndescription: Testing guidance for NiFi extensions (Processors, Controller Services, Connectors). Covers nifi-mock and TestRunner usage.\nalwaysApply: false\n---\n\n# Extension Testing\n\nThis rule applies when writing tests for NiFi extensions: Processors, Controller Services, and Connectors.\n\n## Unit Tests\n\nUnit tests should be used to test individual classes and methods in isolation. This often\nwill result in mocking dependency classes. However, if there already exists a Mock\nimplementation of an interface or dependency class, it is preferred to use the existing\nMock implementation. Similarly, for simple classes, it is preferable to make use of the\nreal implementation of a class rather than creating a Mock implementation. We are infinitely\nmore interested in having tests that are fast, reliable, correct, and easy to maintain than\nwe are in having tests that adhere to strict and arbitrary definitions of what constitutes\na \"unit test.\"\n\n## Use nifi-mock\n\nTests for extensions should always make use of the `nifi-mock` mocking framework. This is\ndone through the `TestRunner` interface and its standard implementation, obtained via\n`TestRunners.newTestRunner(processor)`.\n\nThe `TestRunner` provides methods for:\n- Setting property values (`setProperty`)\n- Enqueueing FlowFiles (`enqueue`)\n- Running the processor (`run`)\n- Asserting transfer to relationships (`assertTransferCount`, `assertAllFlowFilesTransferred`)\n- Validating processor configuration (`assertValid`, `assertNotValid`)\n- Asserting content and attributes of FlowFiles (`assertContentEquals`, `assertAttributeEquals`, etc.)\n\n## No System Tests for Extensions\n\nSystem tests are not expected for extensions. Extensions are tested at the unit level using\n`nifi-mock`. The `nifi-mock` framework provides sufficient isolation and simulation of the\nNiFi runtime environment.\n\n## What to Test\n\n- **Property validation:** If the extension has a custom Validator, it\n- **customValidate:** If the extension overrides the `customValidate` method, test that it correctly\n  validates the configuration and produces appropriate validation results.\n- **Relationship routing:** Verify that FlowFiles are routed to the correct relationship\n  based on input and configuration.\n- **Content transformation:** For processors that modify FlowFile content, verify that\n  output content matches expectations.\n- **Attribute handling:** Verify that expected attributes are set on output FlowFiles.\n- **Error handling:** Verify that error conditions (bad input, misconfiguration, simulated\n  failures) are handled correctly, typically by routing to a failure relationship.\n\n## What NOT to Test\n\n- **NiFi framework behavior:** Do not attempt to test the behavior of the NiFi framework itself.\n  For example, do not test that `session.commitAsync()` actually commits a transaction. Instead,\n  focus on testing that your extension behaves correctly when `commitAsync` is called, and trust\n  that the NiFi framework will handle the commit correctly.\n- **Validator behavior:** If a custom validator is used by an extension, that custom validator should\n  be tested separate as a unit test for the validator itself. However, if the extension point provides\n  a `customValidate` method, that should absolutely be tested as part of the extension's unit tests.\n- **The PropertyDescriptors that are returned:** Do not test that the `getSupportedPropertyDescriptors`\n  method returns the expected PropertyDescriptors. This is an anti-pattern because it does not properly\n  test that the extension abides by the contract of the API. For example, if a new PropertyDescriptor is\n  added whose default is to behave the same way as the old behavior, the test should absolutely pass.\n  However, if the test is written to expect a specific set of PropertyDescriptors, then the test will fail,\n  leading to confusion and unnecessary maintenance.\n\n## Controller Service Testing\n\nWhen a processor depends on a Controller Service, use `TestRunner.addControllerService`\nand `TestRunner.enableControllerService` to wire up either a real or mock implementation\nof the service for testing.\n\n## TestContainers\n\nFor Processors that interact with external systems, it can be helpful to use TestContainers to spin up\na temporary instance of the external system for testing. This allows for more realistic integration tests\nwithout requiring the user to have the external system installed and running on their machine.\n","frontmatter":{"description":"Testing guidance for NiFi extensions (Processors, Controller Services, Connectors). Covers nifi-mock and TestRunner usage.","alwaysApply":false},"isInternal":false,"tokens":886,"sizeBytes":4438},{"name":"framework-testing.mdc","path":".cursor/rules/framework-testing.mdc","rawUrl":"https://raw.githubusercontent.com/apache/nifi/HEAD/.cursor/rules/framework-testing.mdc","title":"Cursor Rule: framework-testing","category":"cursor-rule","format":"mdc","content":"---\ndescription: Testing guidance for NiFi framework code (not extensions). Covers when to use unit, integration, and system tests for framework classes.\nalwaysApply: false\n---\n\n# Framework Testing\n\nThis rule applies when working on NiFi framework code (not Processors, Controller\nServices, or Connectors).\n\n## Unit Tests\n\nUnit tests should be used to test individual classes and methods in isolation. This often\nwill result in mocking dependency classes. However, if there already exists a Mock\nimplementation of an interface or dependency class, it is preferred to use the existing\nMock implementation. Similarly, for simple classes, it is preferable to make use of the\nreal implementation of a class rather than creating a Mock implementation. We are infinitely\nmore interested in having tests that are fast, reliable, correct, and easy to maintain than\nwe are in having tests that adhere to strict and arbitrary definitions of what constitutes\na \"unit test.\"\n\n## Integration Tests\n\nWhen working in the framework, unit tests are still important, but integration tests and\nsystem tests are often more important. Integration tests are still allowed to use mocks but\ntypically we prefer to use real implementations of classes in order to ensure a more\nrealistic and holistic test.\n\n## System Tests\n\nSystem tests live in the `nifi-system-tests` module and should be used for any changes\nthat make significant changes to the framework and the interaction between a significant\nnumber of classes. They should also be used for any changes that may be fairly isolated but\nwhich are in a critical path of the framework, especially those that affect how data is\npersisted, processed, or accessed; or those that affect how components are created,\nconfigured, scheduled, or executed.\n\nGood candidates for system tests include changes to `ProcessScheduler`, `ProcessorNode`,\n`ControllerServiceNode`, `FlowController`, `FlowManager`, how Parameters are handled, flow\nsynchronization, the repositories, etc.\n\nWhen a system test switches the client to a specific cluster node using `switchClientToNode(...)`\nand then needs to verify the state of that node specifically, the request must bypass request\nreplication by passing `NiFiSystemIT.DO_NOT_REPLICATE` to the client call. Otherwise, the request\nmay be replicated or merged across the cluster and will not reliably reflect the state of the\nselected node.\n\n## Escalation\n\nAny unit test that ends up requiring a large number of mocks is a good candidate for an\nintegration test, and any integration test that ends up requiring a large number of mocks\nis a good candidate for a system test.\n","frontmatter":{"description":"Testing guidance for NiFi framework code (not extensions). Covers when to use unit, integration, and system tests for framework classes.","alwaysApply":false},"isInternal":false,"tokens":525,"sizeBytes":2629},{"name":"persona.mdc","path":".cursor/rules/persona.mdc","rawUrl":"https://raw.githubusercontent.com/apache/nifi/HEAD/.cursor/rules/persona.mdc","title":"Cursor Rule: persona","category":"cursor-rule","format":"mdc","content":"---\ndescription: AI persona and general approach for working on the Apache NiFi codebase\nalwaysApply: true\n---\n\n# AI Persona\n\nAct as an experienced Java software engineer. When considering how to implement a task,\nfirst consider the big picture of what is being asked. Then determine which classes will\nneed to be updated.\n\nQuite often, a single request will require manipulating many different classes. Generally\nspeaking, it is best to avoid changing established interfaces, especially those in nifi-api.\nIt is acceptable when necessary, but any change in nifi-api needs to be backward compatible.\nFor example, you might introduce a new method with a default implementation, or add a new method\nand deprecate an old one without removing it.\n","frontmatter":{"description":"AI persona and general approach for working on the Apache NiFi codebase","alwaysApply":true},"isInternal":false,"tokens":150,"sizeBytes":743},{"name":"system-test-troubleshooting-archive.mdc","path":".cursor/rules/system-test-troubleshooting-archive.mdc","rawUrl":"https://raw.githubusercontent.com/apache/nifi/HEAD/.cursor/rules/system-test-troubleshooting-archive.mdc","title":"Cursor Rule: system-test-troubleshooting-archive","category":"cursor-rule","format":"mdc","content":"---\ndescription: Explains that every CI run of NiFi system tests uploads a per-test troubleshooting archive containing each cluster node's logs/ and conf/ directories, and how to download it when investigating a system test failure.\nalwaysApply: true\n---\n\n# System Test Troubleshooting Archive\n\nWhen a NiFi system test (anything under `nifi-system-tests/`) fails in CI, the\n`system-tests` workflow uploads a `*-troubleshooting-logs` artifact for each\nOS/JDK combination that ran. This archive is far more useful than the surefire/\nfailsafe console output alone and **must** be retrieved before drawing\nconclusions about a system test failure.\n\n## What the archive contains\n\nFor every failed test in that job, the archive includes:\n\n```\ntroubleshooting/\n  <TestClass>-<testMethod>/\n    node-1/\n      logs/        full nifi-app.log, nifi-bootstrap.log, etc. for that node\n      conf/        nifi.properties, flow definition, state-management.xml, etc.\n    node-2/\n      logs/\n      conf/\n    ...\n```\n\nThat is, for each failed test, the running NiFi instance(s) used by the test\nare captured with the same `logs/` and `conf/` you would inspect on a real\ndeployment. For clustered tests, every node is captured separately. The\narchive also typically contains the matching `failsafe-reports/`.\n\nThis is critical because a system test failure surfaces in CI as a single\nexception message, but the actual root cause is almost always visible only in\n`nifi-app.log` of one of the cluster nodes (cluster join issues, replication\nerrors, repository corruption, processor scheduling issues, etc.).\n\n## How to fetch the archive\n\n1. Identify the failing GitHub Actions run id. For a PR, list its checks:\n   ```\n   gh pr checks <PR_NUMBER> --repo apache/nifi\n   gh run list --repo apache/nifi --limit 30\n   ```\n2. List the artifacts attached to that run to see which OS/JDK combos are\n   available and confirm none have expired:\n   ```\n   gh api repos/apache/nifi/actions/runs/<RUN_ID>/artifacts \\\n     --jq '.artifacts[] | \"\\(.id)\\t\\(.name)\\t\\(.size_in_bytes)\\t\\(.expired)\"'\n   ```\n3. Download into a directory:\n   ```\n   gh run download <RUN_ID> --repo apache/nifi --dir <local-dir>\n   ```\n   Each `*-troubleshooting-logs` artifact becomes a subdirectory under\n   `<local-dir>`.\n4. Inspect the per-node logs for the specific failing test:\n   ```\n   <local-dir>/<os-jdk>-troubleshooting-logs/troubleshooting/<TestClass>-<testMethod>/node-1/logs/nifi-app.log\n   ```\n\nGitHub Actions artifacts expire (currently 7 days for these workflows), so\nfetch them as soon as a failure is observed; once expired they cannot be\nrecovered.\n\n## When this rule applies\n\n- Investigating any failed test under `nifi-system-tests/` in CI.\n- Triaging suspected flaky system tests across multiple PRs.\n- Reviewing a PR whose `system-tests` check has failed.\n\nIt does **not** apply to unit tests (surefire) or integration tests\n(failsafe outside `nifi-system-tests/`); those workflows do not produce a\ntroubleshooting archive, and only the surefire/failsafe console output is\navailable via `gh run view <RUN_ID> --log-failed`.\n","frontmatter":{"description":"Explains that every CI run of NiFi system tests uploads a per-test troubleshooting archive containing each cluster node's logs/ and conf/ directories, and how to download it when investigating a system test failure.","alwaysApply":true},"isInternal":false,"tokens":749,"sizeBytes":3090},{"name":"testing-standards.mdc","path":".cursor/rules/testing-standards.mdc","rawUrl":"https://raw.githubusercontent.com/apache/nifi/HEAD/.cursor/rules/testing-standards.mdc","title":"Cursor Rule: testing-standards","category":"cursor-rule","format":"mdc","content":"---\ndescription: Shared test coding conventions for all NiFi automated tests (framework and extensions)\nglobs: \"**/src/test/**/*.java\"\nalwaysApply: false\n---\n\n# Testing Standards\n\nIn addition to the general rules defined in the Code Style rule, follow these rules when\ncreating or manipulating automated tests.\n\n## Test Code Conventions\n\n1. NEVER add comments such as `// Given`, `// When`, `// Then`. These comments are\n   considered an anti-pattern and should be removed or replaced whenever they are\n   encountered. Instead, leave them out all together (preferred) or use comments that\n   clearly articulate what is happening, such as `// Setup`, `// Invoke method`,\n   `// Assert expected results`. One of the reasons that this is considered an anti-pattern\n   (in addition to the fact that the given/when/then nomenclature itself provides no\n   meaning) is that it assumes a very specific pattern in unit tests, that we will create a\n   bunch of objects, invoke the method we care about, make assertions, and then end. This\n   often results in many tests that are extremely repetitive. Instead, whenever it makes\n   sense to do so, create the prerequisite objects, invoke the method we care about with\n   appropriate arguments, make assertions, and then invoke again with a different set of\n   arguments, make assertions, etc. There is no need to have many repetitive methods that\n   each create many repetitive objects.\n\n2. Unit tests are Java. They are not English. As such, they should be written like Java.\n   Frameworks such as assertj that strive to make the unit tests look more \"English-like\"\n   should be avoided. Use of these frameworks sometimes works well but often quickly\n   devolves into automated tests that read like neither English nor Java.\n\n3. Like any other code, unit tests should be created using reusable methods where\n   appropriate. Do not create 15 methods that are all very similar and repetitive. Instead,\n   create reusable methods that can be called from each of the methods.\n\n4. Never use the `assert` keyword. Use JUnit assertions instead.\n\n5. Never create a test file that is centered around testing a method or capability. Unit\n   tests must always be named after the class they are testing. It is okay if a given unit\n   test class is very, very long.\n\n6. This is a Java project using the Maven structure. Java test files must always fall under\n   src/test/java of the appropriate sub-module.\n\n7. Never use pointless assertions such as assertDoesNotThrow. This adds nothing but\n   complexity. Just call the method, and if it throws an Exception, the test will fail. It\n   is assumed by default that each line does not throw an Exception.\n\n8. Avoid providing messages in assert statements when they do not offer benefits over the default message.\n   For example, rather than:\n```\n   assertEquals(3, processors.size(), \"Expected 3 processors in the initial flow\");\n```\n   Just use:\n```\n   assertEquals(3, processors.size());\n```\nBecause the expectation is already clear from assertion, and this message is actually harmful because it hides the size\nof the `processors` Collection.\n\n## General Testing Philosophy\n\n- Unit tests should be used to verify any sufficiently complex method in a class. We should\n  *NOT* have unit tests for trivial methods such as getters and setters, or methods that are\n  only a few lines long and are not complex. A good general rule of thumb is that if a\n  person can understand a method and verify that it is correct in a few seconds, then it is\n  not necessary to have a unit test for that method.\n\n- Avoid adding multiple tests that are redundant. For example, if you have a method that\n  takes a single `String` argument it may make sense to test with `null`, a 0-length\n  string, and a long String. But do not test with 4 different Strings each with a different\n  value but which test the same lines of code. Avoid over-testing by adding multiple tests\n  whose differences are conditions that do not truly affect the behavior of the code.\n\n- Unit tests should always focus on the \"contract\" of the method. That is, given a certain\n  input, the test should assert that it receives the correct output. The test should NOT be\n  focused on the inner details of how the method works. Focusing on the inner details of\n  how the method works is an anti-pattern because it results in tests that are more brittle,\n  more difficult to maintain, and tests that fail when the method is improved or refactored\n  but still adheres to the same contract.\n","frontmatter":{"description":"Shared test coding conventions for all NiFi automated tests (framework and extensions)","globs":"**/src/test/**/*.java","alwaysApply":false},"isInternal":false,"tokens":976,"sizeBytes":4513},{"name":"README.md","path":"nifi-framework-bundle/nifi-framework-extensions/nifi-py4j-framework-bundle/README.md","rawUrl":"https://raw.githubusercontent.com/apache/nifi/HEAD/nifi-framework-bundle/nifi-framework-extensions/nifi-py4j-framework-bundle/README.md","title":"nifi-py4j-framework-bundle Documentation","category":"plugin-manifest","format":"markdown","content":"# nifi-py4j-framework-bundle Documentation\n\nPath: `nifi-framework-bundle/nifi-framework-extensions/nifi-py4j-framework-bundle/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/apache/nifi/HEAD/nifi-framework-bundle/nifi-framework-extensions/nifi-py4j-framework-bundle/README.md)","tokens":300,"sizeBytes":303},{"name":"README.md","path":"nifi-registry/nifi-registry-extensions/nifi-registry-aws/nifi-registry-aws-assembly/README.md","rawUrl":"https://raw.githubusercontent.com/apache/nifi/HEAD/nifi-registry/nifi-registry-extensions/nifi-registry-aws/nifi-registry-aws-assembly/README.md","title":"nifi-registry-aws-assembly Documentation","category":"plugin-manifest","format":"markdown","content":"# nifi-registry-aws-assembly Documentation\n\nPath: `nifi-registry/nifi-registry-extensions/nifi-registry-aws/nifi-registry-aws-assembly/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/apache/nifi/HEAD/nifi-registry/nifi-registry-extensions/nifi-registry-aws/nifi-registry-aws-assembly/README.md)","tokens":300,"sizeBytes":321}],"systemPromptSnippet":"<yakaai_skills repo=\"apache/nifi\">\n<!-- File: AGENTS.md (Tokens: ~105 | Category: root-instruction) -->\n<!-- SPDX-License-Identifier: Apache-2.0 -->\n\n# Agent Guide for Apache NiFi\n\nThis file is read by automated agents (security scanners, code\nanalyzers, AI assistants) operating on this repository.\n\n## Security\n\nSecurity model: [SECURITY.md](./SECURITY.md)\n\nAgents that scan this repository should consult `SECURITY.md` and the\nthreat model it links before reporting issues.\n\nThe project security model is published at https://nifi.apache.org/documentation/security/#security-model linked in SECURITY.md.\n\n<!-- File: nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/resources/extensions/ConsumeKafkaRecord_1_0.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Extensions Skill\n\nPath: `nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/resources/extensions/ConsumeKafkaRecord_1_0.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/apache/nifi/HEAD/nifi-registry/nifi-registry-core/nifi-registry-framework/src/test/resources/extensions/ConsumeKafkaRecord_1_0.json)\n\n<!-- File: .cursor/rules/best-practices.mdc (Tokens: ~84 | Category: cursor-rule) -->\n---\ndescription: General coding best practices for maintainable NiFi changes\nalwaysApply: true\n---\n\n# Best Practices\n\n- Reuse expensive, thread-safe helpers instead of constructing them ad hoc in methods or tests.\n- For example, when a class needs an `ObjectMapper`, prefer a shared field such as `private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();` unless a special configuration requires a dedicated instance.\n\n<!-- File: .cursor/rules/building.mdc (Tokens: ~434 | Category: cursor-rule) -->\n---\ndescription: Maven build instructions for the NiFi codebase\nalwaysApply: true\n---\n\n# Building\n\nNiFi is a complex Maven codebase. Never build code (testing or otherwise) using javac.\nAlways use `mvn` instead, or preferably the `.mvnw` wrapper script.\n\nAdditionally, building a maven module using the also-make flag (`-am`) is often very\nexpensive and slow. Instead, only build the specific module you are modifying. Assume that\nthe user has already built the entire codebase and that only the specific module you are\nmodifying needs to be built again. If this fails, you can prompt the user to build the entire\ncodebase, but only after you have attempted to build the relevant modules yourself first.\nIt is important not to run `mvn clean` at the root level or at the `nifi-assembly` level without\nthe user's express permission, as this may delete a running instance of NiFi, causing permanent\nloss of flows and configuration.\n\nWhen preparing to run system tests, packaged runtime artifacts must be refreshed using\n`mvn clean install` on the relevant packaging modules. Do not rely on `mvn install` or\n`mvn package` alone for system tests, as stale NARs or assemblies may be reused.\n\nIf a change affects code packaged into a NAR, rebuild the packaging module that delivers that\ncode before running system tests. For example, changes under\n`nifi-framework-bundle/nifi-framework/nifi-web/...` require rebuilding\n`nifi-framework-bundle/nifi-server-nar`, because the runtime uses `nifi-server-nar` to deliver\nthe updated web application classes.\n\nBefore running tests in `nifi-system-tests/nifi-system-test-suite`, refresh its runtime assembly\nwith `mvn clean install` for that module as well. The system test runtime uses\n`target/nifi-lib-assembly`, which is populated in `pre-integration-test` and later phases, so\nrunning `mvn test` or `mvn package` alone may leave stale runtime contents in place.\n\n<!-- File: .cursor/rules/code-style.mdc (Tokens: ~1631 | Category: cursor-rule) -->\n---\ndescription: Java code style conventions for the NiFi codebase\nglobs: \"**/*.java\"\nalwaysApply: false\n---\n\n# Code Style\n\nNiFi adheres to a few code styles that are not necessarily common. Please ensure that you\nobserve these code styles.\n\n1. Any variable that can be marked `final` must be marked `final`. This includes\n   declarations of Exceptions, method arguments, local variables, member variables, etc.\n2. Short-hand is highly discouraged in names of variables, classes, methods, etc., as well\n   as in documentation. Exceptions to this include in the framework, you may see references to\n   `procNode` for `ProcessorNode` or other such short-hand that is very difficult to confuse with\n   other terms, and it is used only when clearly defined such as `final ProcessorNode procNode = ...`.\n   Even though, however, we would not abbreviate `ControllerService` as `cs` because `cs` is too vague\n   and easily misunderstood. Instead, a value of `serviceNode` might be used.\n3. Private / helper methods should not be placed before the first public/protected method\n   that calls it.\n4. Unless the method is to be heavily reused, avoid creating trivial 1-2 line methods and\n   instead just place the code inline.\n5. Code is allowed to be up to 200 characters wide. Avoid breaking lines into many short lines.\n6. Avoid creating private methods that are called only once unless they are at least 10\n   lines long or are complex.\n7. It is never acceptable to use star imports. Import each individual class that is to be used.\n8. Never use underscores in class names, variables, or filenames.\n9. Never use System.out.println but instead use SLF4J Loggers.\n10. Avoid excessive whitespace in method invocations. For example, instead of writing:\n\n```java\nmyObject.doSomething(\n    arg1,\n    arg2,\n    arg3,\n    arg4,\n    arg5\n);\n```\n\nWrite this instead:\n\n```java\nmyObject.doSomething(arg1, arg2, arg3, arg4, arg5);\n```\n\nIt is okay to use many newlines in a builder pattern, such as:\n```java\nfinal MyObject myObject = MyObject.builder()\n    .arg1(arg1)\n    .arg2(arg2)\n    .arg3(arg3)\n    .build();\n```\n\nIt is also acceptable when chaining methods in a functional style such as:\n```java\nfinal List<String> result = myList.stream()\n    .filter(s -> s.startsWith(\"A\"))\n    .map(String::toUpperCase)\n    .toList();\n```\n\n11. When possible, prefer importing a class, rather than using fully qualified classname\n    inline in the code.\n12. Avoid statically importing methods, except in methods that are frequently used in testing\n    frameworks, such as the `Assertions` and `Mockito` classes.\n13. Avoid trailing whitespace at the end of lines, especially in blank lines.\n14. The `var` keyword is never allowed in the codebase. Always explicitly declare the type of variables.\n15. Prefer procedural code over functional code. For example, prefer using a for loop instead of a stream\n    when the logic is not simple and straightforward. The stream API is powerful but can be difficult to\n    read when overused or used in complex scenarios. Functional style is best used when the logic is simple\n    and chains together no more than 3-4 operations.\n16. Never use single-element arrays such as `final int[] counter = new int[]{0}` or\n    `final boolean[] flag = new boolean[]{false}` as a workaround for capturing a mutable primitive in a\n    lambda or anonymous class. This pattern is unidiomatic and confusing. When a lambda needs to mutate a\n    captured primitive, use the appropriate `java.util.concurrent.atomic` type (`AtomicInteger`,\n    `AtomicLong`, `AtomicBoolean`, `AtomicReference`, etc.). When a lambda needs to mutate a captured\n    object, hold the object in a final reference and mutate the object's state. The same applies to\n    `final Object[] holder = new Object[1]` for capturing a single reference; use `AtomicReference`\n    instead.\n17. Always place a blank line after a closing brace (`}`) that ends a control-flow construct (such as an\n    `if`, `else`, `for`, `while`, `switch`, or `try` / `catch` / `finally` block) when the next line is a\n    new statement or another control-flow construct at the same indentation level. This also applies to\n    the line following the closing brace of a nested block within a method. The goal is to clearly\n    separate logical sections of code. Exceptions:\n      - No blank line is required immediately before a closing brace of the enclosing block.\n      - No blank line is required between `} else {`, `} else if (...) {`, `} catch (...) {`, or\n        `} finally {` on the same chain.\n\n    Bad:\n    ```java\n    for (final Connector connector : connectors) {\n        if (connector.getCurrentState() != ConnectorState.TROUBLESHOOTING) {\n            continue;\n        }\n        final FrameworkFlowContext flowContext = connector.getActiveFlowContext();\n        if (flowContext != null && flowContext.getManagedProcessGroup().findFunnel(funnelId) != null) {\n            return true;\n        }\n    }\n    return false;\n    ```\n\n    Good:\n    ```java\n    for (final Connector connector : connectors) {\n        if (connector.getCurrentState() != ConnectorState.TROUBLESHOOTING) {\n            continue;\n        }\n\n        final FrameworkFlowContext flowContext = connector.getActiveFlowContext();\n        if (flowContext != null && flowContext.getManagedProcessGroup().findFunnel(funnelId) != null) {\n            return true;\n        }\n    }\n\n    return false;\n    ```\n18. Rule #11 (prefer importing a class rather than using a fully qualified classname inline) is not\n    optional. Even when a class is referenced only a single time, add an `import` statement rather than\n    referring to it by fully qualified name inline. The only acceptable use of a fully qualified\n    classname is when there is an unavoidable naming conflict with another imported class in the same\n    file. Fully qualified references scattered throughout code make it much harder to read and maintain.\n19. Never combine a negative condition such as `if (x != null)`, `if (!collection.isEmpty())`, or\n    `if (!flag)` with an `else` clause. Negated conditions are harder to read, and pairing them with an\n    `else` branch forces the reader to mentally invert the predicate for the \"happy path\". Instead,\n    rewrite the condition in the positive form (for example, swap the branches so the `if` tests the\n    positive condition), or use an early `return`/`continue`/`throw` to eliminate the `else` entirely.\n\n    Bad:\n    ```java\n    if (persistedManagedGroup != null) {\n        restore(persistedManagedGroup);\n    } else {\n        logger.warn(\"No snapshot was persisted; leaving Managed Process Group unchanged\");\n    }\n    ```\n\n    Good (positive predicate):\n    ```java\n    if (persistedManagedGroup == null) {\n        logger.warn(\"No snapshot was persisted; leaving Managed Process Group unchanged\");\n    } else {\n        restore(persistedManagedGroup);\n    }\n    ```\n\n    Or, preferably, use an early action and drop the `else`:\n    ```java\n    if (persistedManagedGroup == null) {\n        logger.warn(\"No snapshot was persisted; leaving Managed Process Group unchanged\");\n        return;\n    }\n\n    restore(persistedManagedGroup);\n    ```\n\n<!-- File: .cursor/rules/comments.mdc (Tokens: ~170 | Category: cursor-rule) -->\n---\ndescription: Keep source and test comments durable and code-focused\nalwaysApply: true\n---\n\n# Comment Guidance\n\nWrite comments as durable explanations of the code or test contract itself.\n\n- Do not write temporary commentary such as \"without the fix\", \"with the fix\", \"before this change\", or \"after this change\".\n- Do not describe a code path relative to the current patch or bugfix timeline.\n- In tests, describe the scenario, setup, and behavior being verified, not how the test behaved before or after a fix.\n- Do not add redundant assertion messages that simply restate the assertion or expected value. Use an assertion message only when it adds unique diagnostic context that the assertion itself does not already provide.\n- If historical context matters, put it in commit messages, pull request descriptions, or issue tracking, not in source comments.\n\n<!-- File: .cursor/rules/ending-conditions.mdc (Tokens: ~356 | Category: cursor-rule) -->\n---\ndescription: Task completion checklist that must be verified before considering any task done\nalwaysApply: true\n---\n\n# Ending Conditions\n\nWhen you have completed a task, ensure that you have verified the following:\n\n1. All code compiles and builds successfully using `mvn`.\n2. All relevant unit tests pass successfully using `mvn`.\n3. All code adheres to the Code Style rules.\n4. Checkstyle and PMD pass successfully using\n   `mvn checkstyle:check pmd:check -T 1C` from the appropriate directory.\n5. Unit tests have been added to verify the functionality of any sufficiently complex method.\n6. A system test or an integration test has been added if the change makes significant\n   changes to the framework and the interaction between a significant number of classes.\n7. You have performed a full review of the code to ensure that there are no logical errors\n   and that the code is not duplicative or difficult to understand. If you find any code that\n   is in need of refactoring due to clarity or duplication, you should report this to the user\n   and offer to make those changes as well.\n8. If creating a new Processor or Controller Service, ensure that all relevant annotations\n   have been added, including `@Tags`, `@CapabilityDescription`, `@UseCase`, and\n   `@MultiProcessorUseCase` as appropriate.\n\n\nDo not consider the task complete until all of the above conditions have been met. When you\ndo consider the task complete, provide a summary of what you changed and which tests were\nadded or modified and what the behavior is that they verify. Additionally, provide any feedback\nabout your work that may need further review or that is not entirely complete.\n\n<!-- File: .cursor/rules/extension-development.mdc (Tokens: ~1457 | Category: cursor-rule) -->\n---\ndescription: Development patterns for NiFi extensions (Processors, Controller Services, Connectors). Covers Property Descriptors, Relationships, and common patterns.\nalwaysApply: false\n---\n\n# Extension Development\n\nThis rule applies when developing NiFi extensions: Processors, Controller Services, and Connectors.\n\n## Property Descriptors\n\nProperty Descriptors are defined as `static final` fields on the component class using\n`PropertyDescriptor.Builder`.\n\n- **Naming:** Use clear, descriptive names. The `displayName` field should never be used. Make the\n  name itself clear and concise. Use Title Case for property names.\n- **Required vs. optional:** Mark properties as `.required(true)` when the component cannot\n  function without them. Prefer sensible defaults via `.defaultValue(...)` when possible.\n  When a default value is provided, the property will always have a value. The `required` flag in this\n  case is more of a documentation aid to indicate the importance of the property.\n- **Validators:** Always attach an appropriate `Validator` (e.g., `StandardValidators.NON_EMPTY_VALIDATOR`,\n  `StandardValidators.POSITIVE_INTEGER_VALIDATOR`). The Validator can be left off only when Allowable Values\n  are provided. In this case, do not include a Validator because it is redundant and confusing.\n- **Expression Language:** If a property should support Expression Language, add\n  `.expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES)` or the\n  appropriate scope. Always document when Expression Language is supported in the property\n  description. Some developers tend to go overboard here and feel like Expression Language should be supported\n  everywhere, but this is a mistake! The default assumption should be that Expression Language is not supported\n  unless the value is expected to be different for every FlowFile that is processed.\n- **Dependencies:** Use `.dependsOn(...)` to conditionally show properties based on the\n  values of other properties. This keeps the configuration UI clean and avoids exposing\n  irrelevant properties. If there is a dependency, it is important to understand that `.required(true)` means that\n  this property is required IF AND ONLY IF the dependency condition is met.\n\n## Processors\n\n- The `onTrigger` method should be focused on processing FlowFiles. Keep setup and teardown\n  logic in lifecycle methods when possible.\n- Prefer `session.read()` and `session.write()` with callbacks over directly working with\n  streams to ensure proper resource management.\n- Prefer `session.commitAsync()` over `session.commit()`. The `commit` method was the original implementation,\n  but it has now been deprecated in favor of `commitAsync`. The `commitAsync` call provide a clearer, cleaner\n  interface for handling post-commit actions including success and failure callbacks. In addition, the async\n  method allows Processors to be used much more efficiently in a Stateless NiFi flow.\n\n### Processor Lifecycle Annotations\n\n- Use `@OnScheduled` for setup that should happen once before the processor starts\n  running (e.g., creating clients, compiling patterns).\n- Use `@OnStopped` for cleanup (e.g., closing clients, releasing resources).\n- `@OnUnscheduled` is rarely used but can be used to interrupt long-running processes when the Processor is stopped.\n  Generally, though, it is preferable to write the Processor in such a way that long-running processes check `isScheduled()`\n  and stop gracefully if the return value is `false`.\n\n### Relationships\n- **Declaration**: Relationships are defined as `static final` fields using `new Relationship.Builder()`.\n  Relationship names should generally be lowercase.\n- **Success and Failure:** Most processors define at least a `success` and `failure`\n  relationship. Use `REL_SUCCESS` and `REL_FAILURE` as constant names.\n- **Original relationship:** Processors that enrich or fork FlowFiles often include an\n  `original` relationship for the unmodified input FlowFile.\n\n### Use Case Documentation\nThe `@UseCase` and `@MultiProcessorUseCase` annotations help document common usage patterns for Processors.\nThis is helpful for users to understand when and how to use the component effectively. It is equally important\nfor Agents that can determine which components should be used for a given task.\n\n- Use `@UseCase` to document common use cases for the Processor. This helps users understand\n  when and how to use the component effectively. This is unnecessary for Processors that serve a single use case\n  that is clearly described by the component name and description. For example, a Processor that consumes messages\n  from a specific service likely does not need a `@UseCase` annotation because its purpose is clear.\n- Use `@MultiProcessorUseCase` to document well-known patterns that involve multiple Processors working\n  together to achieve a common goal. Examples include List/Fetch patterns, Fork/Join patterns, etc.\n  The `@MultiProcessorUseCase` annotation should not be added to each individual Processor involved in the pattern.\n  Rather, the convention is to add the annotation to the last Processor in the flow that completes the pattern.\n  Some Processors will have one or more `@UseCase` annotations and no `@MultiProcessorUseCase` annotations,\n  while some will have one or more `@MultiProcessorUseCase` annotations and no `@UseCase` annotations.\n\n\n## Controller Services\n\nController Services are objects that can be shared across multiple components. This is typically done for\nclients that connect to external systems in order to avoid creating many connections, or in order to share\nconfiguration across multiple components without the user having to duplicate configuration. Controller Services\ncan also be helpful for abstracting away some piece of functionality into a separate extension point so that the\nimplementation can be swapped out by the user. For example, Record Readers and Writers are implemented as Controller\nServices so that the user can simply choose which format they want to read and write in a flexible and reusable way.\n\nThat said, Controller Services can be more onerous to configure and maintain for the user, so they should\nbe used sparingly and only when there is a clear benefit to doing so.\n\n### Controller Service Lifecycle Annotations\n\n- Use `@OnScheduled` for setup that should happen once before the service is enabled (e.g., creating clients, compiling patterns).\n- Use `@OnDisabled` for cleanup (e.g., closing clients, releasing resources).\n\n\n## General Patterns\n\n- Use `ComponentLog` (obtained via `getLogger()`) for all logging, not SLF4J directly.\n  This ensures log messages are associated with the component instance and that they generate Bulletins.\n- Use `@CapabilityDescription` to provide a clear and concise description of what the component does. This should not\n  be used for configuration details.\n- Use `@Tags` to provide relevant keywords that help users find the component.\n- Use `@SeeAlso` to reference related components.\n- Use `@WritesAttributes` and `@ReadsAttributes` to document which FlowFile attributes are read and written by the component.\n- Use `@DynamicProperty` to document any dynamic properties supported by the component.\n\n<!-- File: .cursor/rules/extension-testing.mdc (Tokens: ~886 | Category: cursor-rule) -->\n---\ndescription: Testing guidance for NiFi extensions (Processors, Controller Services, Connectors). Covers nifi-mock and TestRunner usage.\nalwaysApply: false\n---\n\n# Extension Testing\n\nThis rule applies when writing tests for NiFi extensions: Processors, Controller Services, and Connectors.\n\n## Unit Tests\n\nUnit tests should be used to test individual classes and methods in isolation. This often\nwill result in mocking dependency classes. However, if there already exists a Mock\nimplementation of an interface or dependency class, it is preferred to use the existing\nMock implementation. Similarly, for simple classes, it is preferable to make use of the\nreal implementation of a class rather than creating a Mock implementation. We are infinitely\nmore interested in having tests that are fast, reliable, correct, and easy to maintain than\nwe are in having tests that adhere to strict and arbitrary definitions of what constitutes\na \"unit test.\"\n\n## Use nifi-mock\n\nTests for extensions should always make use of the `nifi-mock` mocking framework. This is\ndone through the `TestRunner` interface and its standard implementation, obtained via\n`TestRunners.newTestRunner(processor)`.\n\nThe `TestRunner` provides methods for:\n- Setting property values (`setProperty`)\n- Enqueueing FlowFiles (`enqueue`)\n- Running the processor (`run`)\n- Asserting transfer to relationships (`assertTransferCount`, `assertAllFlowFilesTransferred`)\n- Validating processor configuration (`assertValid`, `assertNotValid`)\n- Asserting content and attributes of FlowFiles (`assertContentEquals`, `assertAttributeEquals`, etc.)\n\n## No System Tests for Extensions\n\nSystem tests are not expected for extensions. Extensions are tested at the unit level using\n`nifi-mock`. The `nifi-mock` framework provides sufficient isolation and simulation of the\nNiFi runtime environment.\n\n## What to Test\n\n- **Property validation:** If the extension has a custom Validator, it\n- **customValidate:** If the extension overrides the `customValidate` method, test that it correctly\n  validates the configuration and produces appropriate validation results.\n- **Relationship routing:** Verify that FlowFiles are routed to the correct relationship\n  based on input and configuration.\n- **Content transformation:** For processors that modify FlowFile content, verify that\n  output content matches expectations.\n- **Attribute handling:** Verify that expected attributes are set on output FlowFiles.\n- **Error handling:** Verify that error conditions (bad input, misconfiguration, simulated\n  failures) are handled correctly, typically by routing to a failure relationship.\n\n## What NOT to Test\n\n- **NiFi framework behavior:** Do not attempt to test the behavior of the NiFi framework itself.\n  For example, do not test that `session.commitAsync()` actually commits a transaction. Instead,\n  focus on testing that your extension behaves correctly when `commitAsync` is called, and trust\n  that the NiFi framework will handle the commit correctly.\n- **Validator behavior:** If a custom validator is used by an extension, that custom validator should\n  be tested separate as a unit test for the validator itself. However, if the extension point provides\n  a `customValidate` method, that should absolutely be tested as part of the extension's unit tests.\n- **The PropertyDescriptors that are returned:** Do not test that the `getSupportedPropertyDescriptors`\n  method returns the expected PropertyDescriptors. This is an anti-pattern because it does not properly\n  test that the extension abides by the contract of the API. For example, if a new PropertyDescriptor is\n  added whose default is to behave the same way as the old behavior, the test should absolutely pass.\n  However, if the test is written to expect a specific set of PropertyDescriptors, then the test will fail,\n  leading to confusion and unnecessary maintenance.\n\n## Controller Service Testing\n\nWhen a processor depends on a Controller Service, use `TestRunner.addControllerService`\nand `TestRunner.enableControllerService` to wire up either a real or mock implementation\nof the service for testing.\n\n## TestContainers\n\nFor Processors that interact with external systems, it can be helpful to use TestContainers to spin up\na temporary instance of the external system for testing. This allows for more realistic integration tests\nwithout requiring the user to have the external system installed and running on their machine.\n\n<!-- File: .cursor/rules/framework-testing.mdc (Tokens: ~525 | Category: cursor-rule) -->\n---\ndescription: Testing guidance for NiFi framework code (not extensions). Covers when to use unit, integration, and system tests for framework classes.\nalwaysApply: false\n---\n\n# Framework Testing\n\nThis rule applies when working on NiFi framework code (not Processors, Controller\nServices, or Connectors).\n\n## Unit Tests\n\nUnit tests should be used to test individual classes and methods in isolation. This often\nwill result in mocking dependency classes. However, if there already exists a Mock\nimplementation of an interface or dependency class, it is preferred to use the existing\nMock implementation. Similarly, for simple classes, it is preferable to make use of the\nreal implementation of a class rather than creating a Mock implementation. We are infinitely\nmore interested in having tests that are fast, reliable, correct, and easy to maintain than\nwe are in having tests that adhere to strict and arbitrary definitions of what constitutes\na \"unit test.\"\n\n## Integration Tests\n\nWhen working in the framework, unit tests are still important, but integration tests and\nsystem tests are often more important. Integration tests are still allowed to use mocks but\ntypically we prefer to use real implementations of classes in order to ensure a more\nrealistic and holistic test.\n\n## System Tests\n\nSystem tests live in the `nifi-system-tests` module and should be used for any changes\nthat make significant changes to the framework and the interaction between a significant\nnumber of classes. They should also be used for any changes that may be fairly isolated but\nwhich are in a critical path of the framework, especially those that affect how data is\npersisted, processed, or accessed; or those that affect how components are created,\nconfigured, scheduled, or executed.\n\nGood candidates for system tests include changes to `ProcessScheduler`, `ProcessorNode`,\n`ControllerServiceNode`, `FlowController`, `FlowManager`, how Parameters are handled, flow\nsynchronization, the repositories, etc.\n\nWhen a system test switches the client to a specific cluster node using `switchClientToNode(...)`\nand then needs to verify the state of that node specifically, the request must bypass request\nreplication by passing `NiFiSystemIT.DO_NOT_REPLICATE` to the client call. Otherwise, the request\nmay be replicated or merged across the cluster and will not reliably reflect the state of the\nselected node.\n\n## Escalation\n\nAny unit test that ends up requiring a large number of mocks is a good candidate for an\nintegration test, and any integration test that ends up requiring a large number of mocks\nis a good candidate for a system test.\n\n<!-- File: .cursor/rules/persona.mdc (Tokens: ~150 | Category: cursor-rule) -->\n---\ndescription: AI persona and general approach for working on the Apache NiFi codebase\nalwaysApply: true\n---\n\n# AI Persona\n\nAct as an experienced Java software engineer. When considering how to implement a task,\nfirst consider the big picture of what is being asked. Then determine which classes will\nneed to be updated.\n\nQuite often, a single request will require manipulating many different classes. Generally\nspeaking, it is best to avoid changing established interfaces, especially those in nifi-api.\nIt is acceptable when necessary, but any change in nifi-api needs to be backward compatible.\nFor example, you might introduce a new method with a default implementation, or add a new method\nand deprecate an old one without removing it.\n\n<!-- File: .cursor/rules/system-test-troubleshooting-archive.mdc (Tokens: ~749 | Category: cursor-rule) -->\n---\ndescription: Explains that every CI run of NiFi system tests uploads a per-test troubleshooting archive containing each cluster node's logs/ and conf/ directories, and how to download it when investigating a system test failure.\nalwaysApply: true\n---\n\n# System Test Troubleshooting Archive\n\nWhen a NiFi system test (anything under `nifi-system-tests/`) fails in CI, the\n`system-tests` workflow uploads a `*-troubleshooting-logs` artifact for each\nOS/JDK combination that ran. This archive is far more useful than the surefire/\nfailsafe console output alone and **must** be retrieved before drawing\nconclusions about a system test failure.\n\n## What the archive contains\n\nFor every failed test in that job, the archive includes:\n\n```\ntroubleshooting/\n  <TestClass>-<testMethod>/\n    node-1/\n      logs/        full nifi-app.log, nifi-bootstrap.log, etc. for that node\n      conf/        nifi.properties, flow definition, state-management.xml, etc.\n    node-2/\n      logs/\n      conf/\n    ...\n```\n\nThat is, for each failed test, the running NiFi instance(s) used by the test\nare captured with the same `logs/` and `conf/` you would inspect on a real\ndeployment. For clustered tests, every node is captured separately. The\narchive also typically contains the matching `failsafe-reports/`.\n\nThis is critical because a system test failure surfaces in CI as a single\nexception message, but the actual root cause is almost always visible only in\n`nifi-app.log` of one of the cluster nodes (cluster join issues, replication\nerrors, repository corruption, processor scheduling issues, etc.).\n\n## How to fetch the archive\n\n1. Identify the failing GitHub Actions run id. For a PR, list its checks:\n   ```\n   gh pr checks <PR_NUMBER> --repo apache/nifi\n   gh run list --repo apache/nifi --limit 30\n   ```\n2. List the artifacts attached to that run to see which OS/JDK combos are\n   available and confirm none have expired:\n   ```\n   gh api repos/apache/nifi/actions/runs/<RUN_ID>/artifacts \\\n     --jq '.artifacts[] | \"\\(.id)\\t\\(.name)\\t\\(.size_in_bytes)\\t\\(.expired)\"'\n   ```\n3. Download into a directory:\n   ```\n   gh run download <RUN_ID> --repo apache/nifi --dir <local-dir>\n   ```\n   Each `*-troubleshooting-logs` artifact becomes a subdirectory under\n   `<local-dir>`.\n4. Inspect the per-node logs for the specific failing test:\n   ```\n   <local-dir>/<os-jdk>-troubleshooting-logs/troubleshooting/<TestClass>-<testMethod>/node-1/logs/nifi-app.log\n   ```\n\nGitHub Actions artifacts expire (currently 7 days for these workflows), so\nfetch them as soon as a failure is observed; once expired they cannot be\nrecovered.\n\n## When this rule applies\n\n- Investigating any failed test under `nifi-system-tests/` in CI.\n- Triaging suspected flaky system tests across multiple PRs.\n- Reviewing a PR whose `system-tests` check has failed.\n\nIt does **not** apply to unit tests (surefire) or integration tests\n(failsafe outside `nifi-system-tests/`); those workflows do not produce a\ntroubleshooting archive, and only the surefire/failsafe console output is\navailable via `gh run view <RUN_ID> --log-failed`.\n\n<!-- File: .cursor/rules/testing-standards.mdc (Tokens: ~976 | Category: cursor-rule) -->\n---\ndescription: Shared test coding conventions for all NiFi automated tests (framework and extensions)\nglobs: \"**/src/test/**/*.java\"\nalwaysApply: false\n---\n\n# Testing Standards\n\nIn addition to the general rules defined in the Code Style rule, follow these rules when\ncreating or manipulating automated tests.\n\n## Test Code Conventions\n\n1. NEVER add comments such as `// Given`, `// When`, `// Then`. These comments are\n   considered an anti-pattern and should be removed or replaced whenever they are\n   encountered. Instead, leave them out all together (preferred) or use comments that\n   clearly articulate what is happening, such as `// Setup`, `// Invoke method`,\n   `// Assert expected results`. One of the reasons that this is considered an anti-pattern\n   (in addition to the fact that the given/when/then nomenclature itself provides no\n   meaning) is that it assumes a very specific pattern in unit tests, that we will create a\n   bunch of objects, invoke the method we care about, make assertions, and then end. This\n   often results in many tests that are extremely repetitive. Instead, whenever it makes\n   sense to do so, create the prerequisite objects, invoke the method we care about with\n   appropriate arguments, make assertions, and then invoke again with a different set of\n   arguments, make assertions, etc. There is no need to have many repetitive methods that\n   each create many repetitive objects.\n\n2. Unit tests are Java. They are not English. As such, they should be written like Java.\n   Frameworks such as assertj that strive to make the unit tests look more \"English-like\"\n   should be avoided. Use of these frameworks sometimes works well but often quickly\n   devolves into automated tests that read like neither English nor Java.\n\n3. Like any other code, unit tests should be created using reusable methods where\n   appropriate. Do not create 15 methods that are all very similar and repetitive. Instead,\n   create reusable methods that can be called from each of the methods.\n\n4. Never use the `assert` keyword. Use JUnit assertions instead.\n\n5. Never create a test file that is centered around testing a method or capability. Unit\n   tests must always be named after the class they are testing. It is okay if a given unit\n   test class is very, very long.\n\n6. This is a Java project using the Maven structure. Java test files must always fall under\n   src/test/java of the appropriate sub-module.\n\n7. Never use pointless assertions such as assertDoesNotThrow. This adds nothing but\n   complexity. Just call the method, and if it throws an Exception, the test will fail. It\n   is assumed by default that each line does not throw an Exception.\n\n8. Avoid providing messages in assert statements when they do not offer benefits over the default message.\n   For example, rather than:\n```\n   assertEquals(3, processors.size(), \"Expected 3 processors in the initial flow\");\n```\n   Just use:\n```\n   assertEquals(3, processors.size());\n```\nBecause the expectation is already clear from assertion, and this message is actually harmful because it hides the size\nof the `processors` Collection.\n\n## General Testing Philosophy\n\n- Unit tests should be used to verify any sufficiently complex method in a class. We should\n  *NOT* have unit tests for trivial methods such as getters and setters, or methods that are\n  only a few lines long and are not complex. A good general rule of thumb is that if a\n  person can understand a method and verify that it is correct in a few seconds, then it is\n  not necessary to have a unit test for that method.\n\n- Avoid adding multiple tests that are redundant. For example, if you have a method that\n  takes a single `String` argument it may make sense to test with `null`, a 0-length\n  string, and a long String. But do not test with 4 different Strings each with a different\n  value but which test the same lines of code. Avoid over-testing by adding multiple tests\n  whose differences are conditions that do not truly affect the behavior of the code.\n\n- Unit tests should always focus on the \"contract\" of the method. That is, given a certain\n  input, the test should assert that it receives the correct output. The test should NOT be\n  focused on the inner details of how the method works. Focusing on the inner details of\n  how the method works is an anti-pattern because it results in tests that are more brittle,\n  more difficult to maintain, and tests that fail when the method is improved or refactored\n  but still adheres to the same contract.\n\n<!-- File: nifi-framework-bundle/nifi-framework-extensions/nifi-py4j-framework-bundle/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# nifi-py4j-framework-bundle Documentation\n\nPath: `nifi-framework-bundle/nifi-framework-extensions/nifi-py4j-framework-bundle/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/apache/nifi/HEAD/nifi-framework-bundle/nifi-framework-extensions/nifi-py4j-framework-bundle/README.md)\n\n<!-- File: nifi-registry/nifi-registry-extensions/nifi-registry-aws/nifi-registry-aws-assembly/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# nifi-registry-aws-assembly Documentation\n\nPath: `nifi-registry/nifi-registry-extensions/nifi-registry-aws/nifi-registry-aws-assembly/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/apache/nifi/HEAD/nifi-registry/nifi-registry-extensions/nifi-registry-aws/nifi-registry-aws-assembly/README.md)\n</yakaai_skills>"}