Skip to content

perf(security-issue-sync): decide bulk mode, guardrails, drafting, GHSA routing and the push flow as code - #1710

Merged
potiuk merged 3 commits into
mainfrom
perf/security-4b-sync
Oct 19, 2026
Merged

potiuk merged 3 commits into
mainfrom
perf/security-4b-sync

Conversation

@potiuk

@potiuk potiuk commented Oct 19, 2026

Copy link
Copy Markdown
Member

Layer 4b (part 1) of the security-family deep split (#1672), after #1705 (cve-allocate) and #1707 (deduplicate): the rule-shaped parts of security-issue-sync move into an issue-sync family of tools/security-management. Mining signals, the signals-to-actions table and the gather recipe stay with the model and follow in part 2.

What moves to code

Where Command Decides / renders
Inputs / bulk 1 selector, resolve the selector as gh reads (a malformed CVE-… token stops it before any search); the resolved set (ledger dropped, closed announced cut to 90 days, a CVE matched only in its CVE tool link field, capped lists flagged) and the echo
Bulk 1b preflight-query, preflight the batched state read; the no-op rule table, never skipping an explicitly named tracker
Bulk 3–4 bucket, parse-bulk-reply CVE-affecting vs non-CVE-affecting (the allocated → review-ready push rides the latter), walk order; the merged confirmation syntax
Step 3 parse-reply the single-tracker form (shared.numbered)
Guardrails check reporter CVSS outside the observed state, other ASF projects, a bare CVE ID on markdown (or the CVE-tool URL in an email), bare #NNN
Step 0 / drafts security-cc, draft-backend the CC through the configuration layers (shared.recipients); Gmail or Maildir, then the Gmail backend (shared.drafting.choose)
GHSA ghsa-route the access tier and how each advisory change is delivered
Step 5b push-decision skip, the seven gates (three checked on the JSON, four taken as the model's verdicts), session probe, push, state verification
Step 6 render-recap the recap with every link

Nine eval suites become pytest cases named after each former case (tests/test_issue_sync.py, tests/test_issue_sync_rules.py): step-bulk-selectors, step-bulk-orchestration, step-3-confirm, step-6-recap, step-guardrails, step-security-cc, step-draft-backend, step-ghsa-access-tier, step-5b-push-decision. A new step-5b-judged-gates model suite covers the four gates the model still judges, and passes 2/2. The skill's model cases go from 86 to 38.

SKILL.md grows slightly (7,006 → 7,282 tokens): this part adds the router lines but moves no large section out yet; gather.md and signals-to-actions.md, the bulk of the skill, are part 2.

Behaviour changes

  • A malformed CVE token stops the whole selector before any search.
  • A CVE search keeps only trackers whose CVE tool link names the CVE, read through github-body-field rather than a body regex.
  • A bulk reply naming an unknown tracker or item, two dispositions for one tracker, or force-sync of a tracker that was not skipped applies nothing.
  • An explicit null or blank fallback address in the project layer disables the organization's, and the draft is blocked.
  • An admin-only advisory change after a 403 is never retried, and is always a draft relay, never a tracker comment.
  • A judged push gate with no verdict is asked for before the push, never assumed to pass.
  • The cve-allocate title cascade also strips a bracketed [GHSA-…] ID.
  • A CVE ID that is not CVE-YYYY-NNNNN never reaches a printed command: push-decision and the deduplicate apply plan (from perf(security-issue-deduplicate): decide and render the merge as code #1707) refuse it, and quote it.

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

… and guardrails as code

A new issue-sync family in tools/security-management takes the rules of
security-issue-sync's bulk mode and its rule-shaped single-tracker steps:
the selector and its gh reads, the resolved set, the batched pre-flight
read and the no-op rule table, the CVE-impact buckets, the single and
bulk confirmation forms, a Guardrails check over the proposal's surfaces,
and the Step 6 recap. Gathering state, mining signals and writing
comments and drafts stay with the model.

The step-bulk-selectors, step-bulk-orchestration, step-3-confirm,
step-6-recap and step-guardrails suites become pytest cases named after
each former case (86 -> 67 model cases).

Behaviour changes:
- A malformed CVE token stops the whole selector before any search, and
  a CVE search keeps only trackers whose CVE tool link field names it
  (read through github-body-field, not a body regex).
- A bulk reply naming an unknown tracker or item, two dispositions for
  one tracker, or force-sync of a tracker that was not skipped applies
  nothing.
- The Guardrails check flags a reporter CVSS score outside the observed
  state, another ASF project's name, a bare CVE ID on a markdown surface,
  the CVE-tool URL in a reporter email and a bare #NNN, per passage.

Part of #1672

Generated-by: Claude Opus 5
…GHSA routing and push flow as code

Four more rules of security-issue-sync move into the issue-sync family:

- security-cc: the security CC of every draft through the project,
  organization and framework layers, each key from the first layer that
  declares it (shared.recipients), per the mail-source contract;
- draft-backend: Gmail or Maildir, the person over the project, then the
  Gmail backend (shared.drafting.choose);
- ghsa-route: the operator's advisory access tier and how a field edit,
  collaborator change, state change or reporter reply is delivered;
- push-decision: the Step 5b flow — skip, the seven pre-push gates, the
  session probe, the push and the state verification — checking the
  title, upgrade-target and CWE long-form gates on the JSON and taking
  the four judgement gates as the model's verdicts.

The step-security-cc, step-draft-backend, step-ghsa-access-tier and
step-5b-push-decision suites become pytest cases named after each former
case; a new step-5b-judged-gates model suite covers the four gates the
model still judges (67 -> 38 model cases for the skill).

Behaviour changes:
- An explicit null or blank fallback address in the project layer
  disables the organization's, and the draft is blocked.
- An admin-only advisory change after a 403 is never retried and is
  always a draft relay, never a tracker comment.
- A judged gate with no verdict is asked for before the push, never
  assumed to pass.
- The title cascade also strips a bracketed [GHSA-...] ID.
- A CVE ID that is not CVE-YYYY-NNNNN never reaches a printed command:
  push-decision and the deduplicate apply plan refuse it, and quote it.

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:intake Import external signal + sync tracker state contract:mail-archive Tool capability: mailing-list / forum archive reads contract:mail-source Tool capability: inbound-mail ingestion (mbox / IMAP / ...) contract:mail-create Tool capability: outbound mail composition (editable draft; sending is a separate human step) substrate:analytics Tool substrate: read-only metrics / dashboards / renderers substrate:framework-dev Tool substrate: build / validate / eval the framework itself labels Oct 19, 2026
…he guardrail check

CodeQL read `"cveprocess.apache.org" in sentence` as an incomplete URL
substring sanitization (py/incomplete-url-substring-sanitization). The
check scans proposal text for the CVE-tool URL in a reporter email; it
sanitises nothing. A host-anchored pattern says so and keeps the same
cases flagged.

Generated-by: Claude Opus 5
@potiuk
potiuk merged commit dacf00a into main Oct 19, 2026
12 checks passed
@potiuk
potiuk deleted the perf/security-4b-sync branch October 19, 2026 15:39
potiuk added a commit that referenced this pull request Oct 19, 2026
Hand-sync of tools/spec-loop/specs/ with the functionality that landed
between ccb53a3 and 03c15a6.

Specs changed:
- security-issue-lifecycle: the tools/security-management shared library
  gains drafting, recipients and numbered; the package reuses
  github-body-field, generate-cve-json and magpie-vcs (the embargo
  scrub); every skill of the family now has its CLI family, completing
  the deep split (#1672); the Validation block runs its pytest suite.
- .last-sync bumped to 03c15a6.

No spec change needed (specs already updated in the commit itself):
03c15a6 (#1712), c819c90 (#1711), dacf00a (#1710),
cb6bb8b (#1705).

Exempt: aee01bf (the previous sync), 0bd021f (npm pin bump in prek
hooks).

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:intake Import external signal + sync tracker state contract:mail-archive Tool capability: mailing-list / forum archive reads contract:mail-create Tool capability: outbound mail composition (editable draft; sending is a separate human step) contract:mail-source Tool capability: inbound-mail ingestion (mbox / IMAP / ...) 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.

2 participants