PR S: Machine-form obligation layer (terms-as-data) #23
Reference in New Issue
Block a user
Delete Branch "devin/1776883227-pr-s-obligations"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes gap-analysis v2 §4.1 partial (Legal / Obligation Layer).
Today a Plan only carries a
templateHash— a hash reference to an off-chain document. That satisfies tamper-evidence but is not machine-enforceable: the orchestrator can't tell whether an execution context satisfies the terms without a human reading the underlying PDF.This PR lifts the governing-terms object into first-class structured data so commit/abort/unwind can be evaluated automatically.
What
services/obligations/types.ts—ObligationTermsschema:Consideration,validIssuance[],validPayment[],commit[],abort[],unwind[],AuthorizedParticipant[](role set matches PR M's SoD: coordinator / approver / releaser / validator / exception_manager / operator),GoverningDocument[](keyed bytemplateRef+ SHA-256templateHash, reusing the convention from existingInstrumentTerms).services/obligations/evaluator.ts— closed-operator condition engine (eq,neq,gt,gte,lt,lte,in,not_in,exists,matches,length_gte,length_lte) +all/any/notcombinators + dotted-and-indexed path resolution (e.g.plan.steps[1].type). Noeval, no code execution, deterministic.services/obligations/index.ts— public surface:canonicalize()— lexicographic key-sort JSON encoding.hashObligationTerms()— SHA-256 of canonical JSON, hex, no0xprefix.validateObligationTerms()— cheap structural check (ISO-4217 currency, hex hash, required fields, non-emptygoverningDocuments).evaluateClauses()/evaluateCommit()/evaluateAbort()— run clause arrays against a context, return per-clause results without short-circuiting.buildIssueInstrumentObligation()— derives a sensible default obligation from anissueInstrumentstep'sInstrumentTerms. Bakes in the UCP 600 / URDG 758 rule that MT760 is irrevocable, so unwind applies only when payment failed AFTER instrument dispatch (amendment H / §4.1).Why a self-contained evaluator
PR P (Rules Engine) ships a more general DSL that this evaluator is a subset of. Keeping a local copy lets PR S be merged independently of PR P — the
Conditionshape is deliberately identical so clauses authored for one run on the other. Post-merge cleanup can consolidate the two on whichever order they land.Verification
npx tsc --noEmitclean.buildIssueInstrumentObligationbinding template hash + governing law, non-throwing error surfacing for bad regex.Closes gap-analysis v2 §4.1 partial (Legal / Obligation Layer) — today a Plan only carries a templateHash (hash-reference to an off-chain document). This PR lifts the governing-terms object into structured, machine-enforceable data. - services/obligations/types.ts — ObligationTerms schema: Consideration, validIssuance[], validPayment[], commit[], abort[], unwind[], AuthorizedParticipant[] (role set matches PR M SoD), GoverningDocument[] (keyed by templateRef + SHA-256 templateHash that reuses the existing InstrumentTerms convention). - services/obligations/evaluator.ts — closed-operator condition engine (eq, neq, gt, gte, lt, lte, in, not_in, exists, matches, length_gte, length_lte) + all/any/not combinators, dotted + indexed path resolution (e.g. plan.steps[1].type). No eval, no code execution, deterministic. - services/obligations/index.ts — public surface: canonicalize(), hashObligationTerms(), validateObligationTerms(), evaluateClauses(), evaluateCommit(), evaluateAbort(), buildIssueInstrumentObligation() (derives a sensible default obligation from an issueInstrument step's InstrumentTerms — binds commit, abort, unwind, validIssuance, validPayment clauses that reflect UCP 600 / URDG 758 semantics, including the "MT760 is irrevocable so unwind only applies when payment failed AFTER instrument dispatch" rule from amendment H/§4.1). - tests/unit/obligations.test.ts — 20 tests covering: * canonicalize() key-sorting invariance + array preservation * SHA-256 hash stability and sensitivity to mutation * validateObligationTerms() (shape, ISO-4217 currency, hex hash, authorizedParticipants role required, non-empty docs) * evaluator primitives (eq/gt/lt/in/matches/length_*) * all/any/not combinators * dotted + indexed path resolution * evaluateCommit ok-true + failure attribution * evaluateAbort firing on an active exception * buildIssueInstrumentObligation binding the template hash + governingLaw into governingDocuments * non-throwing error surfacing on bad regex - Full suite: 8 suites, 100/100 passing. tsc --noEmit clean.