jcp §4.10: the flow gate had no spec section, and six code references cited it #190

Merged
dcharlot merged 1 commit from feat/revocation-freshness into main 2026-10-09 02:49:32 -04:00
Owner

jcp-runtime cites "SPEC §4.10" in six places — the taint floor, the flow
gate, check 6c — and jcp/SPEC.md has no §4.10. The section was never written.

So the mechanism that decides whether a value's provenance may exercise a grant
is implemented, gate-enforcing and tested, with no normative text behind it.
That is the mirror of what this sweep has mostly been closing: a check with no
claim, rather than a claim with no check.

I found it by nearly colliding with it — #189 had taken §4.10 for durable spend,
and is renumbered to §4.11, because the code had already spoken for the number.

§4.10 documents what is there

Every normative sentence is backed by a test that already exists in
jcp-runtime:

  • the label's three independent components, ⊥ and ⊤;
  • the ceiling as a FlowBelow caveat, several composing to the strictest via
    meet on the label and min on the tainted sub-budget, so attenuation never
    widens one;
  • the taint floor as the enforcement point's own refusal — the flow analogue of a
    trust root (§7), default-open;
  • the three-case table whose middle row is the whole point:
facts meaning result
absent provenance not computed fails closed against a ceilinged grant
⊥ computed, clean passes any ceiling
labelled computed, tainted tested against ceiling and floor

"Not computed" MUST NOT be read as "clean".

  • the placement, which is normative: after capability and signature, because
    a ceiling inside an unauthenticated grant would seal a verdict over
    attacker-chosen policy; before caveats and budget, so a tainted call costs
    zero joules
    .

A sentence I had to pull back

I first wrote that the implementation propagates labels. It does not —
flow_gate takes the facts from its caller, so propagation belongs to the
host, the only party that sees the data move. The spec now says so, and says
what JCP guarantees instead: a host that propagates gets a decision bound to
what it reported, and one that does not gets a refusal rather than a pass.

Conformance class C25 is declared and defined in §9's list — citing a class
without defining it would be the same dangling reference this PR exists to fix.

§4.3 gains a Propagation paragraph

§4.3 binds an enforcement point once it holds a revocation, and says nothing
about how it comes to hold one. With a budget sharded across N points (§4.5), a
revocation that reached three of five leaves the grant live at two — and an allow
from a point that had not yet heard is indistinguishable, after the fact, from
an allow by a point that heard and ignored it. The first is propagation
delay. The second violates this spec.

An implementation MUST now define how revocations reach its points, and SHOULD
let each state how current its knowledge is.
Runtime::revocation_known_through records it, defaulting to None, which an
auditor MUST read as unverifiable rather than current — "did not say" is not
"was current".

Stated in the method's own docs rather than left implied: the marker is state
on the enforcement point and the jcp/2 receipt does not carry it, so an auditor
needs it out of band. Binding it into the signed core is the stronger fix and
reaches every Decision construction site across six standards' reference
implementations — 75 of them, in workspaces that do not build together — so it is
separate work, not a line in this one.

Verification

jcp-rs workspace 404 passed. The two clippy warnings in jcp-runtime are
pre-existing (2 on the baseline, checked against a stash).

Falsifying the marker caught a vacuous assertion of my own. I had written
that declaring a marker changes no decision, and checked it by comparing two
runtimes that both held no revocation — which passes however the marker
behaves. The test now applies a real revocation first, so clearing the
revocation set from the setter is caught.

Two earlier mutation attempts also "passed" because they patched lines that do
not exist: Runtime derives Default, so the marker's None comes by
derivation and there is no constructor literal to break.

reverted caught by
the default marker claims currency (Some(0)) an_unstated_revocation_marker_is_not_a_current_one
declaring a marker clears the revocation set same test, after it was strengthened

Merge order with the sibling PR

#190 adds §4.10 (the flow gate) and this one adds §4.11. They conflict with
each other
, though each merges cleanly against main: both insert a new §4.x at
the same anchor, after §4.9 and before §5. Nothing else overlaps —
jcp-orchestrate and jcp-runtime are disjoint, and neither references the
other's symbols.

Merge #190 first. Then rebase this one; the resolution is to keep both
sections in numeric order, which is also the order they must read in: §4.10
Flow, §4.11 Durable spend. I checked the landing by merging the whole set onto
main in a scratch branch — the sequence comes out §4.9, §4.10, §4.11, §5, and
afterwards §4.10 has 13 code references against 1 spec section and §4.11 has 0
against 1.

(A git merge-tree between the two branches reported no conflict and I believed
it; a real merge disagrees. The textual check was wrong, not the branches.)

🤖 Generated with Claude Code

`jcp-runtime` cites **"SPEC §4.10"** in six places — the taint floor, the flow gate, check 6c — and `jcp/SPEC.md` has no §4.10. The section was never written. So the mechanism that decides whether a value's provenance may exercise a grant is implemented, gate-enforcing and tested, with **no normative text behind it**. That is the mirror of what this sweep has mostly been closing: a check with no claim, rather than a claim with no check. I found it by nearly colliding with it — #189 had taken §4.10 for durable spend, and is renumbered to §4.11, because the code had already spoken for the number. ## §4.10 documents what is there Every normative sentence is backed by a test that already exists in `jcp-runtime`: - the label's three independent components, `⊥` and `⊤`; - the ceiling as a `FlowBelow` caveat, several composing to the **strictest** via `meet` on the label and `min` on the tainted sub-budget, so attenuation never widens one; - the taint floor as the enforcement point's own refusal — the flow analogue of a trust root (§7), default-open; - the three-case table whose middle row is the whole point: | facts | meaning | result | |---|---|---| | absent | provenance **not computed** | **fails closed** against a ceilinged grant | | `⊥` | computed, clean | passes any ceiling | | labelled | computed, tainted | tested against ceiling and floor | "Not computed" MUST NOT be read as "clean". - **the placement, which is normative**: after capability and signature, because a ceiling inside an unauthenticated grant would seal a verdict over attacker-chosen policy; before caveats and budget, so **a tainted call costs zero joules**. ### A sentence I had to pull back I first wrote that the implementation propagates labels. It does not — `flow_gate` takes the facts from its caller, so propagation belongs to the **host**, the only party that sees the data move. The spec now says so, and says what JCP guarantees instead: a host that propagates gets a decision bound to what it reported, and one that does not gets a refusal rather than a pass. Conformance class **C25** is declared *and defined* in §9's list — citing a class without defining it would be the same dangling reference this PR exists to fix. ## §4.3 gains a Propagation paragraph §4.3 binds an enforcement point *once it holds* a revocation, and says nothing about how it comes to hold one. With a budget sharded across N points (§4.5), a revocation that reached three of five leaves the grant live at two — and an allow from a point that **had not yet heard** is indistinguishable, after the fact, from an allow by a point that heard and **ignored** it. The first is propagation delay. The second violates this spec. An implementation MUST now define how revocations reach its points, and SHOULD let each state how current its knowledge is. `Runtime::revocation_known_through` records it, defaulting to `None`, which an auditor MUST read as **unverifiable** rather than current — "did not say" is not "was current". **Stated in the method's own docs rather than left implied:** the marker is state on the enforcement point and the `jcp/2` receipt does not carry it, so an auditor needs it out of band. Binding it into the signed core is the stronger fix and reaches every `Decision` construction site across six standards' reference implementations — 75 of them, in workspaces that do not build together — so it is separate work, not a line in this one. ## Verification `jcp-rs` workspace **404 passed**. The two clippy warnings in `jcp-runtime` are pre-existing (2 on the baseline, checked against a stash). Falsifying the marker **caught a vacuous assertion of my own**. I had written that declaring a marker changes no decision, and checked it by comparing two runtimes that both held *no* revocation — which passes however the marker behaves. The test now applies a real revocation first, so clearing the revocation set from the setter is caught. Two earlier mutation attempts also "passed" because they patched lines that do not exist: `Runtime` derives `Default`, so the marker's `None` comes by derivation and there is no constructor literal to break. | reverted | caught by | |---|---| | the default marker claims currency (`Some(0)`) | `an_unstated_revocation_marker_is_not_a_current_one` | | declaring a marker clears the revocation set | same test, after it was strengthened | ## Merge order with the sibling PR **#190 adds §4.10 (the flow gate) and this one adds §4.11. They conflict with each other**, though each merges cleanly against main: both insert a new §4.x at the same anchor, after §4.9 and before §5. Nothing else overlaps — `jcp-orchestrate` and `jcp-runtime` are disjoint, and neither references the other's symbols. **Merge #190 first.** Then rebase this one; the resolution is to keep both sections in numeric order, which is also the order they must read in: §4.10 Flow, §4.11 Durable spend. I checked the landing by merging the whole set onto main in a scratch branch — the sequence comes out §4.9, §4.10, §4.11, §5, and afterwards §4.10 has 13 code references against 1 spec section and §4.11 has 0 against 1. (A `git merge-tree` between the two branches reported no conflict and I believed it; a real merge disagrees. The textual check was wrong, not the branches.) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jcp §4.10: the flow gate had no spec section, and six code references cited it
All checks were successful
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 11m23s
standards workspaces build + test / aac-rs (pull_request) Successful in 8m30s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 4m18s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 1m10s
standards workspaces build + test / map-rs (pull_request) Successful in 1m53s
standards workspaces build + test / mesh-rs (pull_request) Successful in 1m33s
standards workspaces build + test / openpay (pull_request) Successful in 13m47s
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 4s
no-vanity-metrics / WAI public text names no comparison (pull_request) Successful in 4s
no-vanity-metrics / WAI public text names no company or product (pull_request) Successful in 3s
standards workspaces build + test / arl-rs (pull_request) Successful in 1m10s
standards workspaces build + test / eoc-rs (pull_request) Successful in 1m54s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m28s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 1m9s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 1m11s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m14s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m14s
standards workspaces build + test / sense-rs (pull_request) Successful in 1m7s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 3m43s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 2m56s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 5m6s
0d107105c6
`jcp-runtime` cites "SPEC §4.10" in six places — the taint floor, the flow gate,
check 6c — and jcp/SPEC.md had no §4.10. The section was never written. So the
mechanism that decides whether a value's provenance may exercise a grant is
implemented, gate-enforcing and tested, with no normative text behind it: the
mirror of the defects this sweep has mostly been closing, a check with no claim
rather than a claim with no check.

I found it by nearly colliding with it — #189 had taken §4.10 for durable spend
and is renumbered to §4.11, because the code had already spoken for the number.

§4.10 documents what is there, and every normative sentence is backed by an
existing test:

- the label's three independent components, `⊥` and `⊤`;
- the ceiling as a `FlowBelow` caveat, several composing to the strictest via
  `meet` on the label and `min` on the tainted sub-budget, so attenuation never
  widens one;
- the taint floor as the enforcement point's own refusal, the flow analogue of a
  trust root, default-open;
- the three-case table whose middle row is the point: provenance **absent** is
  not provenance **clean**, and a ceilinged grant fails closed on an unlabelled
  argument;
- the placement, which is normative: after capability and signature, because a
  ceiling inside an unauthenticated grant would seal a verdict over
  attacker-chosen policy; before caveats and budget, so a tainted call costs
  zero joules.

One sentence I had to pull back: I first wrote that the implementation
propagates labels. It does not — `flow_gate` takes the facts from its caller, so
propagation belongs to the host, which is the only party that sees the data move.
The spec now says so, and says what JCP does guarantee instead: a host that
propagates gets a decision bound to what it reported, and one that does not gets
a refusal rather than a pass.

Conformance class **C25** is declared and defined in §9's list. It cites only
behaviour `jcp-runtime` already tests — zero-debit flow denial, failing closed
without facts, the taint floor overriding a permissive ceiling, the tainted
sub-budget, and byte-identical encoding without a flow caveat.

**§4.3 gains a Propagation paragraph.** §4.3 binds an enforcement point *once it
holds* a revocation and says nothing about how it comes to hold one. With a
budget sharded across N points (§4.5), a revocation that reached three of five
leaves the grant live at two — and an allow from a point that had not yet heard
is indistinguishable, after the fact, from an allow by a point that heard and
ignored it. The first is propagation delay; the second violates this spec. An
implementation MUST now define how revocations reach its points and SHOULD let
each state how current its knowledge is; `Runtime::revocation_known_through`
records it, defaulting to `None`, which an auditor MUST read as *unverifiable*
rather than current.

Stated in the method's own docs rather than left implied: the marker is state on
the enforcement point and the `jcp/2` receipt does not carry it, so an auditor
needs it reported out of band. Binding it into the signed core is the stronger
fix and reaches every `Decision` construction site across six standards'
reference implementations — 75 of them, in workspaces that do not build
together — so it is a separate piece of work rather than a line in this one.

Falsifying the marker caught a vacuous assertion of my own. I had written that
declaring a marker changes no decision, and checked it by comparing two runtimes
that both held *no* revocation — which passes however the marker behaves. The
test now applies a real revocation first, so clearing the revocation set from
the setter is caught. Two earlier mutation attempts also "passed" because they
patched lines that do not exist: `Runtime` derives `Default`, so the marker's
`None` comes by derivation and there is no constructor literal to break.

jcp-rs workspace: 404 passed. The two clippy warnings in jcp-runtime are
pre-existing (2 on the baseline, checked against the stash).

Co-Authored-By: Claude Opus 5 <[email protected]>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!190
No description provided.