Repository navigation
Add the local MCP gateway for online pipeline control - #18
Conversation
✅ SonarQube quality gate: passedOpen the pull-request dashboard Posted by CI from the SonarQube pull-request quality gate. |
949e177 to
3307c87
Compare
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughThe change adds a stdio MCP sidecar, token administration, shared control-plane HTTP transport, artifact and connector validation, secret redaction, and versioned CLI bundle packaging. It also updates release workflows, installation scripts, documentation, architecture checks, and integration tests. ChangesMCP control-plane and CLI delivery
Estimated code review effort: 5 (Critical) | ~120 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 15
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
arch-tests/src/test/java/io/tapstate/archtests/CliFaceProjectionGatesTest.java (1)
70-85: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winUpdate the deferral note; the security domain now has a face.
Line 85 removed
token.create,token.revoke, andtoken.listfrom the deferral list. The javadoc at lines 72-74 still states that the security domain has no face anywhere, with no command and no endpoint. Only user administration remains deferred. The siblingControlFaceProjectionGatesTestupdated the equivalent prose. This comment is the reviewed record for each deferral, so a stale reason weakens the gate.📝 Proposed change
* The registered operations the CLI does not project, each a deliberate deferral rather than an - * oversight. The security domain has no face anywhere — no command and no endpoint — so nothing - * invokes those verbs today; the first administrator is created through the bootstrap entry point - * instead. {`@code` cluster.members} is routed over HTTP but answers 501 with no topology service + * oversight. User administration has no face anywhere — no command and no endpoint — so nothing + * invokes those verbs today; the first administrator is created through the bootstrap entry point + * instead. Machine-token administration now has both a command and an endpoint, so it is no longer + * deferred. {`@code` cluster.members} is routed over HTTP but answers 501 with no topology service * behind it, so a command for it would surface a stub rather than a cluster. The frontend-only source * and connector detail faces are HTTP contracts with no command shape yet.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@arch-tests/src/test/java/io/tapstate/archtests/CliFaceProjectionGatesTest.java` around lines 70 - 85, Update the Javadoc for DEFERRED_WITH_NO_VERB to remove the claim that the security domain has no face anywhere, and describe only the remaining deferred user-administration operations as lacking a CLI command. Keep the existing explanations for cluster.members and the frontend-only source/connector faces unchanged.
🧹 Nitpick comments (16)
distribution/cli-bundle/pom.xml (1)
47-72: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd CI coverage for
mcp-jar-fallback.The release job builds and packages only
-Pnativeand tests onlylibexec/tapstate-mcp. Build the fallback archive and run the CLI withlibexec/tapstate-mcp.jarunder Java 21.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@distribution/cli-bundle/pom.xml` around lines 47 - 72, Add CI coverage for the mcp-jar-fallback Maven profile: update the release job to build the fallback archive with -Pmcp-jar-fallback and execute the CLI using libexec/tapstate-mcp.jar under Java 21, alongside the existing native build and libexec/tapstate-mcp test.app/src/test/java/io/tapstate/app/PipelineLogAppenderTest.java (1)
94-111: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueReuse
withAppenderby adding a redactor overload.This test repeats the attach, try/finally, detach, and stop sequence that
withAppenderalready owns. An overload that accepts aSecretRedactorremoves the duplicated lifecycle code and keeps the cleanup rules in one place.♻️ Proposed refactor
- private void withAppender(RingBufferLogSink sink, String loggerName, java.util.function.Consumer<Logger> body) { + private void withAppender(RingBufferLogSink sink, String loggerName, java.util.function.Consumer<Logger> body) { + withAppender(sink, new SecretRedactor(), loggerName, body); + } + + private void withAppender(RingBufferLogSink sink, SecretRedactor redactor, String loggerName, + java.util.function.Consumer<Logger> body) { Logger logger = (Logger) LoggerFactory.getLogger(loggerName); - PipelineLogAppender appender = new PipelineLogAppender(sink, new SecretRedactor()); + PipelineLogAppender appender = new PipelineLogAppender(sink, redactor);`@Test` void redactsRegisteredSecretsBeforeWritingToTheSink() { RingBufferLogSink sink = new RingBufferLogSink(8, 8); SecretRedactor redactor = new SecretRedactor(); - redactor.replace("source-orders", java.util.List.of("sentinel-password")); - Logger logger = (Logger) LoggerFactory.getLogger("test.logs.redacted"); - PipelineLogAppender appender = new PipelineLogAppender(sink, redactor); - appender.setContext(logger.getLoggerContext()); - appender.start(); - logger.addAppender(appender); - try { - MDC.put(MDC_KEY, "orders"); - logger.warn("connector rejected {}", "sentinel-password"); - } finally { - MDC.remove(MDC_KEY); - logger.detachAppender(appender); - appender.stop(); - } + redactor.replace("source-orders", List.of("sentinel-password")); + withAppender(sink, redactor, "test.logs.redacted", logger -> { + MDC.put(MDC_KEY, "orders"); + logger.warn("connector rejected {}", "sentinel-password"); + });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/test/java/io/tapstate/app/PipelineLogAppenderTest.java` around lines 94 - 111, Refactor redactsRegisteredSecretsBeforeWritingToTheSink to use the existing withAppender helper instead of manually managing appender attachment and cleanup. Add a withAppender overload that accepts a SecretRedactor, constructs PipelineLogAppender with it, and preserves the helper’s existing lifecycle behavior.control/control-core/src/test/java/io/tapstate/control/core/TokenAdminServiceTest.java (1)
58-77: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a case for the documented idempotent revocation.
TokenAdminService.revokedocuments revocation as idempotent. No test revokes an unknown token id or revokes the same id twice. Both paths write an audit record and change no state, so the contract deserves a pinned case.💚 Proposed additional test
`@Test` void revokingAnUnknownOrAlreadyRevokedTokenStaysIdempotent() { FakeTokenStore tokens = new FakeTokenStore(); List<AuditRecord> audit = new ArrayList<>(); TokenAdminService service = service(tokens, audit::add); service.create("alice", Scope.READ); audit.clear(); service.revoke("alice", "tok-1"); service.revoke("alice", "tok-1"); service.revoke("alice", "no-such-token"); assertThat(service.list()).containsExactly(new TokenInfo("tok-1", Scope.READ, true, NOW)); assertThat(audit).hasSize(3); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@control/control-core/src/test/java/io/tapstate/control/core/TokenAdminServiceTest.java` around lines 58 - 77, Add a test alongside listIsSecretFreeAndRevokeIsAuditedAndImmediatelyAuthoritative that revokes the same token twice and then an unknown token ID, verifying the token remains revoked, no additional state changes occur, and three audit records are written.control/control-client/src/test/java/io/tapstate/control/client/HttpControlClientTest.java (1)
85-107: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd a case for a non-2xx status with a non-JSON body.
malformedJsonBodyIsReturnedAsUnreachablecovers status 200 only. A gateway answers 502 or 504 with an HTML body, and the currentdecodereturnsUnreachablefor it. Add a test that asserts the status survives asRejected. This pins the behaviour described in the comment onHttpControlClient.decode.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@control/control-client/src/test/java/io/tapstate/control/client/HttpControlClientTest.java` around lines 85 - 107, Add a test alongside malformedJsonBodyIsReturnedAsUnreachable in HttpControlClientTest that returns a non-2xx status such as 502 or 504 with a non-JSON body, then assert HttpControlClient.get produces ControlResponse.Rejected preserving that status rather than ControlResponse.Unreachable. Keep the existing generic rejection fields consistent with rejectionWithoutAStableCodeUsesTheGenericControlContract.control/control-client/src/main/java/io/tapstate/control/client/HttpControlClient.java (1)
162-169: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winShut down the
HttpClientbefore its executor.The project targets Java 21, where
HttpClient.shutdownNow()is available. Shutting downexecutordoes not terminate theHttpClientselector manager. Callclient.shutdownNow()beforeexecutor.shutdownNow().🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@control/control-client/src/main/java/io/tapstate/control/client/HttpControlClient.java` around lines 162 - 169, Update HttpControlClient.close() to call client.shutdownNow() after cancelling in-flight futures and before executor.shutdownNow(). Preserve the existing compare-and-set guard and cancellation behavior.e2e/src/test/java/io/tapstate/e2e/McpOnlineControlIT.java (2)
109-109: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueThis close is redundant.
The try-with-resources at line 73 closes
input, which wrapsprocess.getOutputStream(). Resources close before thefinallyblock runs, so this call always acts on a closed stream.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@e2e/src/test/java/io/tapstate/e2e/McpOnlineControlIT.java` at line 109, Remove the redundant process.getOutputStream().close() call from the finally block in McpOnlineControlIT, since the try-with-resources input already closes that stream before finally executes.
108-124: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winSurface the child stderr when the process fails.
This test launches a separate JVM. If that JVM fails to start or exits early,
receiveblocks for 10 seconds and then fails with a bare timeout. The reason sits in the stderr file at line 71, but no assertion message includes it.Also, line 117 asserts the exit value after a possible
destroyForcibly. A forced kill produces a non-zero exit value and masks the original cause.Attach the stderr content to the failure output.
💚 Proposed change
} finally { process.getOutputStream().close(); if (!process.waitFor(5, TimeUnit.SECONDS)) { process.destroyForcibly(); assertThat(process.waitFor(5, TimeUnit.SECONDS)).isTrue(); } server.stop(0); } - assertThat(process.exitValue()).isZero(); + assertThat(process.exitValue()) + .as("MCP process stderr:%n%s", Files.readString(stderr)) + .isZero();🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@e2e/src/test/java/io/tapstate/e2e/McpOnlineControlIT.java` around lines 108 - 124, Update the child-process lifecycle assertions around process and stderr so failures include the contents of the stderr file. Capture whether process.waitFor completes before forcibly terminating it, and report a failure with stderr when it times out instead of asserting process.exitValue() after a forced kill. Preserve the zero-exit assertion only for processes that terminate normally.control/mcp-server/src/main/resources/application.properties (1)
1-2: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDuplicate settings are fine, but keep one source of truth in mind.
spring.main.web-application-typeandspring.main.banner-moderepeat whatTapstateMcpApplication.startsets programmatically. The programmatic values win. Keep both only if the properties file must also serve directjava -jarlaunches without thestarthelper. The e2e test launches the boot JAR throughmain, which callsstart, so the properties are redundant today.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@control/mcp-server/src/main/resources/application.properties` around lines 1 - 2, Remove the redundant spring.main.web-application-type and spring.main.banner-mode entries from application.properties, since TapstateMcpApplication.start already sets them for the current launch paths.control/mcp-server/src/test/java/io/tapstate/mcp/McpOperationExecutorTest.java (2)
58-77: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse the imported
Operationtype.
Operationis imported at line 7. The fully qualifiedio.tapstate.control.core.Operationin the declaration at line 58 and the loop at line 77 adds no information.♻️ Proposed change
- List<Map.Entry<io.tapstate.control.core.Operation, Map<String, Object>>> calls = List.of( + List<Map.Entry<Operation, Map<String, Object>>> calls = List.of(- for (Map.Entry<io.tapstate.control.core.Operation, Map<String, Object>> call : calls) { + for (Map.Entry<Operation, Map<String, Object>> call : calls) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@control/mcp-server/src/test/java/io/tapstate/mcp/McpOperationExecutorTest.java` around lines 58 - 77, Replace the fully qualified io.tapstate.control.core.Operation references in the calls declaration and enhanced for-loop within McpOperationExecutorTest with the already imported Operation type, leaving the surrounding test logic unchanged.
55-56: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueState the limit clamp explicitly.
The request sets
limitto 999. The path assertion at line 90 expectslimit=200. That difference encodes a clamp contract, but the test does not say so. A reader cannot tell whether 200 is a clamp or a fixed default that discards the caller value.Add a short comment, or add a second case with a limit below the bound to prove the caller value passes through.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@control/mcp-server/src/test/java/io/tapstate/mcp/McpOperationExecutorTest.java` around lines 55 - 56, Clarify the limit-clamping contract in the test around the logs map and its path assertion: document that the requested limit of 999 is capped at 200, or add a below-bound case proving caller values are preserved. Keep the existing assertions and behavior unchanged.control/mcp-server/src/test/java/io/tapstate/mcp/McpOptionsTest.java (1)
42-51: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winCover the
--token=form.
McpOptions.parserejects both--tokenand--token=<value>at line 28 ofMcpOptions.java. This test only covers the space form. ThestartsWith("--token=")branch guards a credential-leak rule, so add a case for it.💚 Proposed addition
assertThatThrownBy(() -> McpOptions.parse( new String[] {"--token", "leak"}, Map.of("TAPSTATE_TOKEN", "token"))) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("--token"); + assertThatThrownBy(() -> McpOptions.parse( + new String[] {"--token=leak"}, Map.of("TAPSTATE_TOKEN", "token"))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("--token"); assertThatThrownBy(() -> McpOptions.parse(new String[0], Map.of()))🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@control/mcp-server/src/test/java/io/tapstate/mcp/McpOptionsTest.java` around lines 42 - 51, Extend tokenFlagAndMissingEnvironmentTokenAreRejected to call McpOptions.parse with a "--token=leak" argument and assert IllegalArgumentException with a message containing "--token", covering the startsWith("--token=") rejection branch while preserving the existing assertions.control/mcp-server/src/test/java/io/tapstate/mcp/McpEnvironmentTest.java (1)
22-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueThe test name promises null coverage that the test does not provide.
Map.ofrejects null values, so this case only covers an empty string value. Rename the test, or build the map withHashMapand add a real null entry. IfMcpEnvironmentmust tolerate null values fromSystem.getenv-like maps, the second option adds real coverage.♻️ Proposed rename
`@Test` - void emptyAndNullEnvironmentValuesAreRetainedWithoutControlVariables() { + void emptyEnvironmentValuesAreRetainedWithoutControlVariables() { McpEnvironment environment = new McpEnvironment(Map.of( "EMPTY", "", "OTHER", "value"));🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@control/mcp-server/src/test/java/io/tapstate/mcp/McpEnvironmentTest.java` around lines 22 - 29, Update emptyAndNullEnvironmentValuesAreRetainedWithoutControlVariables to either rename it so it only claims empty-value coverage, or construct the input with a nullable map implementation and add an actual null entry to verify McpEnvironment.values() retains it. Prefer the latter if McpEnvironment is expected to handle System.getenv-like maps.control/mcp-server/src/test/java/io/tapstate/mcp/McpStdioProcessTest.java (1)
26-76: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
@TempDirso the stderr file is cleaned up on assertion failure.
Files.createTempFilecreatesstderrat line 27, butFiles.deleteIfExists(stderr)runs only at line 75, after multiple assertions (lines 37-65, 73-74) that can throw. If any of them fails, the temp file is never deleted. Use a JUnit-managed@TempDirso cleanup happens regardless of test outcome.♻️ Proposed fix
class McpStdioProcessTest { `@Test` - void stdioNegotiatesListsReadToolsAndExitsAfterEofWithoutStdoutNoise() throws Exception { - Path stderr = Files.createTempFile("tapstate-mcp-", ".stderr"); + void stdioNegotiatesListsReadToolsAndExitsAfterEofWithoutStdoutNoise(`@TempDir` Path tempDir) throws Exception { + Path stderr = tempDir.resolve("tapstate-mcp.stderr"); Process process = start(stderr, unusedLoopbackPort());The last two lines (
assertThat(process.exitValue())...andFiles.deleteIfExists(stderr)) can then drop the explicit delete, since@TempDirremoves the directory afterward.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@control/mcp-server/src/test/java/io/tapstate/mcp/McpStdioProcessTest.java` around lines 26 - 76, Update stdioNegotiatesListsReadToolsAndExitsAfterEofWithoutStdoutNoise to use a JUnit `@TempDir` directory for the stderr file instead of Files.createTempFile. Store the stderr file beneath that managed directory, remove the explicit Files.deleteIfExists cleanup, and retain the existing process and assertion behavior.cli/src/main/java/io/tapstate/cli/HttpControlPlaneClient.java (1)
60-93: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winClose the lazily-built
httpClienttoo, not justsharedClient.
close()releasessharedClientbut never touches the pre-existinghttpClientfield used bylogin,apply,get,list,test,testResult,discoverSchema,schema,register,connectorList,lifecycle,status,metrics,snapshot,logs, and the websocketstream()helper. Since Java 21,java.net.http.HttpClientimplementsAutoCloseableand exposesclose()/shutdown()precisely so callers are not left relying on GC to reclaim the selector thread and pooled connections. AddingAutoCloseableto this class now promises deterministic cleanup; closing only half of its two underlying HTTP clients does not fully honor that promise, particularly for longREPLsessions that also open websocket streams.♻️ Proposed fix
`@Override` public void close() { sharedClient.close(); + if (httpClient != null) { + httpClient.close(); + } }Confirm the project's declared Java target for the
climodule supportsHttpClient.close()(Java 21+); the pattern-matchingswitchalready used intokenCreate/tokenList/tokenRevokesuggests it does.Also applies to: 183-186
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cli/src/main/java/io/tapstate/cli/HttpControlPlaneClient.java` around lines 60 - 93, Update HttpControlPlaneClient.close() to deterministically close the lazily initialized httpClient as well as sharedClient, guarding against it never being created; first verify the cli module targets Java 21 or newer so HttpClient.close() is available, and preserve cleanup for long-lived sessions and stream() resources.cli/src/main/java/io/tapstate/cli/RemoteCreatedToken.java (1)
3-5: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winMask the bearer value in
toString().The record's generated
toString()printstokenin clear text. Any future log line, assertion message, or exception that includes this record leaks the bearer value.McpOptions.toString()in this same change already masks its token, so apply the same rule here.🔒️ Proposed change
record RemoteCreatedToken(String tokenId, String scope, String token, String createdAt) { + + `@Override` + public String toString() { + return "RemoteCreatedToken[tokenId=" + tokenId + ", scope=" + scope + + ", token=********, createdAt=" + createdAt + "]"; + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cli/src/main/java/io/tapstate/cli/RemoteCreatedToken.java` around lines 3 - 5, Override RemoteCreatedToken.toString() to mask the token field using the same masking behavior as McpOptions.toString(), while retaining the other record components in the representation and ensuring the bearer value is never emitted in clear text.cli/src/main/java/io/tapstate/cli/McpLauncher.java (1)
74-77: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winReuse
executablefor the native sidecar so Windows resolution matches the Java lookup.
executable(Path, String)already handles the.exesuffix, but the native sidecar probe only triestapstate-mcp. On Windows a bundledtapstate-mcp.exeis not found, and the launcher falls back to the JAR path, which then requires a separate Java 21 runtime.♻️ Proposed refactor
Path libexec = bin.resolve("../libexec").normalize(); - Path nativeSidecar = libexec.resolve("tapstate-mcp"); + Path nativeSidecar = executable(libexec, "tapstate-mcp"); List<String> command = new ArrayList<>(); - if (Files.isRegularFile(nativeSidecar) && Files.isExecutable(nativeSidecar)) { + if (nativeSidecar != null) { command.add(nativeSidecar.toString()); } else {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cli/src/main/java/io/tapstate/cli/McpLauncher.java` around lines 74 - 77, Update the native sidecar lookup in McpLauncher to resolve the candidate through the existing executable(Path, String) helper using the tapstate-mcp name, then use that resolved Path for the regular-file and executable checks and command construction. Preserve the existing JAR fallback when no executable is found.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src/main/java/io/tapstate/app/SecretTrackingArtifactStore.java`:
- Around line 90-105: Bound the catalog-miss fallback in secretValues so it no
longer passes every scalar from source.config() to redaction. Replace
scalarValues(source.config()) with a restricted fallback that collects only
values under secret-like keys and excludes trivially short values, reusing
existing traversal helpers where possible; preserve catalog-based secret field
handling unchanged.
In `@cli/native-smoke.sh`:
- Around line 84-87: Wrap the os.write(fd, data) call in the ready/input_sent
branch with OSError handling, matching the existing os.read handling. Treat the
pty-closed EIO case as a normal child-exit race so the harness continues to
report the actual test result instead of terminating with a traceback.
In `@cli/src/main/java/io/tapstate/cli/Repl.java`:
- Around line 498-508: The TEXT branch of renderTokens should print a benign
empty-state message when tokens is empty, matching the existing behavior of
lsOnline and renderConnectors. Add this check before iterating tokens, flush the
output, and return; preserve the current token-row rendering for non-empty
lists.
In
`@control/control-client/src/main/java/io/tapstate/control/client/HttpControlClient.java`:
- Around line 112-131: Update HttpControlClient.decode so malformed response
bodies return Unreachable only for 2xx responses; for non-2xx statuses, continue
with body set to null and return Rejected carrying the HTTP status. Add coverage
in HttpControlClientTest for a non-2xx response with a non-JSON body, asserting
the Rejected outcome preserves that status. Apply changes at
control/control-client/src/main/java/io/tapstate/control/client/HttpControlClient.java:112-131
and
control/control-client/src/test/java/io/tapstate/control/client/HttpControlClientTest.java:85-107.
In
`@control/control-core/src/main/java/io/tapstate/control/core/ControlOperations.java`:
- Around line 55-66: Update the implementations backing
ControlOperations.CONNECTION_TEST and CONNECTION_DISCOVER_SCHEMA to enforce an
egress allow-list or equivalent network isolation before invoking connectors
with caller-supplied settings. Reject loopback, private, link-local, metadata,
and other disallowed destinations at runtime while preserving the existing
persisted-result behavior for permitted targets.
In
`@control/control-core/src/main/java/io/tapstate/control/core/SourceService.java`:
- Around line 68-69: Use one TapstateCatalog snapshot for each source mutation
validation: in SourceService.java lines 68-69 and 104-105, resolve catalog.get()
once before Workspace.of and pass that same snapshot to
CapabilityRules.validateOnline in both create and replace flows.
In `@control/mcp-server/mcp-smoke.sh`:
- Around line 51-58: Update receive() so each process.stdout.readline() call is
bounded by the smoke test’s response deadline, preventing an unresponsive
sidecar from blocking indefinitely before the existing timeout handling.
Preserve the current EOF and JSON decoding errors, and reuse the established
timeout mechanism rather than adding an unrelated global timeout.
In `@control/mcp-server/src/main/java/io/tapstate/mcp/McpOperationExecutor.java`:
- Around line 36-67: Update McpOperationExecutor.execute so the
unsupported-operation default path is converted into a coded McpResult rather
than escaping as an uncaught IllegalArgumentException. Either catch and map that
exception alongside TapstateException, or replace the default throw with a
TapstateException using a dedicated McpError code, while preserving existing
structured handling for all other operation failures.
In
`@control/mcp-server/src/test/java/io/tapstate/mcp/McpOperationExecutorTest.java`:
- Around line 192-207: Update
unreachableServerIsReturnedAsStructuredCodedFailure to use the existing
deterministic http://127.0.0.1:1 endpoint instead of allocating and closing a
ServerSocket, and remove the now-unused ServerSocket import.
In
`@control/mcp-server/src/test/java/io/tapstate/mcp/TapstateMcpApplicationTest.java`:
- Around line 36-46: Update
startsAsAStdioOnlyApplicationAndRegistersTheConfiguredToolSurface to set
spring.ai.mcp.server.stdio to false for the in-process application context,
while leaving McpStdioProcessTest responsible for real stdio transport coverage.
In
`@control/rest-api/src/main/java/io/tapstate/control/restapi/ArtifactController.java`:
- Around line 50-53: Update ArtifactController request extraction to validate
every ArtifactDraft entry and its required content before calling ApplyService,
rejecting null or incomplete drafts as control.malformed-request. Introduce a
shared extraction helper and use it from both apply and validate, then add MVC
coverage for null array entries and null draft content.
In
`@control/rest-api/src/main/java/io/tapstate/control/restapi/PipelineLogsController.java`:
- Around line 30-33: Update the PipelineLogsController method to validate limit
values before calling PipelineLogQueryService: preserve the existing behavior
for null or positive limits, and map zero or negative limits to the documented
client-error response instead of allowing IllegalArgumentException to produce
HTTP 500. Add API tests covering both limit=0 and a negative limit.
In `@control/rest-api/src/test/java/io/tapstate/control/restapi/AuthTest.java`:
- Around line 219-223: Update the token-listing assertions in AuthTest so they
verify that the stored secret hash value produced by FakeTokenSecrets, such as
hash:sec-N for the created token, is absent from the response, in addition to
checking the secretHash field name. Keep the existing token and WRITE assertions
unchanged.
In
`@core/core-catalog/src/main/java/io/tapstate/core/catalog/SpecNormalizer.java`:
- Around line 20-22: Update the operator tracking in the SELF_VALUE
normalization logic around the method covering lines 145–165 to record both
equality and inequality operators. Return null as soon as an operator of the
opposite type appears, regardless of order, while preserving existing handling
for expressions containing only one operator type.
In `@install/install.sh`:
- Around line 211-219: Update the installation flow around the final directory
move and tapstate symlink creation to keep the stable entry resolvable during
same-version reinstalls. Install the staged bundle into an immutable directory,
then atomically replace the versions/$version indirection symlink, or retain an
existing complete version directory instead of removing it before moving the
staged bundle.
---
Outside diff comments:
In
`@arch-tests/src/test/java/io/tapstate/archtests/CliFaceProjectionGatesTest.java`:
- Around line 70-85: Update the Javadoc for DEFERRED_WITH_NO_VERB to remove the
claim that the security domain has no face anywhere, and describe only the
remaining deferred user-administration operations as lacking a CLI command. Keep
the existing explanations for cluster.members and the frontend-only
source/connector faces unchanged.
---
Nitpick comments:
In `@app/src/test/java/io/tapstate/app/PipelineLogAppenderTest.java`:
- Around line 94-111: Refactor redactsRegisteredSecretsBeforeWritingToTheSink to
use the existing withAppender helper instead of manually managing appender
attachment and cleanup. Add a withAppender overload that accepts a
SecretRedactor, constructs PipelineLogAppender with it, and preserves the
helper’s existing lifecycle behavior.
In `@cli/src/main/java/io/tapstate/cli/HttpControlPlaneClient.java`:
- Around line 60-93: Update HttpControlPlaneClient.close() to deterministically
close the lazily initialized httpClient as well as sharedClient, guarding
against it never being created; first verify the cli module targets Java 21 or
newer so HttpClient.close() is available, and preserve cleanup for long-lived
sessions and stream() resources.
In `@cli/src/main/java/io/tapstate/cli/McpLauncher.java`:
- Around line 74-77: Update the native sidecar lookup in McpLauncher to resolve
the candidate through the existing executable(Path, String) helper using the
tapstate-mcp name, then use that resolved Path for the regular-file and
executable checks and command construction. Preserve the existing JAR fallback
when no executable is found.
In `@cli/src/main/java/io/tapstate/cli/RemoteCreatedToken.java`:
- Around line 3-5: Override RemoteCreatedToken.toString() to mask the token
field using the same masking behavior as McpOptions.toString(), while retaining
the other record components in the representation and ensuring the bearer value
is never emitted in clear text.
In
`@control/control-client/src/main/java/io/tapstate/control/client/HttpControlClient.java`:
- Around line 162-169: Update HttpControlClient.close() to call
client.shutdownNow() after cancelling in-flight futures and before
executor.shutdownNow(). Preserve the existing compare-and-set guard and
cancellation behavior.
In
`@control/control-client/src/test/java/io/tapstate/control/client/HttpControlClientTest.java`:
- Around line 85-107: Add a test alongside
malformedJsonBodyIsReturnedAsUnreachable in HttpControlClientTest that returns a
non-2xx status such as 502 or 504 with a non-JSON body, then assert
HttpControlClient.get produces ControlResponse.Rejected preserving that status
rather than ControlResponse.Unreachable. Keep the existing generic rejection
fields consistent with rejectionWithoutAStableCodeUsesTheGenericControlContract.
In
`@control/control-core/src/test/java/io/tapstate/control/core/TokenAdminServiceTest.java`:
- Around line 58-77: Add a test alongside
listIsSecretFreeAndRevokeIsAuditedAndImmediatelyAuthoritative that revokes the
same token twice and then an unknown token ID, verifying the token remains
revoked, no additional state changes occur, and three audit records are written.
In `@control/mcp-server/src/main/resources/application.properties`:
- Around line 1-2: Remove the redundant spring.main.web-application-type and
spring.main.banner-mode entries from application.properties, since
TapstateMcpApplication.start already sets them for the current launch paths.
In `@control/mcp-server/src/test/java/io/tapstate/mcp/McpEnvironmentTest.java`:
- Around line 22-29: Update
emptyAndNullEnvironmentValuesAreRetainedWithoutControlVariables to either rename
it so it only claims empty-value coverage, or construct the input with a
nullable map implementation and add an actual null entry to verify
McpEnvironment.values() retains it. Prefer the latter if McpEnvironment is
expected to handle System.getenv-like maps.
In
`@control/mcp-server/src/test/java/io/tapstate/mcp/McpOperationExecutorTest.java`:
- Around line 58-77: Replace the fully qualified
io.tapstate.control.core.Operation references in the calls declaration and
enhanced for-loop within McpOperationExecutorTest with the already imported
Operation type, leaving the surrounding test logic unchanged.
- Around line 55-56: Clarify the limit-clamping contract in the test around the
logs map and its path assertion: document that the requested limit of 999 is
capped at 200, or add a below-bound case proving caller values are preserved.
Keep the existing assertions and behavior unchanged.
In `@control/mcp-server/src/test/java/io/tapstate/mcp/McpOptionsTest.java`:
- Around line 42-51: Extend tokenFlagAndMissingEnvironmentTokenAreRejected to
call McpOptions.parse with a "--token=leak" argument and assert
IllegalArgumentException with a message containing "--token", covering the
startsWith("--token=") rejection branch while preserving the existing
assertions.
In `@control/mcp-server/src/test/java/io/tapstate/mcp/McpStdioProcessTest.java`:
- Around line 26-76: Update
stdioNegotiatesListsReadToolsAndExitsAfterEofWithoutStdoutNoise to use a JUnit
`@TempDir` directory for the stderr file instead of Files.createTempFile. Store
the stderr file beneath that managed directory, remove the explicit
Files.deleteIfExists cleanup, and retain the existing process and assertion
behavior.
In `@distribution/cli-bundle/pom.xml`:
- Around line 47-72: Add CI coverage for the mcp-jar-fallback Maven profile:
update the release job to build the fallback archive with -Pmcp-jar-fallback and
execute the CLI using libexec/tapstate-mcp.jar under Java 21, alongside the
existing native build and libexec/tapstate-mcp test.
In `@e2e/src/test/java/io/tapstate/e2e/McpOnlineControlIT.java`:
- Line 109: Remove the redundant process.getOutputStream().close() call from the
finally block in McpOnlineControlIT, since the try-with-resources input already
closes that stream before finally executes.
- Around line 108-124: Update the child-process lifecycle assertions around
process and stderr so failures include the contents of the stderr file. Capture
whether process.waitFor completes before forcibly terminating it, and report a
failure with stderr when it times out instead of asserting process.exitValue()
after a forced kill. Preserve the zero-exit assertion only for processes that
terminate normally.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 0de9f3ba-4af2-4a90-bd1f-d52117aa1819
📒 Files selected for processing (119)
.github/workflows/release.ymlREADME.mdapp/src/main/java/io/tapstate/app/ControlPlaneConfiguration.javaapp/src/main/java/io/tapstate/app/PipelineLogAppender.javaapp/src/main/java/io/tapstate/app/PipelineLogCapture.javaapp/src/main/java/io/tapstate/app/SecretTrackingArtifactStore.javaapp/src/test/java/io/tapstate/app/ControlPlaneAssemblyIT.javaapp/src/test/java/io/tapstate/app/ConvergenceDriverTest.javaapp/src/test/java/io/tapstate/app/PipelineLogAppenderTest.javaapp/src/test/java/io/tapstate/app/PipelineLogCaptureTest.javaapp/src/test/java/io/tapstate/app/SecretTrackingArtifactStoreTest.javaarch-tests/pom.xmlarch-tests/src/test/java/io/tapstate/archtests/CliFaceProjectionGatesTest.javaarch-tests/src/test/java/io/tapstate/archtests/ControlFaceProjectionGatesTest.javaarch-tests/src/test/java/io/tapstate/archtests/RingDependencyRulesTest.javaarch-tests/src/test/resources/error-codes.goldenbom/pom.xmlcli/native-smoke.shcli/pom.xmlcli/src/main/java/io/tapstate/cli/Cli.javacli/src/main/java/io/tapstate/cli/CliError.javacli/src/main/java/io/tapstate/cli/ControlPlaneClient.javacli/src/main/java/io/tapstate/cli/HttpControlPlaneClient.javacli/src/main/java/io/tapstate/cli/McpCmd.javacli/src/main/java/io/tapstate/cli/McpLauncher.javacli/src/main/java/io/tapstate/cli/RemoteCreatedToken.javacli/src/main/java/io/tapstate/cli/RemoteToken.javacli/src/main/java/io/tapstate/cli/Repl.javacli/src/main/java/io/tapstate/cli/TokenCreateOutcome.javacli/src/main/java/io/tapstate/cli/TokenListOutcome.javacli/src/main/java/io/tapstate/cli/TokenRevokeOutcome.javacli/src/test/java/io/tapstate/cli/CliTest.javacli/src/test/java/io/tapstate/cli/HttpControlPlaneClientTest.javacli/src/test/java/io/tapstate/cli/McpLauncherTest.javacli/src/test/java/io/tapstate/cli/ReplTest.javacontrol/control-client/pom.xmlcontrol/control-client/src/main/java/io/tapstate/control/client/ControlResponse.javacontrol/control-client/src/main/java/io/tapstate/control/client/HttpControlClient.javacontrol/control-client/src/main/java/io/tapstate/control/client/RequestBudget.javacontrol/control-client/src/test/java/io/tapstate/control/client/HttpControlClientTest.javacontrol/control-core/src/main/java/io/tapstate/control/core/ApplyService.javacontrol/control-core/src/main/java/io/tapstate/control/core/ArtifactValidationResult.javacontrol/control-core/src/main/java/io/tapstate/control/core/ConnectionTestService.javacontrol/control-core/src/main/java/io/tapstate/control/core/ConnectorConfigValidator.javacontrol/control-core/src/main/java/io/tapstate/control/core/ControlApiSchema.javacontrol/control-core/src/main/java/io/tapstate/control/core/ControlError.javacontrol/control-core/src/main/java/io/tapstate/control/core/ControlOperations.javacontrol/control-core/src/main/java/io/tapstate/control/core/CreatedToken.javacontrol/control-core/src/main/java/io/tapstate/control/core/Operation.javacontrol/control-core/src/main/java/io/tapstate/control/core/PipelineLogQueryService.javacontrol/control-core/src/main/java/io/tapstate/control/core/SchemaDiscoveryService.javacontrol/control-core/src/main/java/io/tapstate/control/core/SchemaRef.javacontrol/control-core/src/main/java/io/tapstate/control/core/SourceService.javacontrol/control-core/src/main/java/io/tapstate/control/core/TokenAdminService.javacontrol/control-core/src/main/java/io/tapstate/control/core/TokenService.javacontrol/control-core/src/main/java/io/tapstate/control/core/ValidationDiagnostic.javacontrol/control-core/src/test/java/io/tapstate/control/core/ApplyServiceTest.javacontrol/control-core/src/test/java/io/tapstate/control/core/AuditedSourceServiceTest.javacontrol/control-core/src/test/java/io/tapstate/control/core/ConnectorConfigValidatorTest.javacontrol/control-core/src/test/java/io/tapstate/control/core/ControlApiSchemaTest.javacontrol/control-core/src/test/java/io/tapstate/control/core/ControlOperationsTest.javacontrol/control-core/src/test/java/io/tapstate/control/core/PipelineLogQueryServiceTest.javacontrol/control-core/src/test/java/io/tapstate/control/core/SourceServiceTest.javacontrol/control-core/src/test/java/io/tapstate/control/core/TokenAdminServiceTest.javacontrol/mcp-server/mcp-smoke.shcontrol/mcp-server/pom.xmlcontrol/mcp-server/src/main/java/io/tapstate/mcp/EnvironmentExpander.javacontrol/mcp-server/src/main/java/io/tapstate/mcp/McpEnvironment.javacontrol/mcp-server/src/main/java/io/tapstate/mcp/McpError.javacontrol/mcp-server/src/main/java/io/tapstate/mcp/McpOperationExecutor.javacontrol/mcp-server/src/main/java/io/tapstate/mcp/McpOptions.javacontrol/mcp-server/src/main/java/io/tapstate/mcp/McpResult.javacontrol/mcp-server/src/main/java/io/tapstate/mcp/McpToolCatalog.javacontrol/mcp-server/src/main/java/io/tapstate/mcp/TapstateMcpApplication.javacontrol/mcp-server/src/main/resources/application.propertiescontrol/mcp-server/src/main/resources/logback-spring.xmlcontrol/mcp-server/src/test/java/io/tapstate/mcp/EnvironmentExpanderTest.javacontrol/mcp-server/src/test/java/io/tapstate/mcp/McpEnvironmentTest.javacontrol/mcp-server/src/test/java/io/tapstate/mcp/McpErrorTest.javacontrol/mcp-server/src/test/java/io/tapstate/mcp/McpOperationExecutorTest.javacontrol/mcp-server/src/test/java/io/tapstate/mcp/McpOptionsTest.javacontrol/mcp-server/src/test/java/io/tapstate/mcp/McpResultTest.javacontrol/mcp-server/src/test/java/io/tapstate/mcp/McpStdioProcessTest.javacontrol/mcp-server/src/test/java/io/tapstate/mcp/McpToolCatalogTest.javacontrol/mcp-server/src/test/java/io/tapstate/mcp/TapstateMcpApplicationTest.javacontrol/pom.xmlcontrol/rest-api/src/main/java/io/tapstate/control/restapi/ArtifactController.javacontrol/rest-api/src/main/java/io/tapstate/control/restapi/ControlHttpFace.javacontrol/rest-api/src/main/java/io/tapstate/control/restapi/PipelineLogsController.javacontrol/rest-api/src/main/java/io/tapstate/control/restapi/TokenController.javacontrol/rest-api/src/main/java/io/tapstate/control/restapi/TokenCreateRequest.javacontrol/rest-api/src/main/java/io/tapstate/control/restapi/TokenList.javacontrol/rest-api/src/test/java/io/tapstate/control/restapi/AuthTest.javacontrol/rest-api/src/test/java/io/tapstate/control/restapi/ControlApiTest.javacontrol/rest-api/src/test/java/io/tapstate/control/restapi/PipelineLogsApiTest.javacontrol/rest-api/src/test/java/io/tapstate/control/restapi/SourceApiTest.javacore/core-catalog/src/main/java/io/tapstate/core/catalog/SpecNormalizer.javacore/core-catalog/src/main/resources/catalog/mongodb.jsoncore/core-catalog/src/test/java/io/tapstate/core/catalog/SpecNormalizerTest.javacore/core-common/src/main/java/io/tapstate/core/common/Domain.javacore/core-common/src/test/java/io/tapstate/core/common/DomainTest.javacore/core-dsl/src/main/java/io/tapstate/core/dsl/CapabilityRules.javacore/core-dsl/src/main/java/io/tapstate/core/dsl/DslError.javacore/core-dsl/src/test/java/io/tapstate/core/dsl/CapabilityRulesTest.javacore/core-dsl/src/test/java/io/tapstate/core/dsl/CorpusSemanticGateTest.javacore/core-dsl/src/test/java/io/tapstate/core/dsl/DslErrorTest.javacore/core-logging/src/main/java/io/tapstate/core/logging/SecretRedactor.javacore/core-logging/src/test/java/io/tapstate/core/logging/SecretRedactorTest.javadeploy/quickstart/quickstart-smoke.shdistribution/cli-bundle/pom.xmldistribution/cli-bundle/src/assembly/jar-fallback.xmldistribution/cli-bundle/src/assembly/native.xmldocs/quickstart-online.mde2e/pom.xmle2e/src/test/java/io/tapstate/e2e/McpOnlineControlIT.javainstall/install-smoke.shinstall/install.shmessages/src/main/resources/messages/en.ymlpom.xml
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
control/control-client/src/test/java/io/tapstate/control/client/HttpControlClientTest.java (1)
35-57: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winAssert the HTTP method and request path.
The handler records only the authorization header and parsed body. A regression that sends a different method or uses the wrong path would still pass. Capture and assert
POSTand/api/tokens.Suggested assertions
+ AtomicReference<String> method = new AtomicReference<>(); + AtomicReference<String> path = new AtomicReference<>(); HttpServer server = server(exchange -> { + method.set(exchange.getRequestMethod()); + path.set(exchange.getRequestURI().getPath()); authorization.set(exchange.getRequestHeaders().getFirst("Authorization")); body.set(JsonReader.parse(...)); ... + assertThat(method.get()).isEqualTo("POST"); + assertThat(path.get()).isEqualTo("/api/tokens");🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@control/control-client/src/test/java/io/tapstate/control/client/HttpControlClientTest.java` around lines 35 - 57, Update the postSendsBearerAndJsonAndReturnsTheDecodedTree test and its server handler to capture the incoming HTTP method and request path from the exchange, then assert they are POST and /api/tokens respectively alongside the existing authorization and body assertions.control/mcp-server/src/test/java/io/tapstate/mcp/McpOperationExecutorTest.java (2)
33-45: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winAssert the complete HTTP request sequence.
The test records only the URI and uses
contains(...). It does not verify the HTTP method, request count, or order.source.createuses a connector-specGETfollowed by a sourcePOST, while both paths overlap with other operations. A missing write or incorrect verb can therefore pass.Record the method with the URI and assert the full sequence with
containsExactly(...), including both requests fromsource.create.Proposed assertion fix
- paths.add(exchange.getRequestURI().toString()); + paths.add(exchange.getRequestMethod() + " " + exchange.getRequestURI()); - assertThat(paths).contains( + assertThat(paths).containsExactly( + "GET /api/connectors", + "GET /api/connectors/mysql", + "GET /api/sources", + "GET /api/sources/orders", + "GET /api/connectors/mysql", + "POST /api/sources", ...Also applies to: 82-89
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@control/mcp-server/src/test/java/io/tapstate/mcp/McpOperationExecutorTest.java` around lines 33 - 45, Update the request recording and assertions in McpOperationExecutorTest to capture each HTTP method together with its URI, then replace partial contains checks with an ordered containsExactly assertion. Verify the complete sequence, including the connector-spec GET followed by the source POST, while preserving the existing server behavior.
130-133: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick winAssert the Bearer token on every request.
authorizationis overwritten for each exchange. Sincesource.createmakes multiple requests, line 160 checks only the final request. A missingAuthorizationheader on the connector-spec request would pass.Store every header value and assert that each one equals
Bearer machine-token.Also applies to: 158-162
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@control/mcp-server/src/test/java/io/tapstate/mcp/McpOperationExecutorTest.java` around lines 130 - 133, Update the test server handler around authorization and the assertions near source.create to collect every request’s Authorization header instead of overwriting a single AtomicReference. Assert that each captured header equals “Bearer machine-token,” ensuring all requests, including the connector-spec request, are validated.
🧹 Nitpick comments (2)
control/control-client/src/test/java/io/tapstate/control/client/HttpControlClientTest.java (2)
25-33: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd negative-duration cases to the constructor test.
constructorRejectsNonPositiveTimeoutschecks onlyDuration.ZERO. Add a negative duration for bothlightTimeoutandheavyTimeout. Otherwise, a constructor that accepts negative values can pass this test.Suggested additions
+ assertThatThrownBy(() -> new HttpControlClient( + Duration.ofMillis(-1), Duration.ofSeconds(1))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("lightTimeout"); + + assertThatThrownBy(() -> new HttpControlClient( + Duration.ofSeconds(1), Duration.ofMillis(-1))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("heavyTimeout");🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@control/control-client/src/test/java/io/tapstate/control/client/HttpControlClientTest.java` around lines 25 - 33, Extend constructorRejectsNonPositiveTimeouts to assert IllegalArgumentException for negative lightTimeout and negative heavyTimeout values, while preserving the existing zero-duration assertions and message checks for each parameter.
130-151: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winAssert the configured request budget.
The client uses 250 ms timeouts, but
response.get(5, TimeUnit.SECONDS)allows a several-second regression to pass. Measure elapsed time and assert completion within the request budget plus explicit scheduler slack.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@control/control-client/src/test/java/io/tapstate/control/client/HttpControlClientTest.java` around lines 130 - 151, Update requestBudgetBoundsAStalledExchange to measure elapsed time around the client request and assert that response completion occurs within the configured 250 ms request budget plus an explicit, narrowly defined scheduler slack. Replace the five-second response.get allowance with a timeout aligned to that budget, while preserving the existing Unreachable assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@control/control-client/src/test/java/io/tapstate/control/client/HttpControlClientTest.java`:
- Around line 121-128: Update connectionFailureIsAStableUnreachableOutcome so
the ServerSocket is closed before invoking HttpControlClient.get, ensuring the
endpoint is unreachable and the test covers connection failure rather than a
stalled exchange. Keep the existing assertion and client setup unchanged.
---
Outside diff comments:
In
`@control/control-client/src/test/java/io/tapstate/control/client/HttpControlClientTest.java`:
- Around line 35-57: Update the postSendsBearerAndJsonAndReturnsTheDecodedTree
test and its server handler to capture the incoming HTTP method and request path
from the exchange, then assert they are POST and /api/tokens respectively
alongside the existing authorization and body assertions.
In
`@control/mcp-server/src/test/java/io/tapstate/mcp/McpOperationExecutorTest.java`:
- Around line 33-45: Update the request recording and assertions in
McpOperationExecutorTest to capture each HTTP method together with its URI, then
replace partial contains checks with an ordered containsExactly assertion.
Verify the complete sequence, including the connector-spec GET followed by the
source POST, while preserving the existing server behavior.
- Around line 130-133: Update the test server handler around authorization and
the assertions near source.create to collect every request’s Authorization
header instead of overwriting a single AtomicReference. Assert that each
captured header equals “Bearer machine-token,” ensuring all requests, including
the connector-spec request, are validated.
---
Nitpick comments:
In
`@control/control-client/src/test/java/io/tapstate/control/client/HttpControlClientTest.java`:
- Around line 25-33: Extend constructorRejectsNonPositiveTimeouts to assert
IllegalArgumentException for negative lightTimeout and negative heavyTimeout
values, while preserving the existing zero-duration assertions and message
checks for each parameter.
- Around line 130-151: Update requestBudgetBoundsAStalledExchange to measure
elapsed time around the client request and assert that response completion
occurs within the configured 250 ms request budget plus an explicit, narrowly
defined scheduler slack. Replace the five-second response.get allowance with a
timeout aligned to that budget, while preserving the existing Unreachable
assertion.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 830b489e-f029-4a60-b8b0-2abd588f1dfc
📒 Files selected for processing (22)
.github/workflows/release.ymlapp/src/main/java/io/tapstate/app/SecretTrackingArtifactStore.javaapp/src/test/java/io/tapstate/app/SecretTrackingArtifactStoreTest.javacli/native-smoke.shcli/src/main/java/io/tapstate/cli/Repl.javacli/src/test/java/io/tapstate/cli/ReplTest.javacontrol/control-client/src/main/java/io/tapstate/control/client/HttpControlClient.javacontrol/control-client/src/test/java/io/tapstate/control/client/HttpControlClientTest.javacontrol/control-core/src/main/java/io/tapstate/control/core/SourceService.javacontrol/control-core/src/test/java/io/tapstate/control/core/SourceServiceTest.javacontrol/mcp-server/mcp-smoke.shcontrol/mcp-server/src/main/java/io/tapstate/mcp/McpOperationExecutor.javacontrol/mcp-server/src/test/java/io/tapstate/mcp/McpOperationExecutorTest.javacontrol/rest-api/src/main/java/io/tapstate/control/restapi/ArtifactController.javacontrol/rest-api/src/main/java/io/tapstate/control/restapi/PipelineLogsController.javacontrol/rest-api/src/test/java/io/tapstate/control/restapi/AuthTest.javacontrol/rest-api/src/test/java/io/tapstate/control/restapi/ControlApiTest.javacontrol/rest-api/src/test/java/io/tapstate/control/restapi/PipelineLogsApiTest.javacore/core-catalog/src/main/java/io/tapstate/core/catalog/SpecNormalizer.javacore/core-catalog/src/test/java/io/tapstate/core/catalog/SpecNormalizerTest.javainstall/install-smoke.shinstall/install.sh
🚧 Files skipped from review as they are similar to previous changes (17)
- core/core-catalog/src/main/java/io/tapstate/core/catalog/SpecNormalizer.java
- control/rest-api/src/test/java/io/tapstate/control/restapi/PipelineLogsApiTest.java
- cli/native-smoke.sh
- control/rest-api/src/test/java/io/tapstate/control/restapi/ControlApiTest.java
- install/install-smoke.sh
- install/install.sh
- control/control-core/src/main/java/io/tapstate/control/core/SourceService.java
- control/mcp-server/mcp-smoke.sh
- cli/src/main/java/io/tapstate/cli/Repl.java
- control/rest-api/src/main/java/io/tapstate/control/restapi/ArtifactController.java
- app/src/test/java/io/tapstate/app/SecretTrackingArtifactStoreTest.java
- control/control-client/src/main/java/io/tapstate/control/client/HttpControlClient.java
- cli/src/test/java/io/tapstate/cli/ReplTest.java
- app/src/main/java/io/tapstate/app/SecretTrackingArtifactStore.java
- control/control-core/src/test/java/io/tapstate/control/core/SourceServiceTest.java
- control/rest-api/src/test/java/io/tapstate/control/restapi/AuthTest.java
- control/mcp-server/src/main/java/io/tapstate/mcp/McpOperationExecutor.java
Summary
tapstate mcplauncher support with native and Java 21 fallback distribution layouts.Verification
mvn -o -q clean verifye2e/src/test/java/io/tapstate/e2e/McpOnlineControlIT.javalaunches the packaged MCP Boot JAR, seeds a live connector spec behind a loopback HTTP peer, invokessource_create, and verifies the exact spec/source request sequence, Bearer forwarding, environment expansion, secret redaction, 17 write-enabled tools, and clean EOF.bin/tapstate mcp.Dependencies and Follow-up
source_createconsumes the connector runtime spec fromGET /api/connectors/{id}. The registered connector spec producer remains owned byws/alpha-connector-trunk.Summary by CodeRabbit
New Features
Bug Fixes
Documentation