Skip to content

Commit 41bd4b1

Browse files
committed
docs: improve docs
1 parent eabb6e1 commit 41bd4b1

12 files changed

Lines changed: 15 additions & 73 deletions

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
- The **Conflicts** column on the change request list now matches netbox-branching: a red octagon when there are conflicts, a dash when there are none.
88
- The README now warns that the plugin is below 1.0 and that models, settings and the REST API can still change.
9+
- Improved the docs.
910

1011
## 0.2.0 - 2026-08-27
1112

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ Full documentation is in [`docs/`](docs/index.md).
7474
| [Automatic behaviours](docs/automation.md) | Stale reviews, reevaluation, notifications. |
7575
| [Permissions](docs/permissions.md) | What each role needs. |
7676
| [REST API](docs/api.md) | Every endpoint. |
77-
| [Extending this plugin](docs/extending.md) | How another plugin adds content, tabs and checks. |
77+
| [Extending this plugin](docs/extending.md) | How another plugin adds content and checks. |
7878
| [Design](docs/design.md) | Why it is built this way. |
7979

8080
## Features

‎docs/checks.md‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ Create a policy with **no object types**, which matches every branch, and name t
7474
| Registered checks | `has-changes`, `no-conflicts`, `not-stale` |
7575

7676
> [!IMPORTANT]
77-
> Give that policy a rule as well. A change request is not satisfied while no rule applies to it, so a checks-only policy which is the only one matching a branch leaves the request unable to be approved. The approval panel says "No policy rules apply to this change request" when this happens.
77+
> A change request is not satisfied while no rule applies to it, so a checks-only policy which is the only one matching a branch leaves the request unable to be approved. The approval panel says "No policy rules apply to this change request" when this happens.
7878
7979
## Which checks apply, and where
8080

@@ -160,6 +160,3 @@ def out_of_hours_signoff(change_request):
160160
```
161161

162162
A skipped check clears the gate, so this blocks only the changes it is about. The cost is a visible row on every request carrying the policy, whatever the answer. Use both together: the policy narrows the check to the changes it could apply to, and the skip handles what a policy cannot express.
163-
164-
> [!TIP]
165-
> `threads-resolved` is the one to consider carefully. It means an open question about any single object blocks the whole merge, which is strict but stops a concern being lost when the branch diff disappears on merge. Name it only on the policies where that strictness is wanted, rather than on your catch-all.

‎docs/conflicts.md‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,3 @@ Branching has no merge-conflict editor. It does not blend the two values. Either
6363
2. **Make the two sides agree.** Edit the object in main to match your branch, or activate the branch and edit it there to match main. Either way the conflict clears on its own, and the banner and the check follow immediately.
6464

6565
Syncing does not resolve a conflict on its own. It applies main's value onto the branch, which clears the flag at that moment, and it only replays changes newer than the last sync. If main has not moved since your last sync, syncing again does nothing.
66-
67-
> [!NOTE]
68-
> We would rather delete this code. If `netbox-branching` advanced a diff's baseline on sync, the way git moves the merge base, its own flag would be correct and this plugin could simply trust it.

‎docs/custom-checks.md‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class MyPluginConfig(PluginConfig):
7070
The check now runs whenever checks are refreshed: when the branch is synced or reverted, when a comment thread changes, and when someone presses **Re-run checks**.
7171

7272
> [!TIP]
73-
> A check that raises is recorded as `error` with the exception text as its summary. A broken check blocks the merge but never breaks the page, so you can ship one without fear of taking NetBox down.
73+
> A check that raises is recorded as `error` with the exception text as its summary. A broken check blocks the merge but never breaks the page.
7474
7575
> [!IMPORTANT]
7676
> `name` is the database key. Renaming it creates a new check and deletes the old one, discarding its history. Change the `label` freely; leave the `name` alone.
@@ -159,13 +159,10 @@ Register it as **advisory**:
159159
register_check('ai-review', 'AI review', ai_review, required=False)
160160
```
161161

162-
> [!CAUTION]
163-
> Register an AI check with `required=False`. A model's opinion should inform a human reviewer, not silently block a merge, and it will sometimes be wrong in both directions. Make it required only once you have watched its verdicts on real changes for a while.
164-
165162
> [!WARNING]
166-
> Every check runs on **every** refresh, including each time a comment thread changes. An API call there costs money and adds latency to saving a comment. Before shipping this, consider caching the verdict against the branch's last change time, or moving the call into a background job that reports its result through the REST API like any other external check.
163+
> Every check runs on **every** refresh, including each time a comment thread changes. An API call there costs money and adds latency to saving a comment.
167164
168-
Two more things worth knowing. The whole diff goes to a third party, so do not use this if your inventory is sensitive without checking that first. And the model sees only what `_summarise` sends: it has no view of the wider network, so it cannot reason about anything outside the diff.
165+
The whole diff goes to a third party. The model sees only what `_summarise` sends: it has no view of the wider network, so it cannot reason about anything outside the diff.
169166

170167
## Checks reported by an external system
171168

‎docs/extending.md‎

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,13 @@
11
# Extending this plugin
22

3-
Another plugin can add content to this plugin's pages, tabs to its models, and checks to its merge gate. Nothing special is required of you.
3+
Another plugin can add content to this plugin's pages, and checks to its merge gate. Nothing special is required of you.
44

55
## Injecting content into a page
66

77
Every model here supports NetBox's standard `PluginTemplateExtension` hooks. This is a NetBox feature, and the [NetBox documentation](https://netboxlabs.com/docs/netbox/en/stable/plugins/development/views/#extra-template-content) is the reference for it.
88

99
All six hooks (`buttons`, `alerts`, `left_page`, `right_page`, `full_width_page` and `list_buttons`) work on `policy`, `policyrule`, `changerequest`, `review` and `mergecheck`.
1010

11-
> [!NOTE]
12-
> Page content appears on an object's own page, not on its tabs. This matches core NetBox, where the Changes and Reviews tabs render their own templates.
13-
14-
## Adding a tab
15-
16-
`register_model_view` attaches a tab to any of this plugin's models:
17-
18-
```python
19-
from netbox.views import generic
20-
from utilities.views import ViewTab, register_model_view
21-
22-
from netbox_change_control.models import ChangeRequest
23-
24-
25-
@register_model_view(ChangeRequest, 'tickets', path='tickets')
26-
class ChangeRequestTicketsView(generic.ObjectView):
27-
queryset = ChangeRequest.objects.all()
28-
template_name = 'my_plugin/changerequest_tickets.html'
29-
tab = ViewTab(label='Tickets', badge=lambda obj: 1)
30-
```
31-
3211
## Adding a pre-merge check
3312

3413
The most useful extension point is a check that gates the merge. See [Writing your own checks](custom-checks.md).

‎docs/index.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,5 @@ New here? Read [Installation and configuration](installation.md), then [Policies
3838
|---|---|
3939
| [Automatic behaviours and notifications](automation.md) | Stale reviews, policy reevaluation, notifications, the dashboard widget. |
4040
| [REST API](api.md) | Every endpoint. |
41-
| [Extending this plugin](extending.md) | Injecting content into these pages, adding tabs, and adding checks. |
41+
| [Extending this plugin](extending.md) | Injecting content into these pages, and adding checks. |
4242
| [Design](design.md) | Why the plugin is built the way it is. |

‎docs/merging.md‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,3 @@ Fix it either way round: widen the window, or lower `auto_merge_interval`. Leave
7979
Setting `enable_auto_merge` to `False` skips registering the job entirely, so a site that does not use auto-merge gets no periodic job and no Job records from this plugin.
8080

8181
The merge runs as the requester. Set `enable_auto_merge` to `False` to disable the feature globally without editing any request.
82-
83-
> [!CAUTION]
84-
> Auto-merge removes the last human pause before a change reaches production. Pair it with a change window, and consider leaving it off for policies that govern high-risk object types.

‎docs/permissions.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ The two exemptions are **custom actions on an object type**. Grant them under **
2424
Enter the action in the permission's *additional actions* field, not the view/add/change/delete checkboxes.
2525

2626
> [!NOTE]
27-
> Superusers hold every permission, so they are always exempt from both. Test an exemption with an ordinary account.
27+
> Superusers hold every permission, so they are always exempt from both.
2828
2929
> [!IMPORTANT]
3030
> NetBox resolves a permission name as `<app_label>.<action>_<model>`, splitting on the **last** underscore. A custom permission whose trailing component is not a real model name can never be granted by an object permission, and the exemption silently ends up superuser-only. This is why the bypass lives on `Policy` and the window override on `ChangeRequest`.

‎docs/policy-conditions.md‎

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ Three things decide whether the condition matches.
4747
**Related objects appear as numeric ids**, because the diff stores them that way. So a site is `{"attr": "site", "value": 5}`, not the site's name. Use the object type scope for anything you would otherwise express as a name, and keep conditions for plain values such as status, a boolean flag, or a string.
4848

4949
> [!TIP]
50-
> A condition naming a field the object does not have simply does not match; it does not error. That means a typo in `attr` silently produces a policy that never applies. Check a new condition against a real branch before relying on it.
50+
> A condition naming a field the object does not have simply does not match; it does not error. That means a typo in `attr` silently produces a policy that never applies.
5151
5252
> [!NOTE]
53-
> Conditions cost a scan of the branch diff per condition-bearing policy. Policies scoped only by object type cost nothing extra, so prefer the object type scope where it is sufficient.
53+
> Conditions cost a scan of the branch diff per condition-bearing policy. Policies scoped only by object type cost nothing extra.
5454
5555
## Which side a condition reads
5656

@@ -70,11 +70,8 @@ The default is **either**, because a policy exists to catch changes. When a cond
7070

7171
A creation has no before, and a deletion has no after, so each of those is matched on the one side it has whatever the setting.
7272

73-
> [!IMPORTANT]
74-
> `after` and `before` are deliberate narrowings, and getting one wrong fails quietly: no policy attaches, no review is asked for, and nothing on the change request says a protection you expected did not apply. Leave the setting on **either** unless you can say precisely why not.
73+
### Main is never read directly
7574

76-
### Main is never consulted
75+
The diff also holds `current`, the object as it stands in main right now. Conditions never read it. A colleague editing that same object on main cannot, on their own, pull your change request into a policy or push it out of one. Which policies govern a change depends on the change.
7776

78-
The diff also holds `current`, the object as it stands in main right now. Conditions never read it.
79-
80-
If they did, a colleague editing the same object on main could pull your change request into a policy, or push it out of one, without touching your branch. Which policies govern a change should depend on the change.
77+
A sync is different, and deliberately so. Syncing makes main's values your branch's values, and branching writes them into `modified`. A condition then reads them like anything else in the branch. A branch which touches only a device's description will still see `modified` carry `status: offline` once it syncs that change from main, so a policy conditioned on `status == active` and read **after** stops matching. Read against **either** side it keeps matching, because `original` still holds `active`.

0 commit comments

Comments
 (0)