Skip to content

feat: mcpadapter — keyed tracker and change-request forge over a plugin MCP process (plugins slice 2b-2) (#301) - #304

Merged
DanielNetzer merged 1 commit into
mainfrom
forgecell/issue-301-7704e99c5912ccae2304213e
Oct 31, 2026
Merged

DanielNetzer merged 1 commit into
mainfrom
forgecell/issue-301-7704e99c5912ccae2304213e

Conversation

@DanielNetzer

Copy link
Copy Markdown
Owner

Implements issue #301.

Molecule: mol-301-ef7376de9cb34ce191735081
Formula: forgecell-github-issue
Base: 49ca00c199e0d2256cf490a708da7e2cb2f9b89d
Delivery: 7596763df4aa5b26715b980738e042728141cd55

Awaiting exact-commit CI and human review. Harness-reported checks are not independent CI evidence. No merge, deployment or issue closure is authorized.

@DanielNetzer

Copy link
Copy Markdown
Owner Author

Adversarial review (plugins slice 2b-2): no HIGH findings. I checked:

  • lifecycle: Shutdown is deferred after Start and runs on error, panic and ctx cancel, behind a sync.Once;
  • that there are no typed-nil error traps;
  • that the Get echo key, List item keys and States result keys are re-checked, along with the 50-key and 100-limit bounds;
  • that Error() carries only names and fixed reasons;
  • that every call goes through ValidateInputJSON, and the bytes sent are the bytes validated;
  • that ParseGitHubKey rejects leading zeros, #0, signs, whitespace, Unicode digits and overflow.

Fixed in this PR, with one verify pass:

  1. Correctness: PublishDraft no longer drops a change the plugin reports as published at an unexpected commit. It returns the decoded ref and url together with the refusal, and the call record carries the reason.
  2. Correctness: the KeyedGitHub.List cursor is now the last Issue's creation time plus its number, not an offset. Closing an Issue between page reads no longer skips another one.
  3. Hardening: Tracker.List refuses a nextCursor equal to the request cursor, a nextCursor after an empty page, and a total below the item count. A plugin can't make a caller page forever.
  4. A cleanup failure alone no longer fails a successful PublishDraft, which could otherwise invite a duplicate publish. It's recorded instead.
  5. compileKeyPattern compiles the raw pattern before wrapping it, so ^x)|(y$ is refused.

Deferred to a follow-up hardening Issue:

  • KeyedGitHub output isn't round-tripped through the contract decoder for sanitizing and bounds.
  • ResultSHA256 digests raw wire bytes; re-marshal for a canonical digest.
  • Adapter limits don't match the schema: keys 128 vs 100, keyPattern 256 vs 200, config maxProperties 64, and identifier whitespace in project.
  • KeyedGitHub fidelity: Get labels and milestone, List updatedAt, a 19-digit GitHubKeyPattern, selector whitespace and UTF-8, and List ignoring Dir.
  • The run root's parent ownership.

LOW: the new cursor assumes the GitHub listing is sorted by creation time, then by number, which is how intake reads oldest-first.

Integration: I applied this branch to current main (with #300 and #302). go vet ./... passes, every package compiles its tests, and go test -timeout 20m ./internal/{mcpadapter,tracker,forge,intake}/... passes.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant