Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions .claude/commands/address-pr-feedback.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,12 @@ Track progress with a checklist:

### Step 5: Reply to Close Conversations

For EACH addressed comment, reply to close the conversation.
For EACH addressed comment, reply **inline in the conversation thread** to close it.

**CRITICAL: Replies must be inline, not general PR comments.**
- Use the `in_reply_to` parameter to reply within the existing conversation thread
- Do NOT use `gh pr comment` which creates a new top-level comment
- Inline replies keep the conversation organized and allow proper thread resolution

**Use the reviewer's actual handle** from the comment's `user.login` field:

Expand All @@ -117,21 +122,24 @@ Addressed in commit <hash>. <Brief explanation>
```
(No @ mention needed for human reviewers)

Use the GitHub CLI to reply:
Use the GitHub CLI to reply inline:
```bash
# Get repository info (if not already set)
REPO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')

# Get the reviewer's login from the comment (available from Step 2 output)
COMMENT_ID=<comment_id>
REVIEWER_LOGIN=$(gh api repos/$REPO/pulls/$PR_NUMBER/comments/$COMMENT_ID --jq '.user.login')

# Reply using the dedicated replies endpoint
gh api repos/$REPO/pulls/$PR_NUMBER/comments/$COMMENT_ID/replies \
# Reply inline to a review comment using in_reply_to parameter
# The COMMENT_ID is the id of the comment you are replying to
gh api repos/$REPO/pulls/$PR_NUMBER/comments \
-X POST \
-f body="@$REVIEWER_LOGIN Addressed in commit abc1234. <explanation>"
-F in_reply_to=<COMMENT_ID> \
-f body="@coderabbitai Addressed in commit abc1234. <explanation>"
```

**Important API notes:**
- Use `-F in_reply_to=<id>` (capital F for integer parameter)
- The `in_reply_to` value must be the numeric comment ID
- This creates a reply within the existing conversation thread

### Step 6: Generate Summary Report

After addressing all feedback, produce a summary table:
Expand Down
47 changes: 42 additions & 5 deletions .claude/skills/metaschema-java-library.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,42 @@ Use `target` attribute with Metapath expressions to configure inline definitions
</define-assembly-binding>
```

### Classpath Requirements for Code Generation

When configuring custom base classes or superinterfaces, those classes **must be available on the classpath during code generation**. The generator uses reflection to:

1. **Detect method overrides**: Checks if getter/setter methods are declared in superinterfaces to add `@Override` annotations
2. **Determine IBoundObject extension**: Checks if configured superinterfaces extend `IBoundObject` to avoid redundant interface declarations

**Maven Plugin Configuration:**

Add dependencies to the `metaschema-maven-plugin` configuration to ensure classes are available:

```xml
<plugin>
<groupId>gov.nist.secauto.metaschema</groupId>
<artifactId>metaschema-maven-plugin</artifactId>
<dependencies>
<!-- Add dependency containing custom interfaces/base classes -->
<dependency>
<groupId>com.example</groupId>
<artifactId>my-interfaces</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</plugin>
```

**Behavior when classes are not on classpath:**

| Scenario | Behavior |
|----------|----------|
| Base class not found | `@Override` annotations may be missing from getters/setters |
| Superinterface not found | `@Override` annotations may be missing; interface may be redundantly added |
| Generated interface not yet compiled | Use multi-phase generation or ensure build order |

**Multi-module projects:** When the superinterface is defined in another module, ensure that module is built and available as a dependency before code generation runs.

## Serialization and Deserialization

### Deserializing Content
Expand Down Expand Up @@ -340,11 +376,12 @@ import gov.nist.secauto.metaschema.core.model.constraint.FindingCollectingConstr
FindingCollectingConstraintValidationHandler handler =
new FindingCollectingConstraintValidationHandler();

// Create validator
IConstraintValidator validator = new DefaultConstraintValidator(handler);

// Validate document
validator.validate(documentNodeItem, dynamicContext);
// Create and use validator with try-with-resources
// IConstraintValidator implements AutoCloseable to release thread pools
try (IConstraintValidator validator = new DefaultConstraintValidator(handler)) {
// Validate document
validator.validate(documentNodeItem, dynamicContext);
}

// Check results
IValidationResult result = handler.toValidationResult();
Expand Down
131 changes: 123 additions & 8 deletions .claude/skills/pr-feedback.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,27 @@ Wait for explicit user approval before proceeding.

## Step 6: Respond to Comments

For review threads, you cannot use the REST API replies endpoint (returns 404).
Instead, either:
**CRITICAL: Replies must be inline in the conversation thread, not general PR comments.**

For review threads, use the REST API with `in_reply_to` parameter:

```bash
# Reply inline to a review comment
gh api repos/<OWNER>/<REPO>/pulls/<PR_NUMBER>/comments \
-X POST \
-F in_reply_to=<COMMENT_ID> \
-f body="@coderabbitai Addressed in commit abc1234. <explanation>"
```

**Important:**
- Use `-F in_reply_to=<id>` (capital F for integer parameter)
- The `in_reply_to` value must be the numeric comment ID from Step 1
- This creates a reply within the existing conversation thread
- Do NOT use `gh pr comment` which creates a top-level comment

**Alternative methods:**
1. **Let the bot auto-resolve** - Some bots (like CodeRabbit) auto-detect fixes and resolve threads
2. **Post a consolidated PR comment** - Summarize all fixes in one comment
3. **Resolve via GraphQL** - Use the mutation API (see Step 7)
2. **Resolve via GraphQL** - Use the mutation API (see Step 7)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

## Step 7: Resolve Review Threads

Expand Down Expand Up @@ -247,17 +262,117 @@ Address Feedback → Push → Wait for CI/Bot Review
|------|--------|
| Fetch PR comments | `gh pr view <N> --comments` |
| Fetch file comments | `gh api repos/.../pulls/<N>/comments` |
| Post inline reply | `gh api repos/.../pulls/<N>/comments -X POST -F in_reply_to=<id> -f body=<msg>` |
| Verify GraphQL field exists | `gh api graphql -f query='{ __type(name: "X") { fields { name } } }'` |
| Get thread IDs | GraphQL `reviewThreads` query |
| Resolve thread | GraphQL `resolveReviewThread` mutation |
| Verify resolved | GraphQL query checking `isResolved` |

## Identifying and Commenting in Unresolved Conversations

Use this workflow to systematically find and respond to all unresolved review
threads. This complements Step 6 by providing a discovery mechanism when you
need to identify which threads still need responses.

After addressing feedback, identify unresolved threads that need inline replies:

### Step 1: Find Unresolved Threads with Comment IDs

```bash
gh api graphql -f query='
query {
repository(owner: "<OWNER>", name: "<REPO>") {
pullRequest(number: <PR_NUMBER>) {
reviewThreads(first: 50) {
nodes {
id
isResolved
path
comments(first: 1) {
nodes {
fullDatabaseId
author {
login
}
body
}
}
}
}
}
}
}'
```

### Step 2: Filter for Unresolved Threads

Look for threads where `isResolved: false`. Each has:
- `fullDatabaseId` - The numeric ID needed for inline replies (use this instead of deprecated `databaseId`)
- `path` - The file path for context
- `body` - Preview of what the comment is about
- `author.login` - Who made the comment (e.g., `coderabbitai`)

### Step 3: Post Inline Replies

For each unresolved thread, use the `fullDatabaseId` to reply inline.
Use the `author` field from Step 1's jq output as `<author_login>`:

```bash
gh api repos/<OWNER>/<REPO>/pulls/<PR_NUMBER>/comments \
-X POST \
-F in_reply_to=<FULL_DATABASE_ID> \
-f body="@<author_login> Addressed in commit <hash>. <explanation>"
```

**Example workflow:**

The jq filter iterates through all threads, keeps only unresolved ones, and
extracts the first comment's ID and author for reply purposes:

```bash
# 1. Get unresolved threads with jq extraction
# The jq filter:
# - .data.repository.pullRequest.reviewThreads.nodes[] : iterate all threads
# - select(.isResolved==false) : keep only unresolved ones
# - {id, fullDatabaseId: ..., path, author: ...} : extract needed fields
gh api graphql -f query='...' --jq '
.data.repository.pullRequest.reviewThreads.nodes[]
| select(.isResolved==false)
| {
id,
fullDatabaseId: .comments.nodes[0].fullDatabaseId,
path,
author: .comments.nodes[0].author.login
}'

# 2. For each unresolved thread, note the fullDatabaseId from the output

# 3. Post inline reply using the fullDatabaseId and author from step 1
gh api repos/owner/repo/pulls/123/comments \
-X POST \
-F in_reply_to=2653785779 \
-f body="@coderabbitai Addressed in commit abc123. Fixed the documentation."
```

### When to Use This Workflow

- After pushing fixes for review feedback
- When the user asks "there are open conversations that haven't been commented in"
- Before requesting a re-review to ensure all feedback has responses
- When CodeRabbit or other bots don't auto-resolve threads

---

## Common Pitfalls

1. **REST replies endpoint 404** - The `/pulls/comments/{id}/replies` endpoint doesn't work for review comments. Use GraphQL instead.
1. **Using wrong reply API** - The `/pulls/comments/{id}/replies` endpoint returns 404. Use `POST /pulls/{id}/comments` with `-F in_reply_to=<comment_id>` instead.

2. **Top-level comments instead of inline** - Using `gh pr comment` creates a top-level comment that doesn't appear in the conversation thread. Always use the API with `in_reply_to` for inline replies.

3. **Unresolved threads blocking merge** - Some repos require all threads resolved. Always verify with the GraphQL query.

2. **Unresolved threads blocking merge** - Some repos require all threads resolved. Always verify with the GraphQL query.
4. **Missing feedback** - Check both `comments` (general) and `reviews` (code review) in the PR data.

3. **Missing feedback** - Check both `comments` (general) and `reviews` (code review) in the PR data.
5. **Incorrect automated feedback** - Bots like CodeRabbit can make mistakes. Verify technical claims against official docs or schema introspection before implementing.

4. **Incorrect automated feedback** - Bots like CodeRabbit can make mistakes. Verify technical claims against official docs or schema introspection before implementing.
6. **Not getting databaseId for inline replies** - The GraphQL thread `id` (node ID) is for resolving threads. The `databaseId` from the first comment is needed for inline replies via REST API.
2 changes: 1 addition & 1 deletion .github/workflows/container.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
annotations: |
maintainers="Metaschema Community Admin <admin@metaschema.dev>"
org.opencontainers.image.authors="Metaschema Community Admin <admin@metaschema.dev>"
org.opencontainers.image.documentation="https://metaschema.dev"
org.opencontainers.image.documentation="https://framework.metaschema.dev"
org.opencontainers.image.source="https://github.com/metaschema-framework/metaschema-java"
org.opencontainers.image.vendor="Metaschema Community"
org.opencontainers.image.title="metaschema-cli"
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
!**/.settings/org.eclipse.jdt.core.prefs
!**/.settings/org.eclipse.jdt.ui.prefs
!**/.settings/org.eclipse.m2e.core.prefs
!**/.settings/org.eclipse.jdt.apt.core.prefs
.checkstyle
.factorypath
.pmd
Expand Down
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ PRDs are stored in `PRDs/<YYYYMMDD>-<name>/` with:

| PRD | Description | Completed |
|-----|-------------|-----------|
| `PRDs/20251229-javadoc-coverage/` | Complete Javadoc coverage for schemagen, databind, and maven-plugin modules | 2025-12-29 |
| `PRDs/20251228-targeted-report-constraint/` | Add constraint processing support for TargetedReportConstraint (issue #592) | 2025-12-29 |
| `PRDs/20251228-validation-errors/` | Validation error message improvements (#595, #596, #205) | 2025-12-28 |
| `PRDs/20251224-codegen-quality/` | Code generator Javadoc and quality improvements | 2025-12-28 |
Expand Down
3 changes: 3 additions & 0 deletions cli-processor/.settings/org.eclipse.jdt.apt.core.prefs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
eclipse.preferences.version=1
org.eclipse.jdt.apt.aptEnabled=false
org.eclipse.jdt.apt.reconcileEnabled=false
3 changes: 2 additions & 1 deletion cli-processor/.settings/org.eclipse.jdt.core.prefs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=warning
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.processAnnotations=enabled
org.eclipse.jdt.core.compiler.processAnnotations=disabled
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=11
org.eclipse.jdt.core.formatter.align_arrows_in_switch_on_columns=false
Expand Down Expand Up @@ -213,6 +213,7 @@ org.eclipse.jdt.core.formatter.comment.format_header=false
org.eclipse.jdt.core.formatter.comment.format_html=true
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
org.eclipse.jdt.core.formatter.comment.format_line_comments=true
org.eclipse.jdt.core.formatter.comment.format_markdown_comments=true
org.eclipse.jdt.core.formatter.comment.format_source_code=true
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
Expand Down
3 changes: 3 additions & 0 deletions cli-processor/.settings/org.eclipse.jdt.ui.prefs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ cleanup.make_type_abstract_if_missing_method=false
cleanup.make_variable_declarations_final=false
cleanup.map_cloning=true
cleanup.merge_conditional_blocks=true
cleanup.module_imports=false
cleanup.multi_catch=true
cleanup.never_use_blocks=false
cleanup.never_use_parentheses_in_expressions=true
Expand Down Expand Up @@ -146,8 +147,10 @@ cleanup.use_var=true
cleanup.useless_continue=true
cleanup.useless_return=true
cleanup.valueof_rather_than_instantiation=true
cleanup_profile=_SecAuto Checkstyle
cleanup_settings_version=2
eclipse.preferences.version=1
formatter_profile=_SecAuto CheckStyle Formatter
formatter_settings_version=23
org.eclipse.jdt.ui.ignorelowercasenames=true
org.eclipse.jdt.ui.importorder=\#;com;gov;net;org;java;javax;;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.eclipse.jdt.annotation.NotOwning;
import org.fusesource.jansi.AnsiConsole;

import java.io.PrintStream;
Expand Down Expand Up @@ -105,6 +106,7 @@ public class CLIProcessor {
@NonNull
private final Map<String, IVersionInfo> versionInfos;
@NonNull
@NotOwning
private final PrintStream outputStream;

/**
Expand Down Expand Up @@ -162,10 +164,12 @@ public CLIProcessor(@NonNull String exec, @NonNull Map<String, IVersionInfo> ver
* the version info to display when the version option is provided
* @param outputStream
* the output stream to write to, or {@code null} to use the default
* console
* console. The caller retains ownership of this stream and is
* responsible for closing it.
*/
@SuppressWarnings("resource")
public CLIProcessor(@NonNull String exec, @NonNull Map<String, IVersionInfo> versionInfos,
@Nullable PrintStream outputStream) {
@Nullable @NotOwning PrintStream outputStream) {
this.exec = exec;
this.versionInfos = versionInfos;
if (outputStream == null) {
Expand Down Expand Up @@ -275,10 +279,13 @@ static void handleNoColor() {

/**
* Get the output stream used for writing CLI output.
* <p>
* The caller does not own this stream and must not close it.
*
* @return the output stream
*/
@NonNull
@NotOwning
PrintStream getOutputStream() {
return outputStream;
}
Expand Down
Loading
Loading