feat(planning): plans, validation, planner and a restart-safe executor

Plans turn intentions into revisable dependency graphs (migration 0027):
objective, assumptions, ordered or dependent steps, capabilities,
expected observations, preconditions, risk, estimates, budget,
completion criteria, abort conditions and the alternatives considered.

- Validation owns structure: acyclic dependencies, registered and
  unlocked capabilities only, schema-valid parameters, targets that agree
  with parameters, read-only checks, a closed set of step kinds and
  internal actions. Model proposals are parsed into the same structure;
  fields that could carry authority or claim progress are reported as
  ignored, and an invalid draft is stored as a draft that never runs.
- The planner prefers deterministic strategies: a direct action verified
  by observation, a read-only diagnosis that requests temporary scoped log
  access with risk and fallback stated, and an escalation that diagnoses
  and informs the operator instead of repeating a failed action. Failed
  strategies are not reused; a model is consulted only when none applies.
- The executor holds no state in memory: it claims a step with a
  conditional update, acts only through Invoke and RequestCapability, and
  records the outcome. Waiting for approval or access forms a subordinate
  intention and blocks the parent with the reason. Expectations become
  verification predictions resolved from a read-only observation, so an
  HTTP-level success is never taken as the outcome; a miss is a prediction
  error that weakens the supporting belief and triggers reconsideration.
  Replanning creates a new revision, withdraws open requests of the old
  one and keeps it as history, within a replan limit. Checks never raise
  approval prompts. Budgets stop runaway plans. Preconditions skip actions
  when the world already changed. Dream plans and disabled sandbox minds
  never act. An interrupted non-idempotent action with no record is
  blocked for verification instead of repeated; recorded outcomes are
  adopted. Completed plans are stored as procedural memories; a completed
  escalation does not count as fulfilling the intention.
- Executions and approvals record their origin; minds may withdraw their
  own pending approval requests; Preview reports a decision without side
  effects; service.status is allowed by default as a bounded read.
This commit is contained in:
Andy Dixon
2026-09-14 23:38:44 +01:00
parent 9db188ffe6
commit 813b067842
14 changed files with 3464 additions and 20 deletions
+8
View File
@@ -0,0 +1,8 @@
ALTER TABLE approval_requests DROP CONSTRAINT approval_requests_status_check;
ALTER TABLE approval_requests ADD CONSTRAINT approval_requests_status_check CHECK (status IN ('pending','approved','denied','expired','executed','failed'));
DROP INDEX IF EXISTS approval_requests_origin_idx;
DROP INDEX IF EXISTS tool_executions_origin_idx;
ALTER TABLE approval_requests DROP COLUMN origin;
ALTER TABLE tool_executions DROP COLUMN origin;
DROP TABLE IF EXISTS plan_steps;
DROP TABLE IF EXISTS plans;
+84
View File
@@ -0,0 +1,84 @@
-- Plans and plan steps (PLAN §8.2 items 7–8, docs/planning.md). A plan is a
-- revisable DAG serving one intention; replans are new revisions that keep
-- the old plan as history.
CREATE TABLE plans (
id uuid PRIMARY KEY,
mind_id uuid NOT NULL REFERENCES minds(id),
intention_id uuid NOT NULL REFERENCES intentions(id),
revision integer NOT NULL DEFAULT 1,
supersedes uuid REFERENCES plans(id),
superseded_by uuid,
objective text NOT NULL,
origin text NOT NULL CHECK (origin IN ('template','model','operator','dream','replan')),
strategy text NOT NULL DEFAULT '',
status text NOT NULL DEFAULT 'draft' CHECK (status IN ('draft','validated','waiting','executing','blocked','awaiting_approval','replanning','completed','failed','abandoned','superseded')),
assumptions jsonb NOT NULL DEFAULT '[]'::jsonb,
completion_criteria text NOT NULL DEFAULT '',
abort_conditions jsonb NOT NULL DEFAULT '[]'::jsonb,
alternatives jsonb NOT NULL DEFAULT '[]'::jsonb,
risk text NOT NULL DEFAULT '',
estimate jsonb NOT NULL DEFAULT '{}'::jsonb,
actual jsonb NOT NULL DEFAULT '{}'::jsonb,
budget jsonb NOT NULL DEFAULT '{}'::jsonb,
validation jsonb NOT NULL DEFAULT '{}'::jsonb,
require_approval boolean NOT NULL DEFAULT false,
max_replans integer NOT NULL DEFAULT 3,
llm_call uuid,
status_reason text NOT NULL DEFAULT '',
closed_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX plans_mind_idx ON plans (mind_id, status, created_at DESC);
CREATE INDEX plans_intention_idx ON plans (intention_id, revision);
CREATE TABLE plan_steps (
id uuid PRIMARY KEY,
plan_id uuid NOT NULL REFERENCES plans(id),
mind_id uuid NOT NULL REFERENCES minds(id),
key text NOT NULL,
position integer NOT NULL,
title text NOT NULL,
kind text NOT NULL CHECK (kind IN ('capability','request_capability','verify','wait','monitor','internal')),
depends_on text[] NOT NULL DEFAULT '{}',
capability text NOT NULL DEFAULT '',
target text NOT NULL DEFAULT '',
params jsonb NOT NULL DEFAULT '{}'::jsonb,
require_approval boolean NOT NULL DEFAULT false,
expectation jsonb,
precondition jsonb,
spec jsonb NOT NULL DEFAULT '{}'::jsonb, -- kind-specific: request, check, wait, monitor, internal action
status text NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','ready','executing','awaiting_approval','awaiting_grant','waiting','completed','failed','skipped','blocked','cancelled')),
attempts integer NOT NULL DEFAULT 0,
max_attempts integer NOT NULL DEFAULT 1,
lease_until timestamptz,
next_run_at timestamptz,
approval_id uuid,
request_id uuid,
execution_id uuid,
prediction_id uuid,
intention_id uuid, -- subordinate intention this step formed
result jsonb,
observation jsonb,
error text NOT NULL DEFAULT '',
status_reason text NOT NULL DEFAULT '',
started_at timestamptz,
finished_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (plan_id, key)
);
CREATE INDEX plan_steps_due_idx ON plan_steps (status, next_run_at) WHERE status IN ('ready','executing','awaiting_approval','awaiting_grant','waiting');
CREATE INDEX plan_steps_plan_idx ON plan_steps (plan_id, position);
-- Provenance for recovery: which cognitive object asked for an execution
-- or approval, so an interrupted step can find what already happened.
ALTER TABLE tool_executions ADD COLUMN origin text NOT NULL DEFAULT '';
ALTER TABLE approval_requests ADD COLUMN origin text NOT NULL DEFAULT '';
CREATE INDEX tool_executions_origin_idx ON tool_executions (origin) WHERE origin <> '';
CREATE INDEX approval_requests_origin_idx ON approval_requests (origin) WHERE origin <> '';
-- A mind may withdraw an approval request it no longer needs.
ALTER TABLE approval_requests DROP CONSTRAINT approval_requests_status_check;
ALTER TABLE approval_requests ADD CONSTRAINT approval_requests_status_check CHECK (status IN ('pending','approved','denied','expired','executed','failed','withdrawn'));