Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
166 commits
Select commit Hold shift + click to select a range
603ae1c
test(core): require bounded semantic node observation
seonghobae Aug 10, 2026
f876711
style(core): format semantic observation RED contract
seonghobae Aug 10, 2026
3d6fb8c
test(core): isolate semantic observation RED failure
seonghobae Aug 10, 2026
50051da
test(core): specify bounded semantic observation failures
seonghobae Aug 10, 2026
4aae3bc
feat(core): scaffold semantic observation module
seonghobae Aug 10, 2026
59e00a7
feat(core): implement bounded semantic observation
seonghobae Aug 10, 2026
3754f47
feat(core): export semantic observation contract
seonghobae Aug 10, 2026
10a40be
style(core): apply rustfmt to semantic exports
seonghobae Aug 10, 2026
84f609e
style(core): apply rustfmt to semantic observation tests
seonghobae Aug 10, 2026
939ab06
docs(changelog): record semantic observation slice
seonghobae Aug 10, 2026
bda159a
test(core): require semantic observation provenance
seonghobae Aug 10, 2026
df54c61
fix(core): require semantic observation provenance
seonghobae Aug 10, 2026
3f52c1c
test(core): cover provenance validation error
seonghobae Aug 10, 2026
661091d
style(core): apply rustfmt to provenance error
seonghobae Aug 10, 2026
b1bd4f8
test(core): require bounded semantic relationships
seonghobae Aug 10, 2026
e8be794
feat(core): bound semantic node relationships to exact authority
seonghobae Aug 10, 2026
632e724
feat(core): export semantic relationship bound
seonghobae Aug 10, 2026
dbe75ca
style(core): apply canonical semantic relationship formatting
seonghobae Aug 10, 2026
94fd284
test(core): cover each semantic relationship authority axis
seonghobae Aug 10, 2026
e8cf134
test(core): require typed semantic node query
seonghobae Aug 10, 2026
d0cd133
style(core): apply canonical semantic query formatting
seonghobae Aug 10, 2026
096bb97
feat(core): implement typed semantic node query
seonghobae Aug 10, 2026
5e6b81b
feat(core): export typed semantic node query
seonghobae Aug 10, 2026
135d325
docs(changelog): record typed semantic node query boundary
seonghobae Aug 10, 2026
bcd69bf
style(core): apply canonical semantic query formatting
seonghobae Aug 10, 2026
b4fa499
fix(core): satisfy strict semantic query linting
seonghobae Aug 10, 2026
308fa77
test(core): require authority-bound semantic action target
seonghobae Aug 10, 2026
f2bb6db
style(core): apply canonical action-target test formatting
seonghobae Aug 10, 2026
ef5fd33
feat(core): add authority-bound semantic action target
seonghobae Aug 10, 2026
01b1500
feat(core): export semantic action target contract
seonghobae Aug 10, 2026
c81c273
test(core): prove semantic action target authority invalidation
seonghobae Aug 10, 2026
efe440c
docs(changelog): record semantic action target boundary
seonghobae Aug 10, 2026
cd784b6
test(core): require node-to-business-action binding
seonghobae Aug 11, 2026
33fd6ae
style(core): apply canonical action-binding rustfmt
seonghobae Aug 11, 2026
c150e2d
feat(core): bind semantic node targets to business actions
seonghobae Aug 11, 2026
f4bc756
merge: align semantic observation with current browser authority
seonghobae Aug 15, 2026
4863768
merge: align semantic query with current observation authority
seonghobae Aug 15, 2026
325070e
merge: align semantic action target with current query authority
seonghobae Aug 15, 2026
d436036
merge: align semantic action binding with current target authority
seonghobae Aug 15, 2026
a0986fe
test(core): require standard public validation errors
seonghobae Aug 15, 2026
5c6d0ed
fix(core): expose standard validation errors
seonghobae Aug 15, 2026
15048b1
fix(core): wire public validation error contracts
seonghobae Aug 15, 2026
2f1e7cd
test(core): use standard validation errors without expect
seonghobae Aug 15, 2026
8bd82c4
refactor(core): make contract exports explicit
seonghobae Aug 15, 2026
d242b23
test(core): require explicit sha256 digest prefix
seonghobae Aug 15, 2026
90227c5
fix(core): clarify sha256 digest prefix
seonghobae Aug 15, 2026
913b21b
merge: align semantic observation with current browser authority
seonghobae Aug 15, 2026
255c53a
merge: align semantic query with current observation authority
seonghobae Aug 15, 2026
c33cbd2
merge: align semantic action target with current query authority
seonghobae Aug 15, 2026
7f697c4
merge: align semantic action binding with current target authority
seonghobae Aug 15, 2026
2203d60
test(core): preserve origin-bound expiring extension grants
seonghobae Aug 17, 2026
14cc3f5
fix(core): preserve protected extension grant authority
seonghobae Aug 17, 2026
8ab27e5
merge(main): reconcile extension authority baseline
seonghobae Aug 17, 2026
c146e3e
fix(core): delegate missing extension grant evaluation
seonghobae Aug 17, 2026
8a4b4c2
style(core): apply canonical extension authority formatting
seonghobae Aug 17, 2026
d5efaca
chore(core): align semantic observation stack with current browser au…
seonghobae Aug 17, 2026
756668c
chore(core): align semantic query stack with current observation auth…
seonghobae Aug 17, 2026
55ad82d
chore(core): align semantic action-target stack with current query au…
seonghobae Aug 17, 2026
0a9f86d
chore(core): align semantic action binding with current action target
seonghobae Aug 17, 2026
b55716d
test(core): require extension grant task binding
seonghobae Aug 17, 2026
d043af9
fix(core): bind extension grants to Agent Task identity
seonghobae Aug 17, 2026
a4830c2
style(core): apply canonical task-authority formatting
seonghobae Aug 17, 2026
b45ab7b
docs(adr): record active task-bound extension authority
seonghobae Aug 17, 2026
692cf58
docs(changelog): record task-bound extension grants
seonghobae Aug 17, 2026
52ef3fb
chore(core): align semantic observation stack with current browser au…
seonghobae Aug 17, 2026
96c5df1
chore(core): align semantic query stack with current observation auth…
seonghobae Aug 17, 2026
184fa70
chore(core): align semantic action-target stack with current query au…
seonghobae Aug 17, 2026
1c6a208
test(browser): keep failed node allocation transactional
seonghobae Aug 20, 2026
8d51ea3
fix(browser): keep failed node binding transactional
seonghobae Aug 20, 2026
b9e9f3a
style(browser): format transactional node binding
seonghobae Aug 20, 2026
0428a97
test(browser): cover fail-closed registry invariants
seonghobae Aug 20, 2026
0260711
chore(core): align semantic observation with latest browser authority
seonghobae Aug 20, 2026
ffd3810
chore(core): align semantic query with latest observation authority
seonghobae Aug 20, 2026
093873e
chore(core): align semantic action target with latest query authority
seonghobae Aug 20, 2026
8bf7803
test(browser): reject forged or retired node handles
seonghobae Aug 20, 2026
d5705f8
fix(browser): revalidate live node authority in registry
seonghobae Aug 20, 2026
0d12ad5
style(browser): apply canonical node validation formatting
seonghobae Aug 20, 2026
1248ccf
test(browser): close registry coverage gaps
seonghobae Aug 20, 2026
4380016
chore(core): align semantic observation stack to live browser authority
seonghobae Aug 21, 2026
c84a9c4
fix(core): preserve live browser authority during stack alignment
seonghobae Aug 21, 2026
0553c69
test(core): require registry authority for semantic observations
seonghobae Aug 21, 2026
1f57328
style(core): apply canonical formatting to semantic authority regression
seonghobae Aug 21, 2026
64f503b
fix(core): require live registry authority for semantic observations
seonghobae Aug 21, 2026
575515d
test(core): use live registry authority in semantic observation suite
seonghobae Aug 21, 2026
f71c5c3
style(core): apply canonical semantic observation formatting
seonghobae Aug 21, 2026
957e52c
test(core): cover forged parent observation authority
seonghobae Aug 21, 2026
0e87928
fix(core): remove unreachable relationship coverage branches
seonghobae Aug 21, 2026
57032ef
chore(core): realign semantic query stack to live observation head
seonghobae Aug 21, 2026
5e8c254
docs(changelog): retain semantic query contract after stack realignment
seonghobae Aug 21, 2026
867db7d
test(core): require registry-live semantic action targets
seonghobae Aug 21, 2026
7656d98
fix(core): revalidate action targets against live registry
seonghobae Aug 21, 2026
4daeb26
docs(changelog): record registry-live action targets
seonghobae Aug 21, 2026
7b198e0
fix(core): carry registry-live authority into action bindings
seonghobae Aug 21, 2026
1d9e7c5
test(browser): reject cross-registry node-handle reuse
seonghobae Aug 22, 2026
1fa6fec
test(browser): reject forged matching registry handles
seonghobae Aug 22, 2026
4a828ed
fix(browser): bind node handles to registry instances
seonghobae Aug 22, 2026
2dbcb03
test(browser): cover registry authority invariants
seonghobae Aug 22, 2026
76651a5
style(browser): apply canonical Rust formatting
seonghobae Aug 22, 2026
724305f
test(browser): cover corrupt zero node authority
seonghobae Aug 22, 2026
94de6c2
test(browser): require same-document node retirement
seonghobae Aug 22, 2026
2c08bca
style(browser): apply canonical rustfmt to node retirement regression
seonghobae Aug 22, 2026
8587c8f
feat(browser): revoke same-document node authority
seonghobae Aug 22, 2026
77df987
test(core): cover node retirement and epoch exhaustion
seonghobae Aug 22, 2026
e8c105c
fix(core): purge duplicate node aliases on retirement
seonghobae Aug 22, 2026
1e5f85a
test(core): cover session retirement unit branches
seonghobae Aug 22, 2026
28e417a
fix(core): restore canonical browser registry source
seonghobae Aug 22, 2026
42068e7
test(core): cover session retirement unit branches
seonghobae Aug 22, 2026
32f058f
test(core): cover duplicate context and identifier bounds
seonghobae Aug 22, 2026
755cb39
test(core): cover direct registry fail-closed branches
seonghobae Aug 22, 2026
0edb86a
test(core): cover allocation and rotation error regions
seonghobae Aug 22, 2026
556c986
test(core): close remaining registry coverage region
seonghobae Aug 22, 2026
da4ccc8
docs(adr): cite primary MV3 authority sources
seonghobae Aug 22, 2026
c30c043
chore(core): align semantic observation with browser authority root
seonghobae Aug 22, 2026
2241c14
chore(core): align semantic query with observation root
seonghobae Aug 22, 2026
034cc7f
chore(core): align semantic action target with query root
seonghobae Aug 22, 2026
b5f2e1a
chore(core): align action binding with target root
seonghobae Aug 22, 2026
a056a01
test(core): restore canonical action binding formatting
seonghobae Aug 22, 2026
eb29cad
test(browser): hide session membership from unissued node handles
seonghobae Aug 23, 2026
a8b03e0
fix(browser): authenticate node issuance before registry lookup
seonghobae Aug 23, 2026
c0ef5d5
docs(changelog): record node-handle oracle hardening
seonghobae Aug 23, 2026
eb992a4
test(browser): cover authenticated retired-authority failures
seonghobae Aug 23, 2026
8093169
test(browser): apply canonical formatting
seonghobae Aug 23, 2026
7020dbe
test(browser): cover issued context-session corruption
seonghobae Aug 23, 2026
3a29725
test(browser): cover context-origin corruption
seonghobae Aug 23, 2026
281737b
repair(browser): restore registry source after transport corruption
seonghobae Aug 23, 2026
c8a357c
test(browser): cover corrupted origin authority rejection
seonghobae Aug 23, 2026
562b4b7
style(browser): apply canonical Rust formatting
seonghobae Aug 23, 2026
8452f7d
fix(browser): restore current registry branch before coverage repair
seonghobae Aug 23, 2026
4cbcb03
test(browser): cover missing pinned origin authority
seonghobae Aug 23, 2026
5cb111b
chore(browser): realign semantic observation stack
seonghobae Aug 23, 2026
630503c
chore(browser): realign semantic query stack
seonghobae Aug 23, 2026
d6fb55c
chore(browser): realign semantic action stack
seonghobae Aug 23, 2026
d77bdd2
chore(browser): realign business-action binding stack
seonghobae Aug 23, 2026
f71c1ca
test(core): reject empty-hex browser IPv4 authorities
seonghobae Aug 23, 2026
0a73431
fix(core): reject empty-hex browser IPv4 authorities
seonghobae Aug 23, 2026
5157366
docs(changelog): record empty-hex origin hardening
seonghobae Aug 23, 2026
6193f18
chore(stack): realign semantic observation on browser authority
seonghobae Aug 23, 2026
adcafe1
docs(changelog): preserve semantic observation slice after stack real…
seonghobae Aug 23, 2026
0300473
merge: refresh semantic query onto live observation prerequisite
seonghobae Aug 23, 2026
657f18b
merge: refresh semantic action target onto live query prerequisite
seonghobae Aug 23, 2026
c2fc69c
merge: refresh semantic action binding onto live action-target prereq…
seonghobae Aug 23, 2026
eeac29f
test(core): restack action binding on admitted node authority
seonghobae Sep 1, 2026
293781d
feat(core): bind admitted nodes to business actions
seonghobae Sep 1, 2026
a23bddb
feat(core): export admitted-node action binding
seonghobae Sep 1, 2026
0a7598b
style(core): apply rustfmt to action binding test
seonghobae Sep 1, 2026
cae5459
chore(stack): converge action binding on current postcondition parent
seonghobae Sep 1, 2026
da4aef6
chore(stack): carry current typed-text postcondition contract
seonghobae Sep 1, 2026
e985ef4
chore(stack): carry current typed-text postcondition regression
seonghobae Sep 1, 2026
b4649c2
chore(stack): reconcile #93 onto current #271 ancestry
seonghobae Sep 1, 2026
0cb57a0
test(core): bind local node action to policy intent
seonghobae Sep 1, 2026
b07e48d
fix(core): retain exact node action in policy binding
seonghobae Sep 1, 2026
256a4f4
chore(stack): restack #93 on current #271
seonghobae Sep 2, 2026
1578fb9
test(core): require current admitted-node authority
seonghobae Sep 2, 2026
e6af691
fix(core): revalidate opaque admitted-node authority
seonghobae Sep 2, 2026
71a3ecf
Merge remote-tracking branch 'origin/feat/webdriver-bidi-type-text-po…
seonghobae Sep 4, 2026
7dce412
fix(core): restore typed semantic action contract
seonghobae Sep 4, 2026
21224af
Merge remote-tracking branch 'origin/feat/webdriver-bidi-type-text-po…
seonghobae Sep 4, 2026
96da9df
merge: adopt postcondition response parent
seonghobae Sep 5, 2026
82b0ebb
merge: inherit opening-write timeout cleanup
seonghobae Sep 5, 2026
18cccdf
merge: inherit opening-response fixture cleanup
seonghobae Sep 5, 2026
0664f04
merge: adopt complete opening fixture cleanup
seonghobae Sep 5, 2026
342fb2c
test(network): reproduce inherited foreign observation reply
seonghobae Sep 7, 2026
9b4e656
merge: adopt verified observation parent preserving semantic authority
seonghobae Sep 7, 2026
576c431
docs: clarify action binding and observation evidence boundaries
seonghobae Sep 7, 2026
12d032e
test(docs): distinguish registry ownership from reply provenance
seonghobae Sep 7, 2026
82056d1
docs: separate node ownership and reply connection evidence
seonghobae Sep 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to OriginWeave are documented in this file. The format follo

## [Unreleased]

- Preserve current-node action safeguards while adopting connection-bound field-value replies; a matching reply still does not authorize an action.

- Reject field-value replies received on a replacement connection, even when their request identifier and text match.

- Reject field-observation requests on another browser session and preserve pending requests only when a write may have reached the peer.
Expand Down Expand Up @@ -59,6 +61,7 @@ All notable changes to OriginWeave are documented in this file. The format follo

### Added

- Complete typed semantic-node action retention at the public binding boundary, including fail-closed coverage of registry-authority corruption and deterministic typed error sources.
- Typed WebDriver BiDi command-family correlation for text-value post-condition observations, so a matching numeric response id cannot consume an outstanding command from another operation family; successful envelopes reuse the parser-proven non-null id invariant without an unreachable fallback branch.
- Deterministic WebDriver BiDi primary-button click serialization for an already admitted remote node: it emits one fixed `input.performActions` mouse sequence from bounded command/context/node identifiers and remains inert until a trusted adapter binds it to current session, origin, document, policy, and approval authority.
- Typed outbound WebDriver BiDi primary-button click transport over the bounded client WebSocket stream: it rejects invalid frame deadlines before correlation registration, retires only the just-registered id when local frame preflight proves no command bytes were emitted, preserves correlation across ambiguous writes, and does not treat frame-write success as proof that the browser performed the click.
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ homepage = "https://github.com/ContextualWisdomLab/OriginWeave"

[workspace.lints.rust]
unsafe_code = "forbid"
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage)'] }

[workspace.lints.clippy]
dbg_macro = "deny"
Expand Down
124 changes: 122 additions & 2 deletions crates/originweave-core/src/browser_authority_registry.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use std::collections::BTreeMap;
use std::error::Error;
use std::fmt::{Display, Formatter};
use std::ops::Deref;
use std::sync::Arc;

use crate::browser_registry::BrowserAuthorityRegistry as RawBrowserAuthorityRegistry;
use crate::{
BrowserRegistryError, BrowserSessionId, BrowsingContextId, DocumentEpoch, ObservedNodeHandle,
Origin,
BrowserRegistryError, BrowserSessionId, BrowsingContextId, DocumentEpoch, NodeHandleError,
ObservedNodeHandle, Origin,
};

/// A registry-issued node handle that carries opaque provenance in addition to descriptive node state.
Expand All @@ -28,6 +30,52 @@ impl Deref for AdmittedNodeHandle {
}
}

/// A fail-closed error while revalidating opaque registry-issued node authority.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AdmittedNodeAuthorityError {
/// The supplied handle was issued by a different registry instance.
ForeignRegistry,
/// The registry no longer retains this exact admitted node binding.
NotAdmitted,
/// Current session, context, or canonical-origin authority no longer matches.
BrowserAuthority(BrowserRegistryError),
/// The handle no longer matches the registry's current document lifetime.
NodeHandle(NodeHandleError),
}

impl Display for AdmittedNodeAuthorityError {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::ForeignRegistry => formatter
.write_str("admitted node was issued by a different browser authority registry"),
Self::NotAdmitted => formatter
.write_str("admitted node authority is no longer retained by this registry"),
Self::BrowserAuthority(error) => {
write!(
formatter,
"admitted node browser authority rejected input: {error}"
)
}
Self::NodeHandle(error) => {
write!(
formatter,
"admitted node document authority rejected input: {error}"
)
}
}
}
}

impl Error for AdmittedNodeAuthorityError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::BrowserAuthority(error) => Some(error),
Self::NodeHandle(error) => Some(error),
Self::ForeignRegistry | Self::NotAdmitted => None,
}
}
}

/// Public browser-authority registry with raw node minting kept inside the crate.
///
/// Browser-session, browsing-context, document-epoch, and canonical-origin lifecycle operations are
Expand Down Expand Up @@ -210,6 +258,44 @@ impl BrowserAuthorityRegistry {
.require_context_origin(browser_session, browsing_context, origin)
}

/// Revalidate one exact registry-issued admitted node before later typed dispatch.
///
/// This check preserves opaque registry-instance provenance and verifies that this registry still
/// retains the exact admitted node key under the current session, context, origin, and document
/// epoch. It deliberately does not validate the adapter-local wire identifier; the final typed
/// adapter constructor must still bind the exact `sharedId` immediately before browser I/O.
pub fn validate_admitted_node_handle(
&self,
handle: &AdmittedNodeHandle,
) -> Result<(), AdmittedNodeAuthorityError> {
if !Arc::ptr_eq(&self.registry_identity, &handle.registry_instance) {
return Err(AdmittedNodeAuthorityError::ForeignRegistry);
}
if !self
.admitted_node_external_identifiers
.contains_key(&node_authority_key(&handle.observed))
{
return Err(AdmittedNodeAuthorityError::NotAdmitted);
}

self.require_context_origin(
handle.browser_session(),
handle.browsing_context(),
handle.origin(),
)
.map_err(AdmittedNodeAuthorityError::BrowserAuthority)
.and_then(|current_epoch| {
handle
.validate_current(
handle.browser_session(),
handle.browsing_context(),
handle.origin(),
current_epoch,
)
.map_err(AdmittedNodeAuthorityError::NodeHandle)
})
}

/// Advance a browsing context to the next document epoch and invalidate old node bindings.
pub fn advance_document(
&mut self,
Expand Down Expand Up @@ -306,3 +392,37 @@ fn node_authority_key(handle: &ObservedNodeHandle) -> (u64, u64, u64, u64) {
handle.node_id(),
)
}

#[cfg(test)]
mod tests {
use std::error::Error;

use super::{AdmittedNodeAuthorityError, BrowserAuthorityRegistry};
use crate::{BrowserRegistryError, Origin};

#[test]
#[cfg_attr(coverage, coverage(off))]
fn admitted_node_revalidation_preserves_broken_registry_authority() -> Result<(), Box<dyn Error>>
{
let mut registry = BrowserAuthorityRegistry::new();
let session = registry.register_session("broken-authority-session")?;
let context = registry.register_context(session, "broken-authority-context")?;
let origin = Origin::parse("https://example.com").map_err(|error| {
std::io::Error::other(format!("fixture origin rejected: {error:?}"))
})?;
let handle = registry
.bind_admitted_nodes(session, context, &origin, &["node"])?
.pop()
.ok_or("fixture did not bind its node")?;
registry.validate_admitted_node_handle(&handle)?;
registry.inner.remove_context(context)?;

assert_eq!(
registry.validate_admitted_node_handle(&handle),
Err(AdmittedNodeAuthorityError::BrowserAuthority(
BrowserRegistryError::UnknownBrowsingContext
))
);
Ok(())
}
}
8 changes: 7 additions & 1 deletion crates/originweave-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![cfg_attr(coverage, feature(coverage_attribute))]
//! Shared security and governance contracts for OriginWeave.
//!
//! This crate keeps the long-lived value contracts in `contracts` and the
Expand Down Expand Up @@ -45,6 +46,7 @@ mod browser_registry;
mod browser_registry_coverage;
mod browser_registry_external_context;
mod contracts;
mod semantic_action_binding;
mod webdriver_bidi_command;
mod webdriver_bidi_error_code;
mod webdriver_bidi_pointer_click_authority;
Expand All @@ -58,7 +60,8 @@ mod webdriver_bidi_websocket_connect_target;
mod webdriver_bidi_websocket_endpoint;

pub use browser_authority_registry::{
AdmittedNodeHandle, BrowserAuthorityRegistry, BrowserRegistryIdentity,
AdmittedNodeAuthorityError, AdmittedNodeHandle, BrowserAuthorityRegistry,
BrowserRegistryIdentity,
};
pub use browser_protocol::{
BrowserProtocolAdapterDescriptor, BrowserProtocolCapability,
Expand Down Expand Up @@ -88,6 +91,9 @@ pub use browser_registry::{
UNICODE_PROTOCOL_FORMAT_INJECTION_CHARS,
};
pub use contracts::*;
pub use semantic_action_binding::{
NodeActionKind, SemanticNodeActionBinding, SemanticNodeActionBindingError,
};
pub use webdriver_bidi_command::{
CorrelatedWebDriverBiDiLocateNodesResponse, MAX_WEBDRIVER_BIDI_COMMAND_ID,
ValidatedWebDriverBiDiLocateNodesResponse, WEBDRIVER_BIDI_PERFORM_ACTIONS_METHOD,
Expand Down
103 changes: 103 additions & 0 deletions crates/originweave-core/src/semantic_action_binding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use std::fmt;

use crate::{
ActionRequest, AdmittedNodeAuthorityError, AdmittedNodeHandle, BrowserAuthorityRegistry,
};

/// A node-local typed action retained by an authorized semantic binding.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum NodeActionKind {
/// Activate the node using browser-native click semantics.
Click,
/// Insert bounded non-secret text using browser-native input semantics.
TypeText,
/// Select one option using browser-native selection semantics.
SelectOption,
/// Set a checkable control to an explicit checked state.
SetChecked,
/// Scroll the node into the viewport without activating it.
ScrollIntoView,
}

/// One registry-issued browser node and local node action explicitly paired with the business
/// action request they would serve.
///
/// The binding prevents a caller from independently validating one current browser node, selecting
/// a different browser-local action at dispatch, and combining that side effect with a separately
/// authorized business intent. It deliberately does not authorize policy, map the node-local action
/// to a business risk class, grant a destination, resolve secrets, or execute browser I/O. The later
/// typed adapter boundary still revalidates registry provenance and the exact admitted wire node
/// immediately before I/O.
#[derive(Debug)]
pub struct SemanticNodeActionBinding {
handle: AdmittedNodeHandle,
node_action: NodeActionKind,
request: ActionRequest,
}

impl SemanticNodeActionBinding {
/// Bind one registry-issued admitted node and exact node-local action to a business request from
/// the same source origin.
pub fn new(
handle: AdmittedNodeHandle,
node_action: NodeActionKind,
request: ActionRequest,
) -> Result<Self, SemanticNodeActionBindingError> {
if handle.origin() != request.source_origin() {
return Err(SemanticNodeActionBindingError::SourceOriginMismatch);
}
Ok(Self {
handle,
node_action,
request,
})
}

/// Return the exact registry-issued node retained for later immediate-use authority checks.
#[must_use]
pub const fn handle(&self) -> &AdmittedNodeHandle {
&self.handle
}

/// Return the exact browser-local node action retained with the authorized business intent.
#[must_use]
pub const fn node_action(&self) -> NodeActionKind {
self.node_action
}

/// Return the independently classified business action request.
#[must_use]
pub const fn request(&self) -> &ActionRequest {
&self.request
}

/// Revalidate the exact retained admitted-node authority against the trusted current registry.
///
/// This preserves opaque registry-instance provenance and current session/context/origin/document
/// state. It does not validate the final adapter-local wire identifier or execute browser I/O;
/// the typed adapter command constructor must still perform that immediate-use check.
pub fn validate_current(
&self,
registry: &BrowserAuthorityRegistry,
) -> Result<(), AdmittedNodeAuthorityError> {
registry.validate_admitted_node_handle(&self.handle)
}
}

/// A bounded failure to pair admitted browser-node authority with a business action request.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SemanticNodeActionBindingError {
/// The request claims a different source document origin than the admitted node.
SourceOriginMismatch,
}

impl fmt::Display for SemanticNodeActionBindingError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::SourceOriginMismatch => formatter
.write_str("admitted node origin does not match action request source origin"),
}
}
}

impl std::error::Error for SemanticNodeActionBindingError {}
Loading
Loading