Skip to content

feat(pm): the fleet-write relay publishes an approved security advisory — reopen, set the versions, publish - #30135

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-30110-relay-advisory-publish
Nov 4, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-30110-relay-advisory-publish

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #30110

维护者速读

  • 改了什么:中继(fleet-write)现在能发布安全公告——advisory_state 新增「已关闭→草稿」与「草稿→已发布」两种迁移,新增只写版本范围的 advisory_versions 一行,发布后回读校验并在运行注释里点名公告编号。
  • 为什么改:维护者裁定中继必须能发布(原「发布留给我」一条作废);发布哪些仍由维护者在发版前定清单,中继只执行批准的清单。
  • 风险与回滚:发布不可逆、发布即公开;中继在发布前强制要求版本已设、只允许从草稿发布、执行端自读公告再判,任何未知状态一律拒;回滚即还原本 PR(4 个中继脚本 + 3 份技能参考)。
  • 先看:scripts/pm/fleet-write/ops.mjs 的 ADVISORY_TRANSITIONS 与 ADVISORY_FENCES 新增的 versions 围栏;execute.mjs 的 advisoryPreflight / confirmAdvisory;security-findings.md 删去的一行(见 Acceptance notes)。
  • 两分钟验证:node scripts/pm/fleet-write/validate.mjs --self-test && node scripts/pm/fleet-write/execute.mjs --self-test,预期分别打印 329 与 269 个用例全过。
  • 需要你做的:合入后从你批准的发布清单里点一份公告,让席位经中继做首次实发并把运行编号记在本卡。

What changes

The relay could not publish a security advisory: published was refused by name and ADVISORY_TRANSITIONS allowed only triage → draft and triage|draft → closed. The maintainer ruled that the relay must publish (the ruling and the superseded clause are quoted verbatim in the ops.mjs header). What is published stays the maintainer's list, approved before each release.

  • advisory_state gains two transitions. closed → draft (the reopen; a fixed advisory is closed once its fix merges) and draft → published. Nothing leaves published or withdrawn. Each move is still judged on the executor's own GET. A target the table does not name now has no from-states and fails closed at the state fence (before this PR, an unknown target would have fallen back to the collaborators' open states; the validator's enum already kept it out, and now both layers refuse it).
  • ADVISORY_REFUSED_STATES loses published and is empty; the field's named-refusal hook stays wired.
  • A new row, advisory_versions. It carries ghsa_id and versions, a capped list of closed entries: exactly package (an npm name), vulnerable_version_range and patched_versions. No prose, no summary or description, no vulnerable functions, no state. The range takes one comparator, or a lower bound then an upper bound, each an operator, one space and a release version (GitHub's own example spelling). The patched list holds one to five release versions. A pair whose bounds do not rise, a version named twice, and a patched version inside its own range are refused. The PATCH body is { vulnerabilities } alone. It travels alone like every advisory row, and its token carries repository-advisories: write for that stroke only.
  • The versions fence. GitHub's documentation of this PATCH says "To add vulnerable versions, include existing versions in the array", so the call replaces the whole list. The executor therefore sends versions only to a draft whose every listed package the action names. A listed entry of another ecosystem, a nameless one, or one carrying vulnerable_functions (which the row does not write and the PATCH would drop) is refused with nothing sent. A publish is refused unless the advisory's GET lists at least one entry and every entry is in the row's spelling: the versions are set first.
  • The publish read-back. A publish lands only when the PATCH answer reads published AND lists exactly the entries the preflight GET read (order ignored), so the advisory is read back as published with the versions it was sent. A landed publish alone emits a notice annotation naming the id (public once published). The seat-side reader (dispatch.mjs) parses it, hands it on as advisories, and says to record the run on the seat post. Every other state move stays unannotated.
  • What crosses the public run. The versions row and the publish print the id, the state and counts. They never print a package name or a version, which belong to the unpublished advisory until the publish. Refusals never echo a value.
  • Skill references (.claude/skills/pm-dispatch/references/). security-findings.md §3 now says that the seat publishes only the list the maintainer approved for that release, through the relay, versions first, recording the run ids on the seat post with no text. §9 says that the publish list stays the maintainer's. lanes/security.md points to §3 instead of "publish by hand in the UI", per §8's one-place rule. rest-channel.md no longer says that publishing is the maintainer's by hand. Every file stays at its ratchet ceiling.

Governed surface: the diff touches .claude/** (Tier S). It lands on the owning seat's contract-tier review of record, not on green alone.

Tests

Relay self-tests, at the base 1f13eb3189 and then at this head:

self-test base head advisory battery floor
validate.mjs 303 329 51 → 77
execute.mjs 260 269 32 → 41
dispatch.mjs 315 318 14 → 17

The pins that said "⛔ published is reachable by NO row" moved with the ruling. They now say that published is reached by ONE row (advisory_state), through ONE field value, from draft alone, through the versions fence, and that no request any row spells carries it in a path or a body. The unknown-state cases still fail closed, at the validator's enum and at the state fence. The executor's marker sweep now covers 73 runs, up from 46, including every publish and versions run. The marker reaches no log line, row, summary, annotation or pace log.

Ablation, one-shot. Each mutation went through scripts/ablation-replace.mjs (anchor hit 1 → 0, blob changed) and was restored there (blob == HEAD, git diff HEAD empty):

  • A: published may also leave closed. validate went red on 2 cases (the transition table and the fenced transitions). execute went red on 1 case: an illegal closed → published sent a PATCH.
  • B: the fail-closed default removed (an unknown target falls back to the open states). validate went red on 1 case (the unknown target fails closed). execute stayed green: its preflight independently gives an unknown target no from-states, so the second layer held.
  • C: the publish half of the versions fence removed. validate went red on 3 cases. execute went red on 1 case: an unversioned publish sent a PATCH.

Acceptance notes

  • Measured from documentation, not live. No advisory was touched during development. The REST description (operation security-advisories/update-repository-advisory) and the App permission ledger put state (published, closed, draft) and vulnerabilities (package, range, patched versions, functions) on one PATCH, under repository_advisories: write alone, with no additional permission. The App's PATCH of state is already measured live (triage → draft and draft → closed, runs 41443030055 and 41443112178). The first live publish after merge confirms four things: closed → draft, draft → published and the versions from the App, the versions answered in the spelling sent (the read-back compares the strings exactly, so a platform that normalises them fails closed with nothing re-sent), and whether the platform needs versions before publishing (the relay needs them either way).
  • One line deleted to pay the line ratchet. security-findings.md had zero headroom. Its §2 line "while the relay run log still prints the whole payload, no mechanism in the relay payload" is conditional on a state that no longer holds: the relay reads client_payload from the event file and prints none of it (pinned by validate.mjs's event-file battery and the live marker runs). It also contradicts §3, which routes the mechanism through advisory_create's payload. If the seat prefers to keep it, the alternative is a ruled +1 on that file's ceiling.
  • Not edited: .github/workflows/fleet-write.yml's comment still lists "FOUR write consumers" of permission-repository-advisories and does not name advisory_versions. The mint input itself needs no change. The comment lives on a Tier H surface, so it was left out of this Tier S PR.
  • ops.mjs's requests documentation named a confirmAdvisoryCollaborators that never existed. The sentence was rewritten to name confirmAdvisory.

Verification

Head 5aa0bddcdc (the branch merged with origin/main): node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 46 families, and all 46 ran locally, each with its exit captured before any pipe. --ran reconciliation: "46 derived famil(ies) accounted for — 46 run, 0 NOT-MEASURED (a DERIVED zero …)".

Local gate exits

Every family exited 0. pnpm --filter @objectstack/lint run check:doc-formula-expressions first exited 3 (PREREQUISITE NOT MET: @objectstack/formula and @objectstack/lint unbuilt). It exited 0 after pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint under the shared verify lock ("VERDICT command-exit 0"). The relay importers outside the derivation, fleet-roster, mailbox-open and rerun-failed, were also run with --self-test, and each exited 0.

Families CI runs beyond the local subset are read from this PR's check-runs.

Seat note

@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Nov 4, 2026
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation labels Nov 4, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

security#1 · os-justin · session_01QSeKYn5CmmdShCJifm6WYj

Contract-review-claim: session_01QSeKYn5CmmdShCJifm6WYj · head 5aa0bdd · 2026-11-04T15:33Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

security#1 · os-justin · session_01QSeKYn5CmmdShCJifm6WYj

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 5aa0bddcdc7906ccabd26154feb1c207bb383f20
Local-runs: none
Scope: .claude/skills/pm-dispatch/references/lanes/security.md, .claude/skills/pm-dispatch/references/rest-channel.md, .claude/skills/pm-dispatch/references/security-findings.md + scripts/pm/fleet-write/ops.mjs, scripts/pm/fleet-write/validate.mjs, scripts/pm/fleet-write/execute.mjs, scripts/pm/fleet-write/dispatch.mjs, .github/workflows/fleet-write.yml (read for its stale comment), scripts/pm/check-skill-line-ratchet.mjs (read for the ceilings)

Inputs read: card #30110 (body with the maintainer's ruling, and its one comment, the os-dev report); PR #30135 body, file list (7 files, +714/−190) and the net diff against the merge-base with main (987ff33); the check-runs on this head. node scripts/pm/check-governed-merges.mjs --pr 30135 names the three .claude/** paths above as the only governed hits, all Tier S. Head repo = base repo (not a fork). Nothing built, run or re-run locally: the self-tests the dev cites are run by CI's Lint · repo & tooling gates job (check:pm-fleet-write-validate|execute|dispatch, plus check:pm-skill-ratchet), which is green on this head.

① Derived judgments

Every relay accept-set change, checked against the card's acceptance list, each judged on the tree:

  • closed → draft and draft → published — right. ADVISORY_TRANSITIONS (ops.mjs) is now { draft: [triage, closed], closed: [triage, draft], published: [draft] }, frozen. The validator's advisory_state enum is derived from its keys (ADVISORY_SETTABLE_STATES), so published is a value the validator takes and the MOVE is fenced in the executor on its own GET (advisoryPreflight → ADVISORY_FENCES state), as the card requires ("judged on the executor's own GET, as today"). Pins: validate.mjs transition table (five from-states × three targets), execute.mjs reopen (closed → draft, body exactly { state: draft }) and publish (body exactly { state: published }).
  • "nothing leaves published/withdrawn" — right. No value of ADVISORY_TRANSITIONS contains published or withdrawn (pinned as a flat filter in validate.mjs); execute.mjs pins ten illegal pairs including published → draft, withdrawn → draft, published → closed, withdrawn → closed, each refused with the PATCH NOT SENT, exit 5.
  • Unknown targets fail closed — right, at two layers. Validator: the enum refuses anything outside draft|closed|published (pinned with withdrawn, open, Published, empty, a number). Executor: advisoryPreflight gives a target not in the table from = [] (Object.hasOwn guard) and the state fence's fromOf returns [] when to is set and from is not an array, so no advisory state passes; the sentence says the table names no such move. Before this PR an unknown target would have fallen back to ADVISORY_OPEN_STATES; this is a tightening.
  • ADVISORY_REFUSED_STATES emptied — right. Object.freeze({}); the enum's named hook stays wired (validate.mjs still honours spec.named), so a future by-name refusal needs only a row. The "published is reachable by NO row" pins moved with the ruling to "published is reached by ONE row (advisory_state) through ONE field, from draft alone, through the versions fence, and no request any row spells carries it in a path or a body".
  • The new advisory_versions row — right. Closed keys: required: [ghsa_id, versions], optional: []; any other key (summary, description, severity, state, advisory_state, collaborators, packages, vulnerabilities, vulnerable_functions, cve_id, credits, cvss_vector_string, body, issue) is refused by name (pinned). Shape: versions is its own field kind, 1..10 entries, each exactly package (NPM_PACKAGE_SHAPE, ≤214 bytes), vulnerable_version_range (one comparator, or a rising lower-then-upper pair, each "operator, one space, MAJOR.MINOR.PATCH" — no pre-release, build tag, caret, tilde, or-range, leading zero), patched_versions (1..5 release versions, comma-space separated, none repeated, none inside the entry's own range); an extra key or a missing key refuses the entry; a duplicate entry refuses the list. Every refusal is quiet (type and byte count, never the value; an unknown key name outside [a-z_]{1,40} prints as "(a key not echoed)"). Alone-ness: alone: true, pinned beside a comment, beside advisory_state, two of a kind. The PATCH body is the table's { vulnerabilities } only, each entry { package: { ecosystem: npm, name }, vulnerable_version_range, patched_versions } — no state, no prose, no function (pinned key-by-key).
  • "versions before publish" — right. ADVISORY_FENCES gains versions: for a publish it fails unless the GET lists ≥1 entry and every npm entry passes advisoryVersionEntryFailure (null range or patched versions — a draft as created — fails; a foreign-ecosystem entry fails). Execute.mjs pins four unversioned publishes refused, not sent, exit 5, no annotation. For the versions row itself the same fence refuses a list the PATCH would silently shrink: a listed package the action does not name, a nameless or foreign entry, or one carrying vulnerable_functions; and ADVISORY_VERSIONS_STATES = [draft] keeps it off triage, closed, published.
  • The publish read-back — right. advisoryStateReadBack for to = published additionally requires the PATCH answer's npm entries to equal, as a multiset, the entries the preflight GET read (advisoryVersionsDiff, order ignored); any foreign entry, missing or extra entry fails the action in counts alone; a landed publish alone returns ghsaId and the count, which confirmAdvisory hands to annotationFor (the landed object at the call site), so exactly one notice annotation names the id for a publish and none for a close, accept or reopen (pinned in execute.mjs and parsed back by dispatch.mjs's advisoryPublishes / matchRunAdvisories, which ignores an advisory_state annotation on a non-publish stroke). The versions row's own landing (advisoryVersionsReadBack) requires still-draft, the id named, and exactly the entries sent.
  • No advisory text, summary or description edit reachable by any row — holds. advisory_versions and advisory_state carry no summary/description key (refused by name); the executor builds the state body itself; the versions body carries only the four wire keys; REFUSED_PATH_FAMILIES still admits only the one-advisory path, the reports endpoint and the bare collection for a POST (pinned, with advisory_versions PATCH now in the enumerated request list).
  • Sender gate still target-write — holds. No senderGate is declared on any write row (the key exists only on advisory_read, still target-write); every stroke passes the executor's target-write sender gate before its first request, unchanged. The row comment in ops.mjs restates the 29160 ruling verbatim.
  • Card bullets accounted for: advisory_state gains the two transitions ✓; everything else refused ✓; one row sets only package/range/patched ✓; closed, shape-checked ✓; no prose, travels alone ✓; summary/description out of reach ✓; a publish stroke carries one advisory alone ✓ (alone, pinned with two publishes of different ids refused); annotation names the id ✓; read back as published with its versions ✓; ADVISORY_REFUSED_STATES loses published, header prose / row comments / self-tests / pins moved ✓; unknown state fails closed ✓; the three skill-text statements ✓ (see ④); CVE request, private fork, collaborator removal still unreachable ✓ (path families unchanged). The last bullet — one live publish after merge with the run id on the card — is post-merge by construction.

② Semver level

skip-changeset is right. The diff touches scripts/pm/fleet-write/** (repo tooling, no package.json, not published) and .claude/** (internal agent playbooks, never shipped); nothing in any released package moves, so no changeset is owed and no Clause-② / Metadata-protocol lines apply. Check Changeset is green on this head. No metadata-protocol change.

③ Boundary flags

  • (1) First live run confirms four platform facts — A, as recommended. The relay fails closed on each: a refused reopen or publish is a FAILED action carrying ADVISORY_ACCESS_REMEDY (never re-sent under a wider token, pinned); versions answered in another spelling fail the versions read-back; a publish answer that omits or re-spells vulnerabilities fails the publish read-back. Note the one consequence of that last fail-closed: if the platform's PATCH answer to a publish does not echo the vulnerabilities list, the advisory IS published while the relay reports FAILED and forbids a re-send — the seat must then read the advisory, not re-publish, which is what the row text says. That is the correct direction (no silent pass) and the card's own order (one approved publish, run id on the card) is the measurement. No throwaway advisory is needed; a throwaway would also be a public publish.
  • (2) The PATCH answer as the read-back — A, as recommended. Every other advisory row lands on its own PATCH answer; a second GET adds a request and no new fact, and the answer is the stored advisory on the two moves already measured live. Should the live run show the PATCH answer not carrying vulnerabilities, a follow-up may add the GET — judged then, on a measurement.
  • (3) The deleted line in security-findings.md §2 — read verbatim: 「中继运行日志仍打印整份载荷时,⛔ 机制不进 fleet-write 中继载荷:该日志公开。」 Its own condition ("while the relay run log still prints the whole payload") no longer holds: fleet-write.yml's header states the payload never rides a step's env: and both scripts read client_payload from $GITHUB_EVENT_PATH, validate.mjs's event-file battery pins that no env: names client_payload, and the executor's marker sweep (73 runs) pins that advisory prose reaches no log line, row, summary, annotation or pace line. The line also contradicted §3 (already on main before this PR), which routes the mechanism through advisory_create's payload by design. It carries no live rule; deletion A is right, and the ratchet (countLines counts every line; the file stays at 64) is green on this head.
  • Tier H out-of-scope stale comment in .github/workflows/fleet-write.yml (line 154 at this head): "FOUR write consumers … advisory_collaborators_add and advisory_state" — now five, and advisory_versions is unnamed. The mint input itself (permission-repository-advisories: ${{ steps.validate.outputs.permission_repository_advisories }}) needs no change and validate.mjs pins only that input line, so nothing is wrong at runtime. Should-fix, as a one-line Tier H follow-up (a card, or the next PR that touches the workflow by the maintainer's hand) — correctly left out of this Tier S PR.
  • Landing blocker, not a tree defect: PR-body guards is red — "PR feat(pm): the fleet-write relay publishes an approved security advisory — reopen, set the versions, publish #30135 closes pm/fleet-write: the relay publishes a security advisory on the maintainer's list: reopen, set the affected and patched versions, publish #30110 with no Claim: on the card naming claude/issue-30110-relay-advisory-publish". The card carries no Claim: and no assignee (the dev reports it worked from the dispatch order and wrote neither, as the executor must). Per AGENTS.md the dispatching seat owes step 1 (assignee) and step 2 (the Claim: naming the dev's branch); Tier S landing requires every check green. The owning seat must post that Claim: through the relay and re-run the guard before enqueueing. Every other check-run on this head is success or skipped (path-filtered); none is still running at the time of writing.
  • §6 discipline — clean. The diff, the three commits (c2e1c1da88, e3715819f5, b1337ab86c, plus the merge 5aa0bddcdc), the PR body and the skill text name no real advisory id (only the fixture GHSA-abcd-2345-wxyz and its reversals), no advisory text and no mechanism; the two run ids quoted (41443030055, 41443112178) are public relay runs. The commit trailers are the model-free pair; the PR body footer is the session-URL form.

④ Design judgment

For each governed file, does the rule text match what the relay now enforces, and the maintainer's ruling?

  • security-findings.md — §3 gains 「只发维护者为该版批准的清单:经中继先定版本再发布;运行编号记座位贴,⛔ 无正文。」 This is the card's three statements in one line: approved list for that release only; versions first, then publish (the relay's versions fence makes "first" mechanical — a publish without versions is NOT SENT); run ids on the seat post, ids only. 「发布归维护者」 leaves §3's App-identity bullet, and §9 now reads 「发布清单归维护者」 — the standing rule 「哪些高危要发布应该在我版本发布前决策吧」 kept, the superseded 「发布留给我」 clause gone. Matches the code and the ruling; the ruling itself is quoted verbatim (byte-identical to the card body, full-width comma included) in ops.mjs's header with the old clause marked SUPERSEDED, which is where this repo keeps provenance (rules only in the skill text).
  • lanes/security.md — 「维护者批准清单后,本席经中继发布,照 ../security-findings.md 第 3 节;⛔ 不发清单外的。」 replaces "publish by hand in the UI, the relay cannot move a closed advisory". Correct in both directions: the relay now can (reopen is closed → draft), and the rule points at one place (§8's one-place rule) rather than restating. Line 42 (close via advisory_state on fix merge) still holds — closed from draft is unchanged.
  • rest-channel.md — 「公告经中继 advisory_create(首选)等操作,发布亦经中继;规则住 security-findings.md。」 drops the stale "four operations" count and "publishing is the maintainer's". Matches.

Attempts to break it, judged on the tree:

  • A publish without versions — refused at the versions fence (none listed; null range/patched; a re-spelled range; a patched version inside its range), PATCH not sent, exit 5, no annotation. Pinned in both validate.mjs (fence, pure) and execute.mjs (end to end).
  • A versions PATCH that wipes entries — a listed package the action does not name is refused (the PATCH replaces the whole array, so this is the only way an entry could vanish); a foreign-ecosystem or nameless entry and one with vulnerable_functions (which the row never writes back) are refused too; and the landing demands the answer list exactly what was sent. The row may still replace the lines of a package it names (two lines → one) — that is the row's purpose and the read-back shows it; and it may add a package the advisory did not yet list, which is within its closed keys (package is one of them, as it is for advisory_create). Not a defect.
  • closed straight to published — ADVISORY_TRANSITIONS.published = [draft]; execute.mjs pins closed → published refused at the state fence with the PATCH not sent, and the dev's ablation A (publish also from closed) reds two validate pins and one execute pin.
  • A versions row smuggling prose — package must match NPM_PACKAGE_SHAPE (lowercase, no spaces), the range and patched strings must match the two version regexes exactly, an unknown entry key is refused by name, summary/description beside the row are refused by name, and every refusal is quiet (validate.mjs pins the marker in the list, an entry, each of the three keys and an entry key, none echoed; execute.mjs's 73-run sweep covers the versions and publish runs).
  • A stroke carrying a publish plus another op — alone: true on every advisory row: [versions, publish], [publish, publish-of-another-id], [publish, comment] are refused at the validator before any request and at the seat's packer (dispatch.mjs). The token's repository-advisories: write is minted for that one stroke only (tokenPermissionsOf pinned for the publish stroke).
  • The annotation leaking the id early — annotationFor emits for advisory_state only when req.advisoryState.to === published AND landed.ghsaId is set, which advisoryStateReadBack returns only on a landed publish; a close/accept/reopen landing returns no id (pinned). The id is public once published, which the card names as the point.
  • Dead or phantom tests — the three self-test batteries' floors rise with the cases (validate 51 → 77, execute 32 → 41, dispatch 14 → 17) and are named rosters, so the new cases cannot silently vanish; CI's Lint · repo & tooling gates runs all three and is green at this head.

Findings. Must-fix: none. Should-fix (out of this PR's reach, filed as follow-ups): the fleet-write.yml "FOUR write consumers" comment (Tier H); the missing Claim: on #30110 (a dispatch act, owed before landing). Nothing in the three governed files or the four relay scripts needs to change for this head to land.

Implemented-by: claude/issue-30110-relay-advisory-publish
Reviewed-by: session_01QSeKYn5CmmdShCJifm6WYj

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

security#1 · os-justin · session_01QSeKYn5CmmdShCJifm6WYj

席位核对

ACCEPT · head 5aa0bddcdc7906ccabd26154feb1c207bb383f20 · 2026-11-04T15:58Z

  • Shape: 7 files, +714/−190. Every governed path is under .claude/** and Tier S, so the PR lands on an at-tier contract review in the seat. skip-changeset: repo tooling and agent playbooks, nothing published.
  • Rule held (the maintainer's ruling in pm/fleet-write: the relay publishes a security advisory on the maintainer's list: reopen, set the affected and patched versions, publish #30110, 「中继不允许把公告设为已发布 ,立卡解决」):
    • The relay can reopen a closed advisory (closed → draft).
    • It sets the affected and patched versions through a new row with closed keys.
    • It publishes (draft → published) only once the versions are set.
    • Nothing leaves published or withdrawn; an unknown target fails closed; no row reaches an advisory's text.
    • The skill text says the seat publishes only the maintainer's approved list for that release: versions first, run ids on the seat post.
  • Contract review: comment 6635092528. Served-tier: CONTRACT_REVIEW_TIER, Head-sha equals this head, VERDICT PASS, no must-fix. The dev's three open questions are answered A:
    • the first live publish measures the four platform facts;
    • the PATCH answer is the read-back;
    • the security-findings.md §2 deletion is right.
  • Check-runs at this head (latest per name): 18 skipped, 23 success. PR-body guards first read red because the dispatching seat had not posted the card's Claim:. The claim is now posted, and the guard re-ran green (job 126683688358). mergeable_state: clean.
  • Follow-ups, not for this landing:
  • Landing: ready, then auto-merge.

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

Labels

documentation Improvements or additions to documentation size/l skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pm/fleet-write: the relay publishes a security advisory on the maintainer's list: reopen, set the affected and patched versions, publish

1 participant