Skip to content

perf(security-issue-deduplicate): decide and render the merge as code - #1707

Merged
potiuk merged 1 commit into
perf/security-3-cve-allocatefrom
perf/security-4-deduplicate
Oct 19, 2026
Merged

potiuk merged 1 commit into
perf/security-3-cve-allocatefrom
perf/security-4-deduplicate

Conversation

@potiuk

@potiuk potiuk commented Oct 19, 2026

Copy link
Copy Markdown
Member

Layer 4a of the security-family deep split (#1672), stacked on #1705 (cve-allocate): security-issue-deduplicate's rule-shaped steps move into an issue-deduplicate family of tools/security-management. Sync (4b) follows separately.

What moves to code

Step Command Decides / renders
Inputs selector the kept and dropped trackers, kept first; one tracker is refused (the kept side is the user's call)
1 classify both open, one shared scope label, neither duplicate; the CVE rules (a CVE only on the dropped side → swap, public → refuse, two CVEs → the kept one at least as far along, the dropped one retracted)
2 fields both bodies' template fields through github-body-field (fail closed), the CVE JSON attachment cut off, CWE conflict, severity-propagation risk
3 merge-body the kept body rewritten field by field with replace_field
4 render-rollup-kept, render-rollup-dropped both rollup entries
5 proposal, parse-reply, apply-plan items 1–7, the reply grammar, the per-backend apply order
6 render-recap the recap

The model keeps the root-cause judgement, the headline and analysis, and a merged summary or widened version range when the trackers disagree.

The merged body is the kept body rewritten one field at a time with github-body-field's replace_field, which refuses a rewrite that would change any other field. The CVE JSON attachment, a project's extra fields and every unchanged value stay byte-exact.

shared.numbered (the numbered-proposal reply grammar) and shared.rollup.notification_line move out of cve_allocate, so both skills use one copy.

SKILL.md becomes a router (6,312 → 6,029 tokens); every heading and golden-rule headline is unchanged. The step-1 to step-6 model suites are now pytest cases named after each former case (tests/test_issue_deduplicate.py). A new step-3-merge-inputs model suite covers what the model still writes, and passes 3/3 on a self-eval.

Behaviour changes

  • Reporter-supplied CVSS vectors, scores and severity labels are removed mechanically from the appended report, and listed for the user.
  • A dropped report cannot add, end or hide a field: field-ending and machine-marker lines are escaped, an unmatched </details> is defused, and a body github-body-field will not decide on is refused.
  • A CVE only on the dropped side, a dropped CVE further along than the kept one, or a tracker without exactly one scope label blocks the merge.
  • When both summaries or both version ranges differ, the model's merged value is required.
  • The apply plan is refused while a blocker (a CWE conflict) is open.
  • A list with a comment ("1,2,3,4 — skip 5 and 6 for now") applies the list only when the comment names other items to hold them back.

Spec security-issue-lifecycle describes the new family.

Part of #1672

Generative AI disclosure

Drafted with Claude Code (Claude Opus 5) and reviewed by the author.

🤖 Generated with Claude Code

A new issue-deduplicate family in tools/security-management takes the
rules of security-issue-deduplicate that are pure functions of tracker
state: the selector (the kept side first, never guessed), the Step 1
checks and CVE rules, the Step 2 field reads and conflicts, the Step 3
merged body, both Step 4 rollup entries, the Step 5 proposal, reply
grammar and per-backend apply order, and the Step 6 recap. Judging the
shared root cause, the headline and analysis, and a merged summary or
widened version range stay with the model.

The merged body is the kept body rewritten field by field through
github-body-field's replace_field, which refuses any rewrite that would
change another field, so the CVE JSON attachment, a project's extra
fields and every unchanged value stay byte-exact.

The numbered-proposal reply grammar moves from cve_allocate to
shared.numbered and the Reporter-notification line to
shared.rollup.notification_line, so both skills use one copy.

SKILL.md becomes a router (6,312 -> 6,029 tokens); every heading and
golden-rule headline is unchanged. The step-1 to step-6 suites become
pytest cases named after each former case; a new step-3-merge-inputs
model suite covers what the model still writes.

Behaviour changes:
- Reporter-supplied CVSS vectors, scores and severity labels are removed
  from the appended report mechanically, and listed for the user.
- A dropped report cannot add, end or hide a field: field-ending and
  machine-marker lines are escaped, an unmatched </details> is defused,
  and a body github-body-field will not decide on is refused.
- A CVE only on the dropped side, a dropped CVE further along than the
  kept one, or a tracker without exactly one scope label blocks the merge.
- Both summaries or both version ranges differing needs the model's
  merged value; the merge is refused without it.
- The apply plan is refused while a blocker (a CWE conflict) is open.
- A list with a comment ("1,2,3,4 — skip 5 and 6 for now") applies the
  list only when the comment names other items to hold them back.

Part of #1672

Generated-by: Claude Opus 5
@github-actions github-actions Bot added family:security security-* skills family:tools tools/* family:docs Docs, MISSION.md, READMEs capability:resolve Close-out: invalidate, dedupe, CVE allocate, announcement substrate:analytics Tool substrate: read-only metrics / dashboards / renderers substrate:framework-dev Tool substrate: build / validate / eval the framework itself labels Oct 19, 2026
@potiuk
potiuk merged commit 594ea66 into perf/security-3-cve-allocate Oct 19, 2026
8 checks passed
@potiuk
potiuk deleted the perf/security-4-deduplicate branch October 19, 2026 11:27
potiuk added a commit that referenced this pull request Oct 19, 2026
…s code (#1705)

* perf(security-cve-allocate): decide and render the allocation rules as code

A new cve-allocate family in tools/security-management takes the rules of
security-cve-allocate that are pure functions of tracker state: the
selector and its CVE override, the Step 1 blockers, the Step 2 title
cascade, the Step 0 API pre-flight and the allocation exits, the Step 3
API proposal, recipe and relay, the paste-back CVE ID, the reporter draft
plan and its leak check, the Step 4 numbered proposal and rollup entry,
the Step 5 reply grammar and per-backend apply order, and the Step 7
recap. Writing the reporter draft stays with the model, and allocating
the CVE with the user.

The draft backend choice and the leak scan move from issue_invalidate to
shared.drafting, unchanged, so both skills use one copy.

SKILL.md becomes a router (7,679 -> 7,324 tokens); every heading and
golden-rule headline is unchanged, and the blocker list moved to
outcomes/blockers.md. The title cascade leaves the inline Python recipe
for `cve-allocate title`, which takes the project's names and tracker
prefixes from title-normalization.md. The step-1, 2, 3, 4, 5 and 7
suites become pytest cases named after each former case; a new
step-4-draft-body model suite covers the one step the model still owns.

Behaviour changes:
- The reporter status draft names the bare CVE ID and never the CVE-tool
  record URL, which a reporter cannot open (editorial guidelines); the
  canned-response template said the opposite and is fixed.
- A malformed or second CVE token in the invocation or the paste-back is
  refused, never repaired.
- Two scope labels, or a CVE tool link field github-body-field will not
  decide on, block instead of being guessed.
- A re-run with the CVE already wired into the field (a partial apply) is
  a resume, not an "already allocated" blocker.
- A draft id is claimed pending in the rollup only after checking Drafts.
- An allocation is never re-run automatically; after an expired token it
  needs a fresh yes.
- A free-form reply applies nothing: it is an edit, and the proposal is
  confirmed again.

Part of #1672

Generated-by: Claude Opus 5

* fix(security-cve-allocate): answer the two CodeQL findings

- The item-list pattern of the confirmation grammar allowed whitespace
  on both sides of a whitespace separator, so a reply of many spaced
  zeros backtracked exponentially (py/redos). The separators are now a
  comma, `and`, or whitespace alone, which cannot overlap.
- A recap test asserted on `dict.pop` (py/side-effect-in-assert); it
  reads the key instead.

Generated-by: Claude Opus 5

* perf(security-issue-deduplicate): decide and render the merge as code (#1707)

A new issue-deduplicate family in tools/security-management takes the
rules of security-issue-deduplicate that are pure functions of tracker
state: the selector (the kept side first, never guessed), the Step 1
checks and CVE rules, the Step 2 field reads and conflicts, the Step 3
merged body, both Step 4 rollup entries, the Step 5 proposal, reply
grammar and per-backend apply order, and the Step 6 recap. Judging the
shared root cause, the headline and analysis, and a merged summary or
widened version range stay with the model.

The merged body is the kept body rewritten field by field through
github-body-field's replace_field, which refuses any rewrite that would
change another field, so the CVE JSON attachment, a project's extra
fields and every unchanged value stay byte-exact.

The numbered-proposal reply grammar moves from cve_allocate to
shared.numbered and the Reporter-notification line to
shared.rollup.notification_line, so both skills use one copy.

SKILL.md becomes a router (6,312 -> 6,029 tokens); every heading and
golden-rule headline is unchanged. The step-1 to step-6 suites become
pytest cases named after each former case; a new step-3-merge-inputs
model suite covers what the model still writes.

Behaviour changes:
- Reporter-supplied CVSS vectors, scores and severity labels are removed
  from the appended report mechanically, and listed for the user.
- A dropped report cannot add, end or hide a field: field-ending and
  machine-marker lines are escaped, an unmatched </details> is defused,
  and a body github-body-field will not decide on is refused.
- A CVE only on the dropped side, a dropped CVE further along than the
  kept one, or a tracker without exactly one scope label blocks the merge.
- Both summaries or both version ranges differing needs the model's
  merged value; the merge is refused without it.
- The apply plan is refused while a blocker (a CWE conflict) is open.
- A list with a comment ("1,2,3,4 — skip 5 and 6 for now") applies the
  list only when the comment names other items to hold them back.

Part of #1672

Generated-by: Claude Opus 5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

capability:resolve Close-out: invalidate, dedupe, CVE allocate, announcement family:docs Docs, MISSION.md, READMEs family:security security-* skills family:tools tools/* substrate:analytics Tool substrate: read-only metrics / dashboards / renderers substrate:framework-dev Tool substrate: build / validate / eval the framework itself

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant