Line

You are reading the unreleased docs (next). See latest (v0.9)

packages/core/block-contract

the uniform block contract: { id, writes, declaredScope, actions, run }.

Core engine, next. Source: packages/core/block-contract.mjs.

Type Aliases#

ActionRule#

ActionRule = object

Defined in: packages/core/block-contract.mjs:73

One row of an action rule table. applies decides whether the action is offered at all in this state (default: yes); blockedBy returns the reason it is offered but disabled, or a falsy value when it is available.

Type Parameters#

Type Declaration#

applies?

optional applies?: (state) => boolean

Parameters
state

any

Returns

boolean

blockedBy?

optional blockedBy?: (state) => string | null | undefined

Parameters
state

any

Returns

string | null | undefined

blockId?

optional blockId?: string

id

id: string

kind

kind: "mechanical" | "ai" | "free"

label

label: string

requires?

optional requires?: string[]


Block#

Block = object

Defined in: packages/core/block-contract.mjs:70

The uniform block contract.

Type Parameters#

Type Declaration#

actions

actions: (state, ctx?) => BlockAction[]

Parameters
state

any

ctx?

any

Returns

BlockAction[]

declaredScope

declaredScope: (args, ctx?) => DeclaredScope | null

Parameters
args

any

ctx?

any

Returns

DeclaredScope | null

id

id: string

label?

optional label?: string

meta?

optional meta?: object

run

run: (scope, args, ctx?) => { changedFiles: string[]; } | Promise<{ changedFiles: string[]; }>

Parameters
scope

DeclaredScope | null

args

any

ctx?

any

Returns

{ changedFiles: string[]; } | Promise<{ changedFiles: string[]; }>

summary?

optional summary?: string

writes

writes: boolean


BlockAction#

BlockAction = object

Defined in: packages/core/block-contract.mjs:66

One legal next action. enabled: false is an action shown disabled, with why (the rule that forbids it here); blockId names the block it hands over to; gates lists the guardrails an ai or free action passes through.

Type Parameters#

Type Declaration#

blockId?

optional blockId?: string

enabled

enabled: boolean

gates?

optional gates?: string[]

id

id: string

kind

kind: "mechanical" | "ai" | "free"

label

label: string

requires?

optional requires?: string[]

why?

optional why?: string


DeclaredScope#

DeclaredScope = object

Defined in: packages/core/block-contract.mjs:63

A block's declared scope; the same shape as a plan step's touches (packages/core/plan.mjs).

Type Parameters#

Type Declaration#

features?

optional features?: string[]

files?

optional files?: object[]

Variables#

ACTION_KINDS#

const ACTION_KINDS: readonly string[]

Defined in: packages/core/block-contract.mjs:30

The three kinds of action a block can offer.


BLOCK_ERROR_CODES#

const BLOCK_ERROR_CODES: Readonly<{ ACTION_FIELD_INVALID: "ACTION_FIELD_INVALID"; ACTION_GATES_MISSING: "ACTION_GATES_MISSING"; ACTION_ID_DUPLICATE: "ACTION_ID_DUPLICATE"; ACTION_ID_INVALID: "ACTION_ID_INVALID"; ACTION_KIND_INVALID: "ACTION_KIND_INVALID"; ACTION_LABEL_INVALID: "ACTION_LABEL_INVALID"; ACTION_NOT_OBJECT: "ACTION_NOT_OBJECT"; ACTION_WHY_MISSING: "ACTION_WHY_MISSING"; BLOCK_ACTIONS_INVALID: "BLOCK_ACTIONS_INVALID"; BLOCK_FIELD_NOT_FUNCTION: "BLOCK_FIELD_NOT_FUNCTION"; BLOCK_ID_INVALID: "BLOCK_ID_INVALID"; BLOCK_NOT_OBJECT: "BLOCK_NOT_OBJECT"; BLOCK_SCOPE_INVALID: "BLOCK_SCOPE_INVALID"; BLOCK_SCOPE_NOT_EMPTY: "BLOCK_SCOPE_NOT_EMPTY"; BLOCK_SCOPE_NULL: "BLOCK_SCOPE_NULL"; BLOCK_WRITES_INVALID: "BLOCK_WRITES_INVALID"; RUN_READONLY_WROTE: "RUN_READONLY_WROTE"; RUN_RESULT_INVALID: "RUN_RESULT_INVALID"; }>

Defined in: packages/core/block-contract.mjs:41

Every rejection the contract validator can produce, by name, so callers and tests match on a code, not on text.


BLOCK_ID_RE#

const BLOCK_ID_RE: RegExp

Defined in: packages/core/block-contract.mjs:81

The shape of a dotted block id (create.unit, process.lifecycle); choosers reuse it.


GUARDRAILS#

const GUARDRAILS: readonly string[]

Defined in: packages/core/block-contract.mjs:38

The guardrails that never switch off, by the name each has in the code: containment (paths stay inside the project root), approval (per-diff approval, approvalGate.mjs), sessionGate (the Cockpit server's session check in front of every route) and checks (deterministic validation of the staged tree before AI or free output lands).

Functions#

defineBlock()#

defineBlock(spec): Block

Defined in: packages/core/block-contract.mjs:281

Build a block: validate the spec, freeze it and return it. The only constructor, so a malformed block fails where it is defined rather than where a screen first tries to read its menu.

Parameters#

spec

Block

{ id, writes, declaredScope, actions, run } plus optional label, summary, meta.

Returns#

Block

The frozen block.

Throws#

Listing every problem, when the spec does not satisfy the contract.

Example#

const block = defineBlock({ id: 'validate', writes: false, declaredScope: () => emptyScope(), actions: () => [], run: () => ({ changedFiles: [] }) });

deriveActions()#

deriveActions(rules, state?): BlockAction[]

Defined in: packages/core/block-contract.mjs:195

Turn a rule table and a state into the legal next actions -- the only way a block's menu is produced. A rule whose applies(state) is false is not offered; one whose blockedBy(state) returns a reason is offered disabled with that reason as why; ai and free actions are stamped with every guardrail in gates. Pure and order-preserving.

Parameters#

rules

ActionRule[]

The block's rule table.

state?

any = {}

Whatever state the rules read (a process state, a step, a plan, ...).

Returns#

BlockAction[]

The actions, in rule order.

Example#

deriveActions([{ id: 'run', kind: 'mechanical', label: 'Run', blockedBy: (s) => (s.ok ? null : 'Fix the arguments first.') }], { ok: false });
// => [{ id: 'run', kind: 'mechanical', label: 'Run', enabled: false, why: 'Fix the arguments first.' }]

emptyScope()#

emptyScope(): DeclaredScope

Defined in: packages/core/block-contract.mjs:92

The scope of a read-only block: no features, no files.

Returns#

DeclaredScope

A fresh empty scope, { features: [], files: [] }.

Example#

emptyScope(); // => { features: [], files: [] }

isEmptyScope()#

isEmptyScope(scope): boolean

Defined in: packages/core/block-contract.mjs:105

True when a scope names nothing at all (what a read-only block declares).

Parameters#

scope

DeclaredScope

A declared scope.

Returns#

boolean

true for an object with no features and no files; false for null (an unknown scope is not an empty one).

Example#

isEmptyScope({ features: [], files: [] }); // => true

runBlock()#

runBlock(block, args?, ctx?): Promise<{ changedFiles: string[]; scope: DeclaredScope; }>

Defined in: packages/core/block-contract.mjs:301

Run a block through the contract: derive its scope from args, call run(scope, args, ctx), and check the result is { changedFiles: string[] } of project-relative paths (a read-only block that reports a changed file is refused). The changed files come back sorted and de-duplicated, so the same run yields the same list.

Parameters#

block

Block

A block from defineBlock.

args?

any = {}

The arguments (a plan step's args).

ctx?

any = {}

Caller context passed to declaredScope and run (e.g. { root, exec }).

Returns#

Promise<{ changedFiles: string[]; scope: DeclaredScope; }>

The scope the block declared and the files it reports changed.

Throws#

With a code from BLOCK_ERROR_CODES when the result breaks the contract.

Example#

const { scope, changedFiles } = await runBlock(block, { name: 'cart' }, { root });

validateAction()#

validateAction(action, at?): object

Defined in: packages/core/block-contract.mjs:136

Validate one action. An ai or free action must list every guardrail in gates, and a disabled action must say why.

Parameters#

action

any

The action to check.

at?

string = 'action'

Path prefix for error paths.

Returns#

object

Every problem found; never throws.

errors

errors: object[]

valid

valid: boolean

Example#

validateAction({ id: 'run', kind: 'mechanical', label: 'Run', enabled: true }).valid; // => true

validateActions()#

validateActions(actions, at?): object

Defined in: packages/core/block-contract.mjs:168

Validate a list of actions: each one, plus unique ids.

Parameters#

actions

any

The value block.actions(state) returned.

at?

string = 'actions'

Path prefix for error paths.

Returns#

object

Every problem found; never throws.

errors

errors: object[]

valid

valid: boolean

Example#

validateActions([{ id: 'run', kind: 'mechanical', label: 'Run', enabled: true }]).valid; // => true

validateBlock()#

validateBlock(block, options?): object

Defined in: packages/core/block-contract.mjs:223

Validate a block's static shape: id, writes, the three functions. With options.state, also calls block.actions(state) and validates what comes back (unique ids, kinds, disabled-with-why, guardrails on ai/free). With options.args, also calls declaredScope(args) and checks it: valid touches shape, empty for a read-only block, never null for one.

Parameters#

block

any

The candidate block.

options?

Optional state/args to exercise actions and declaredScope with.

args?

any

ctx?

any

state?

any

Returns#

object

Every problem found; never throws (a throwing function is reported).

errors

errors: object[]

valid

valid: boolean

Example#

validateBlock(block, { state: { processState: 'queued' } }).errors;

validateScope()#

validateScope(scope, at?): object

Defined in: packages/core/block-contract.mjs:120

Validate a declared scope with the plan validator's own touches rules (unknown keys, relative paths, known change kinds), so a scope that passes here is accepted as a step's touches by validatePlan.

Parameters#

scope

any

The value to check.

at?

string = 'scope'

Path prefix for error paths.

Returns#

object

Every problem found; never throws.

errors

errors: object[]

valid

valid: boolean

Example#

validateScope({ features: ['cart'], files: [{ path: 'features/cart/types.ts', change: 'create' }] }).valid; // => true