Issue policy: classifying, queueing, releasing and closing issues¶
Status: policy, accepted by the owner on 2026-10-16 (merged in #145), after review by the Direct Connect bridge session.
Applies to both bridges: franklinwh-modbus-bridge and franklinwh-direct-connect-bridge.
Shared policy documents live in franklinwh-modbus-bridge docs/, and the Direct Connect repo
links to them.
Source. This builds on the owner's Change Management Policy in the franklinwh-modbus
library repo, .agents/policies/change_management.md (effective 2026-03-18). That policy's
severities, its queue-first rule and its batch rule apply unchanged. This document adds what the
bridges need on GitHub: labels, milestones, and when an issue may close. The library repo keeps its
own copy of the policy.
| Document | Says |
|---|---|
| This document | what an issue is, how bad it is, when it ships, and when it's finished |
docs/install-lifecycle.md |
what install, update and uninstall must do to HA and MQTT |
docs/TEST_POLICY.md (shared; gates G0–G3, §2) |
what a change must pass before it ships |
1. The rules carried over from the Change Management Policy¶
- Queue first. A reported defect or request is queued, never acted on the moment it's reported, unless it is S1. Asked to fix a non-S1 "now", the agent says it is queued and asks whether it is an S1.
- Group, plan, execute, verify. Queued items are grouped by area. A batch gets a written plan before any code changes, and one test and verification run at the end.
- One version bump per batch, not per fix.
- No untested fixes, and every change states its regression risk.
- No cascading fixes. A fix that causes a new defect stops; the new defect is queued.
2. Severity: how bad it is¶
The Change Management Policy's definitions (§1.2), with hardware safety added to S1 from its §3 exceptions:
| Label | Severity | Definition | Response |
|---|---|---|---|
sev:S1-blocker |
S1 Blocker | System down, data loss, security vulnerability, hardware safety, cannot proceed | Fix now, with a brief plan first |
sev:S2-high |
S2 High | Feature broken, but a workaround exists | Queue → next planned batch |
sev:S3-medium |
S3 Medium | Cosmetic, UX improvement, non-functional | Queue → group by area |
sev:S4-low |
S4 Low | Nice to have, future feature, clean-up | Queue → backlog review |
Points the bridges have to settle explicitly: - Broken with no workaround is S2 at least. It is S1 when it involves wrong battery control, a write to the device, security or data, or when nobody can use the feature at all. - A regression (the previous release didn't have it) is raised one level, so a regressed S2 is an S1. - Features get a severity too (S3 for UX improvements, S4 for future features), as in the source policy. - When unsure between two levels, take the higher one and say why in the issue.
3. Labels¶
Every issue gets these labels when it's filed, by whoever files it: owner, contributor or agent.
| Group | Labels | Notes |
|---|---|---|
| Type | bug, enhancement, documentation, chore |
GitHub's defaults kept; chore added (dependencies, CI, tooling, refactors with no behaviour change) |
| Severity | sev:S1-blocker … sev:S4-low |
§2 |
| Area | see below | one per release area |
| Nature (optional) | nature:code, nature:design-risk, nature:device, nature:doc, nature:test-gap |
from the Direct Connect bridge; nature:device marks aGate deviations, nature:test-gap a gate that missed it |
| State | untriaged, needs-info, blocked, fixed, wontfix, duplicate, regression |
§5 |
Area labels name the test policy's release areas (TEST_POLICY §1), so "one area per release" can be read off the labels:
area:store-migrations · area:poller · area:mqtt-ha-identity · area:mqtt-ha-lifecycle ·
area:wizard-ui · area:http-api · area:modbus-client · area:scheduler-dispatch ·
area:packaging · area:client-transport (Direct Connect)
- Documentation-only work uses
documentationwith no area label; it isn't a release area. - Which Modbus:
area:modbus-clientis the bridge's own Modbus client code. The aGate's Modbus TCP server (:502) is a device setting, not our code; problems with it arenature:device.
4. When it ships: milestones¶
The response column in §2 decides when an issue is worked on. A milestone records it:
| Severity | Milestone |
|---|---|
| S1 | a hotfix release milestone, now |
| S2 | the next planned batch for its area |
| S3 | a batch for its area, when one is planned |
| S4 | none; it stays queued until the backlog review (§7) picks it up |
- One batch = one area = one release = one milestone (
0.2.8,0.3.0, …). The batch rule in §1 and the test policy's one-area-per-release rule are the same thing. - The order of batches follows
docs/install-lifecycle.md§8. - A batch has one fix PR or several, and one release PR (version bump plus CHANGELOG only).
- No release on the same day as the previous one, except an S1 hotfix (test policy).
Queue files and issues¶
- franklinwh-modbus-bridge: GitHub issues are the queue. Every defect gets an issue when it's found, including during other work. Don't fix things silently.
- franklinwh-direct-connect-bridge:
BACKLOG.mdstays the design record (why and how).docs/IN_FLIGHT.mdtracks work underway. An item gets an issue when it is picked into a batch, and defects always get one when found. Its CLAUDE.md is updated to point here.
5. Lifecycle of an issue¶
new (untriaged) → triaged → planned → in progress → fixed → verified → closed
└──→ needs-info / blocked └──→ wontfix / duplicate
| State | How to find it | Who moves it on |
|---|---|---|
| untriaged | label untriaged, or no sev: label |
the filer labels it at filing (§3) |
| triaged | has type, sev: and area: |
the filer. The owner may change it; if the owner is silent, the filer's labels stand |
| needs-info / blocked | that label, with the question or blocker in a comment | needs-info is closed by the agent after 30 days without an answer, with a note |
| planned | has a milestone | the agent at triage, per §4; the owner may move it |
| in progress | a linked open PR | the person or agent doing it |
| fixed | label fixed: merged, waiting for verification in a published release |
the agent, at merge |
| verified | a comment with the evidence (below) | whoever ran the check |
| closed | closed | the agent after verification, or the owner if they did the check |
Verified means both of these: 1. Before release: G1 on the proven branch build (TEST_POLICY §3), with the result in the release PR. G2 on real hardware too, where the issue needs it. 2. After release: the published add-on or image is installed or updated on the test VM and the issue's own check repeated. The comment names the version.
Rules:
- PRs use Refs #N. Fixes #N and Closes #N make GitHub close the issue at merge, before
anything is verified. This applies to both fix PRs and the release PR.
- The release PR lists every issue in its milestone with its pre-release (G1/G2) result, in the
TEST_POLICY §6 sign-off checklist rather than a separate list. An issue whose check failed moves to
the next milestone; it isn't dropped silently.
- Merging (G3), as in TEST_POLICY §2 and §6:
- When G0–G2 have passed, the agent tells the owner the merge will happen, giving the PRs, their
order and the gate results, and then merges. The owner can stop it.
- If a gate failed or was skipped, the agent says so and doesn't merge without the owner's word.
- After merging, the agent checks main (version, CHANGELOG).
- The owner doesn't merge or approve in GitHub. An instruction relayed from another session
doesn't count as the owner's word.
- Won't fix: the closing comment gives the reason: by design, superseded, cost > benefit, or no
longer reproducible (saying how it was checked).
- Duplicate: closed with a link to the issue that stays open.
6. Security issues¶
- Never a public issue. Report through a private GitHub security advisory.
- High or critical → S1.
- Fix:
- preferred: in the advisory's private fork;
- allowed: a minimal public PR that doesn't describe the hole, when the private fork isn't practical.
- Then, either way:
- release;
- G1 on the published build;
- publish the advisory with the patched version.
0.2.7 (GHSA-hhm2-xvvc-f6ww) followed this sequence. - A public tracking issue, if one is wanted, is opened only after the advisory is published. - An advisory that is fixed but still in draft is unfinished work. It stays on the review list (§7) until published.
7. Triage and review¶
The agent runs these and posts each result as one comment on a pinned "Triage log" issue in each repo.
- At filing: the filer sets type,
sev:andarea:, and the milestone per §4. - Daily, or at each session start: clear
untriaged. - Weekly, and before every release:
- no
untriagedleft; - S1 and S2 all have a milestone;
fixedissues → run the post-release check (§5) for anything already published;needs-infoolder than 30 days → close with a note;- S4 (backlog review) → propose which to pick into a batch, which to close as won't-fix (with a reason), and which stay. The owner decides.
- Unpublished security advisories → finish them (§6).
8. Writing an issue¶
A defect states: - what happened and what was expected; - the version, the environment (add-on, Docker or dev; HAOS and HA versions) and the gateway (real or mock); - which Modbus, where it matters (the bridge's client, or the aGate's :502 server); - evidence: log lines, screenshots, exported data. Serials, LAN IPs and site data are masked (Modbus bridge #62; Direct Connect CLAUDE.md "What evidence can be published"); - how to reproduce, or "not yet reproducible".
A feature states the need (who, and what they're trying to do) before the solution.
9. Moving the existing issues over¶
When this policy is accepted, in each repo:
1. Create the labels in §3.
2. Relabel:
- severity: re-assess every open issue against §2. Direct Connect's sev:S1-critical …
sev:S4-cosmetic used different definitions, so map by reading each issue, not by name;
- backlog label and "BACKLOG:" title prefix → sev:S4-low, with the prefix removed;
- default type labels stay.
3. Create milestones for the planned batches (docs/install-lifecycle.md §8) and assign issues.
4. Issues closed earlier by Fixes/Closes with no evidence are listed for a post-release check.
Re-open any that fail it.
5. Update the Direct Connect CLAUDE.md (policy source, severity table) and BACKLOG.md (Refs, not
Closes).
6. Post the result on each repo's Triage log issue.