# nb-09 — From permit to duties and powers
*Northbridge course, intermediate ([nb-05](/tutorials/northbridge/nb-05-suitable-applicant/) →
[nb-06](/tutorials/northbridge/nb-06-register-silence/) → [nb-07](/tutorials/northbridge/nb-07-document-vs-fact/) →
[nb-08](/tutorials/northbridge/nb-08-edition-and-terms/) → [nb-09](/tutorials/northbridge/nb-09-duties-powers/)).
All law is fictional; every office, notice, revocation and towing rule is
synthetic and unofficial. No real municipal deployment or legal-validity
claims. Engine `law 0.1.0`, semantics `law.core/0.2`.*
## Situation
Ann filed her parking-permit application on 1 March 2026. Filing is
not the end of the story — it starts several directed relations at
once. The permit office now owes Ann a decision within thirty days.
Ann, once eligible, must not resell her permit to anyone during 2026.
If she does resell it, the office may revoke it by sending a revocation
notice — and only then. If the office refuses her outright, she may
object during March. And while she displays a valid permit, the office
may not have her car towed.
The eligibility lessons answered "may Ann have a permit?":
[nb-01: First permit: facts, a rule and a question](/tutorials/northbridge/nb-01-first-permit/),
[nb-02: Why a missing fact is not a refusal](/tutorials/northbridge/nb-02-missing-fact/),
[nb-03: Exceptions and conflicting rules](/tutorials/northbridge/nb-03-exceptions/). This
article answers what happens next: who owes what to whom, what counts
as doing it or breaking it, and which acts change the legal state. Law
DSL expresses those relations as five position constructs: **duty**,
**prohibition**, **power**, **liberty**, and **immunity**, each shown
with its meaning in place below.
## Prerequisites
[nb-01: First permit: facts, a rule and a question](/tutorials/northbridge/nb-01-first-permit/):
facts, strict rules, `law test` as the way to check a claim.
[nb-02: Why a missing fact is not a refusal](/tutorials/northbridge/nb-02-missing-fact/):
the four truth statuses — `NEITHER` below means "no conclusion either
way", never a refusal.
[nb-03: Exceptions and conflicting rules](/tutorials/northbridge/nb-03-exceptions/): strict
rules and how conflicts resolve.
[nb-08: Which edition applies and when the term expires](/tutorials/northbridge/nb-08-edition-and-terms/):
the `CALENDAR_DAYS` deadline policy — the decision duty's window end
(`due`) is computed by the `DecisionDeadline` rule with that policy,
and the duty tests set the policy in their context.
New here: positions are not truth claims. `truth(...)` says whether a
proposition holds; `positions()` says what lifecycle state each duty,
prohibition, power, liberty and immunity is in: `ACTIVE` (in force,
outcome still open), `SATISFIED` (goal met), `VIOLATED` (goal failed),
`UNDETERMINED` (too little known to say either).
## Minimal example
All fragments are excerpts from
`packs/examples/language-demo/permits/package.law` (identifiers as
written; package header, imports and unrelated rules cut).
Excerpt 1 — the office's duty to decide (lines 77–90). A **duty** names a
bearer o (who owes), a beneficiary (to whom it is owed), and a goal: here
an achievement goal with a condition and a window.
```law
rule DecisionDuty strict {
for a: Applicant;
for o: Office;
for on, due: Date;
when demo.northbridge.vocabulary::application_filed(a, on) and decision_due(a, due) and demo.northbridge.vocabulary::permit_office(o);
then duty NotifyDecision {
bearer o;
beneficiary a;
goal achievement {
condition demo.northbridge.vocabulary::decision_notified(a);
window [on, due];
}
};
}
```
One idea: an **achievement** goal is met when its condition becomes true
at least once inside the window — notify Ann once before the due date and
the duty is satisfied.
Excerpt 2 — Ann's prohibition on resale (lines 92–102). A **prohibition**
names a bearer a, a beneficiary, a forbidden action, and a window.
```law
rule NoResale strict {
for a: Applicant;
for o: Office;
when permit_eligible(a) and demo.northbridge.vocabulary::permit_office(o);
then prohibition NoPermitResale {
bearer a;
beneficiary o;
action demo.northbridge.vocabulary::resells_permit(a);
window [@2026-01-01, @2026-12-31];
};
}
```
One idea: the mirror image of a duty — instead of "bring about this
condition", "do not perform this action" for the whole of 2026.
Excerpt 3 — the maintenance duty (lines 149–162). Same parties shape, a
different goal kind: **maintenance** must hold at every point of the
window, not just once.
```law
rule MaintainEligibility strict {
for a: Applicant;
for o: Office;
for on: Date;
when demo.northbridge.vocabulary::application_filed(a, on) and demo.northbridge.vocabulary::permit_office(o);
then duty MaintainConditions {
bearer a;
beneficiary o;
goal maintenance {
condition permit_eligible(a);
window [@2026-01-01, @2026-12-31];
}
};
}
```
One idea: staying eligible all year is a duty too — one counterexample
(fines on 1 June) violates it, while one good day proves nothing.
Excerpt 4 — the revocation power (lines 164–175). A **power** names a
holder, the party it is over, the exercise act, the `valid_when` grounds,
and the `effect` the exercise creates.
```law
rule RevocationPower strict {
for a: Applicant;
for o: Office;
when demo.northbridge.vocabulary::permit_office(o) and permit_eligible(a);
then power RevokePermit {
holder o;
over a;
exercise revocation_notice(o, a);
valid_when (demo.northbridge.vocabulary::resells_permit(a));
effect create(permit_revoked(a));
};
}
```
One idea: the notice is only an exercise — `permit_revoked(a)` appears
solely when the grounds (`resells_permit`) hold too.
Excerpt 5 — the liberty to object (lines 177–187). A **liberty** names a
holder, the party it is against, a permitted action, and a window.
```law
rule ObjectionRight strict {
for a: Applicant;
for o: Office;
when permit_refused(a, o);
then liberty MayObject {
holder a;
against o;
action objects(a, o);
window [@2026-03-01, @2026-03-31];
};
}
```
One idea: a refusal opens a one-month window in which objecting is
permitted — the liberty exists only because the refusal happened.
Excerpt 6 — immunity against towing (lines 189–199). An **immunity**
names a holder, the party it is against, a protected effect (something
that must not be brought about against the holder), and a window.
```law
rule NoTowing strict {
for a: Applicant;
for o: Office;
when permit_displayed(a, o);
then immunity NoTowingWithPermit {
holder a;
against o;
protected_effect vehicle_towed(o, a);
window [@2026-01-01, @2026-12-31];
};
}
```
One idea: displaying the permit shields Ann from one specific effect —
towing — without granting anything else.
## Command and result
```sh
law test packs/examples/language-demo/permits
```
Observed result (engine `law 0.1.0`) — the ten position, power, liberty
and immunity lines of the run plus its summary:
```text
ok [demo.northbridge.permits] tests/permits.lawtest / positions: duty and prohibition active
ok [demo.northbridge.permits] tests/permits.lawtest / duty satisfied
ok [demo.northbridge.permits] tests/permits.lawtest / duty violated
ok [demo.northbridge.permits] tests/permits.lawtest / outcome unknown — no violation
ok [demo.northbridge.permits] tests/permits.lawtest / maintenance holds while the window is open
ok [demo.northbridge.permits] tests/permits.lawtest / maintenance violated by counterexample
ok [demo.northbridge.permits] tests/permits.lawtest / without a certificate the post-window outcome is unknown
ok [demo.northbridge.permits] tests/permits.lawtest / power exercised lawfully
ok [demo.northbridge.permits] tests/permits.lawtest / power without grounds does not operate
ok [demo.northbridge.permits] tests/permits.lawtest / liberty and immunity
итого: 28 проверено, 28 прошли, 0 не прошли, 0 не исполнены; код 0
```
The package also passes the static check:
```sh
law engine check packs/examples/language-demo/permits/package.law
```
```text
check OK: packs/examples/language-demo/permits/package.law
```
What the decisive tests assert (`evaluate positions()`, except the two
power tests, which ask `truth(permit_revoked(ann))`):
| Test | Setup | Expects |
|---|---|---|
| `positions: duty and prohibition active` | filed 1 March, office known, judged 5 March | `NotifyDecision` ACTIVE, `NoPermitResale` ACTIVE |
| `duty satisfied` | filed 2 March, notified, judged 10 April | `NotifyDecision` SATISFIED |
| `duty violated` | filed 2 March, explicitly not notified, judged 10 April | `NotifyDecision` VIOLATED |
| `outcome unknown — no violation` | filed 2 March, notification neither asserted nor denied, judged 10 April | `NotifyDecision` UNDETERMINED |
| `maintenance holds while the window is open` | still eligible, judged 1 June | `MaintainConditions` ACTIVE |
| `maintenance violated by counterexample` | fines recorded, judged 1 June | `MaintainConditions` VIOLATED |
| `without a certificate the post-window outcome is unknown` | filed 2 March, no certificate facts either way, judged 5 January | `MaintainConditions` UNDETERMINED |
| `power exercised lawfully` | notice sent and resale recorded | `permit_revoked` TRUE_ONLY |
| `power without grounds does not operate` | notice sent, no resale recorded | `permit_revoked` NEITHER |
| `liberty and immunity` | refused and permit displayed, judged 10 March | `MayObject` ACTIVE, `NoTowingWithPermit` ACTIVE |
All 28 tests pass: each answer matched its expectation. A passing
test is not a ruling in anyone's favour — it only says the engine's
answer agreed with the test's `expect` line. The summary is in
Russian: `итого: 28 проверено, 28 прошли, 0 не прошли, 0 не исполнены;
код 0` — 28 checked, 28 passed, 0 failed, 0 skipped, exit code 0.
Note the dates doing work: the achievement duty is judged on 10 April,
after the 30-day window from the 2 March filing has closed — so its
outcome is decidable. The maintenance duty is judged on 1 June,
mid-window — still open, hence ACTIVE while no counterexample exists.
## Why this construct
The task is to turn "the office must decide, Ann must not resell, the
office may revoke on resale, Ann may object, towing is off-limits"
into machine-checkable relations with named parties and observable
lifecycles.
Each of the five answers a different question the desk clerk actually
asks. Duty and prohibition: who owes what to whom. Power: can this act
change her legal state. Liberty: may she do this. Immunity: are we
barred from doing that to her. One construct per question — no
overloading.
The ten tests are the proof: every lifecycle state (ACTIVE, SATISFIED,
VIOLATED, UNDETERMINED) and both power outcomes (TRUE_ONLY, NEITHER)
are executed by `law test`, not asserted in prose.
What is not proven: that these are the *right* municipal rules. The
tests prove the machine tracks the declared duties and powers; no test
can prove Northbridge chose wisely. All acts are fictional data, not
legal advice.
Why not eligibility rules or constraints?
Eligibility rules
([nb-01: First permit: facts, a rule and a question](/tutorials/northbridge/nb-01-first-permit/),
[nb-02: Why a missing fact is not a refusal](/tutorials/northbridge/nb-02-missing-fact/),
[nb-03: Exceptions and conflicting rules](/tutorials/northbridge/nb-03-exceptions/)) can
label someone "revocable" but cannot name the act, the grounds, or the
effect. Constraints can flag "issued without fee" but carry no parties
and no lifecycle. Positions keep parties, performance, violation and
effect in one named unit.
## Changed condition
Take the lawful revocation and remove one fact:
`resells_permit(ann)`.
With resale asserted (office, eligibility and the notice all present),
`truth(permit_revoked(ann))` is TRUE_ONLY — exercise plus grounds, so
the `create(permit_revoked(a))` effect fires. Without resale, everything
else identical, the same query is NEITHER.
Why: the notice was still sent and the power still exists, but an
exercise without `valid_when` grounds operates nothing. One condition,
two outcomes — and the difference shows in the truth status, not in
anyone's intention.
The same one-change logic governs duties: `duty satisfied` versus `duty
violated` differ only in whether `decision_notified(ann)` is asserted
or explicitly denied on 10 April; assert neither and the third test
reports UNDETERMINED. The engine never invents a violation from
silence.
## Typical mistake
The mistake is treating the exercise act as the effect: "the office
sent the notice, so the permit is revoked."
It is not — the groundless-power test returns NEITHER with the notice
on record. `exercise` names *which act counts*; `valid_when` decides
*whether it operates*; only an operating exercise produces the
`effect`.
The fix: a power needs three things at once — standing (office plus
eligibility in the `when`), the exercise act, and true grounds. Remove
any one and `permit_revoked` stays NEITHER. If you want revocation on
notice alone, write `valid_when` that way — but then say so, because
the test suite will hold you to it.
## Limits
- **Positions are read through `positions()`.** Truth queries see the
power's *effect* (`permit_revoked`), never the duty or liberty itself.
Asking `truth(NotifyDecision)` is a category error — duties have
lifecycle states, not truth values.
- **Windows bound everything.** `MayObject` opens only for refusals
inside 1–31 March; `NoPermitResale` covers only 2026. Outside its
window a position is not violated — it simply does not apply.
- **UNDETERMINED is not VIOLATED.** After the window closes with the
outcome unrecorded (`without a certificate ... unknown`), the engine
reports UNDETERMINED rather than guessing. Violation needs a witness:
an explicit denial for achievement, a counterexample for maintenance.
- **Verified profile:** engine `law 0.1.0`, semantics `law.core/0.2`.
The lifecycle vocabulary (ACTIVE, SATISFIED, VIOLATED, UNDETERMINED,
exercise / valid_when / effect) is a fact about this profile's
implementation, never a claim about the language in general.
## Exercise
Without running the engine, predict, then check with `law test`:
1. In `duty violated`, which single change to the given facts flips
`NotifyDecision` from VIOLATED to SATISFIED, and which flips it to
UNDETERMINED?
2. In `maintenance violated by counterexample`, why is the status
VIOLATED rather than UNDETERMINED — what plays the witness role?
3. In `power without grounds does not operate`, name the three
ingredients (standing, exercise, grounds) and say which one is absent.
4. In `liberty and immunity`, why is `MayObject` ACTIVE on 10 March but
would not even apply to a refusal dated 15 April?
Write down each prediction first; run the suite; explain any miss in one
sentence. Checkable solution:
[solutions/nb-09-solutions.md](/tutorials/northbridge/solutions/nb-09-solutions/).
## Sources
- Source: `packs/examples/language-demo/permits/package.law` (rules
`DecisionDuty`, `NoResale`, `MaintainEligibility`, `RevocationPower`,
`ObjectionRight`, `NoTowing`)
- Tests: `packs/examples/language-demo/permits/tests/permits.lawtest`
(the ten position, power, liberty and immunity tests)
- Suite tour: `packs/examples/language-demo/README.md`
- Language reference: `docs/language/09-advanced-cheat-sheet.law.md`
(positions and advanced constructs),
`docs/language/06-testing-a-package.law.md`
(the `law test` reference)
- Prerequisite: [nb-01: First permit: facts, a rule and a question](/tutorials/northbridge/nb-01-first-permit/); next:
[nb-10: A procedure with parallel checks](/tutorials/northbridge/nb-10-parallel-procedure/)
Three levels:
1. **Northbridge use** (this article): office decision duty, resale
prohibition, eligibility maintenance, revocation power, objection
liberty, towing immunity — verified by the ten tests above.
2. **Domain template:** whenever a regime says who must do what for
whom, model each relation as one named position with explicit parties
and a window; use achievement for one-off acts, maintenance for
standing conditions; give every power named grounds plus a named
effect, and read lifecycles only through `positions()`.
3. **Confirmed example elsewhere:** unit training readiness (US Army
AR 350-1, para. 1-9a) — package `us.army.training`,
`corpus/laws/us/army-training/03-deontic.law:50-61`, construct
`then duty` with an addressed holder, a beneficiary, and an
achieve-during window (`UnitTrainingReadiness`). Why this form fits:
one named position with explicit parties and a window — this
article's domain template, outside civil law. Evidence:
`docs/research/constructs/10-duty/corpus-forms.en.md` section 1
(rated exemplary there). Limit of verification: presence of the named
construct at the cited lines only, confirmed by direct source read;
no claim about deployment, runtime behavior, or legal correctness.
Where an exercised objection would be heard
The appeals package takes over where positions stop: readings are
selected, judgments are rendered, precedents are followed or
distinguished — verified by
`law test packs/examples/language-demo/appeals`
(7 checked, 7 passed, 0 failed), including `judgment rendered`
(TRUE_ONLY once the hearing officer's judgment is on record) and
`precedent distinguished` (FALSE_ONLY where the defendant's facts
differ). Positions create the relations; appeals resolve what is
contested about them — exactly as the limits say.