Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
102 changes: 102 additions & 0 deletions examples/08_controller_preview_diff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import {
DECISION_CLARIFY,
DECISION_PASSTHROUGH,
DECISION_UPDATE,
createEngine,
getPolicyItems,
getPremiseValue,
get_decision_state,
is_clarify,
is_update,
preview,
state_diff,
step,
type Decision,
type EngineState
} from '../src/index.js';

declare const process: { argv: string[] };

function summarizeState(state: EngineState): {
premise: string | null;
usePolicies: string[];
prohibitPolicies: string[];
} {
return {
premise: getPremiseValue(state),
usePolicies: getPolicyItems(state, 'use'),
prohibitPolicies: getPolicyItems(state, 'prohibit')
};
}

function summarizeDecision(decision: Decision): {
kind: typeof DECISION_UPDATE | typeof DECISION_CLARIFY | typeof DECISION_PASSTHROUGH;
promptToUser: string | null;
decisionState: ReturnType<typeof summarizeState> | null;
} {
if (is_update(decision)) {
const decisionState = get_decision_state(decision);
return {
kind: DECISION_UPDATE,
promptToUser: null,
decisionState: decisionState ? summarizeState(decisionState) : null
};
}

if (is_clarify(decision)) {
return {
kind: DECISION_CLARIFY,
promptToUser: decision.prompt_to_user,
decisionState: null
};
}

return {
kind: DECISION_PASSTHROUGH,
promptToUser: null,
decisionState: null
};
}

export function runExample08(): {
stateBeforePreview: ReturnType<typeof summarizeState>;
preview: {
wouldMutate: boolean;
decision: ReturnType<typeof summarizeDecision>;
};
stateChangedAfterPreview: boolean;
apply: {
decision: ReturnType<typeof summarizeDecision>;
stateAfterStep: ReturnType<typeof summarizeState>;
};
} {
const engine = createEngine();

const stateBeforePreview = engine.state;

const previewResult = preview(engine, 'prohibit peanuts');

const stateAfterPreview = engine.state;
const diffAfterPreview = state_diff(stateBeforePreview, stateAfterPreview);

const stepResult = step(engine, 'prohibit peanuts');

return {
stateBeforePreview: summarizeState(stateBeforePreview),
preview: {
wouldMutate: previewResult.would_mutate,
decision: summarizeDecision(previewResult.decision)
},
stateChangedAfterPreview: diffAfterPreview.changed,
apply: {
decision: summarizeDecision(stepResult.decision),
stateAfterStep: summarizeState(stepResult.state)
}
};
}

if (typeof process !== 'undefined' && process.argv[1] && import.meta.url === new URL(process.argv[1], 'file://').href) {
const result = runExample08();
console.log('example 08: controller preview + state diff + apply flow');
console.log(JSON.stringify(result, null, 2));
}
5 changes: 5 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,8 @@ Demonstrates transcript replay behavior with `compile_transcript(messages)` and

Demonstrates explicit single-policy correction without `reset policies`:
`prohibit peanuts` -> `remove policy peanuts` -> `use peanuts`.

## 08_controller_preview_diff.ts

Demonstrates controller-layer auditability with `preview(engine, input)` and `state_diff(before, after)`.
Shows that preview does not mutate live engine state, then applies the same input with `step(engine, input)`.
Loading