A plan file (.spl) is the single source of truth for a multi-agent workflow. It declares what tasks exist, which agents are available, when tasks become ready, and which agent should handle each one. The hence reasoner evaluates these declarations on every query, so the plan's answers are always up to date with the current set of facts.

SPL (Spindle Lisp) is a small, readable dialect of defeasible logic from the Spindle project. You write rules that say "normally, if these conditions hold, conclude this." When multiple rules conflict, you use prefer to say which one wins. The result is expressive routing policies that stay legible even as they grow in complexity.

Note
SPL files are plain text. You can open them in any editor, commit them to git, diff them, and read them with cat. The format is intentionally simple so that LLMs can read and write valid plans without special tooling.

Grammar reference

The complete SPL grammar in EBNF notation. This is the canonical form as defined in the Spindle Lisp parser.

theory      = statement*
statement   = fact | rule | prefer | meta
            | claims | trusts | decays | threshold

; Core
fact        = "(given" literal ")"
rule        = "(" keyword label? body head ")"
keyword     = "always" | "normally" | "except"
prefer      = "(prefer" label+ ")"
meta        = "(meta" label property* ")"

; Trust
claims      = "(claims" source claims-meta* statement* ")"
claims-meta = ":at" atom | ":sig" atom | ":id" atom | ":note" atom
trusts      = "(trusts" source number ")"
decays      = "(decays" source decay-model number ")"
decay-model = "exponential" | "linear" | "step"
threshold   = "(threshold" name number ")"

; Temporal
time-expr   = "(moment" rfc3339-string ")" | integer | "inf" | "-inf"
during      = "(during" literal time-expr time-expr ")"

; Literals
literal     = atom | "(" atom arg* ")" | "(not" literal ")"
            | during | modal
modal       = "(" modal-op literal ")"
modal-op    = "must" | "may" | "forbidden"
body        = literal | "(and" literal+ ")"
atom        = identifier | variable
variable    = "?" identifier
source      = atom
number      = float in [0.0, 1.0]

Labels on rules are optional — the reasoner auto-generates them (prefix r for normally, s for always, d for except). Explicit labels are recommended when you use prefer or meta to reference a rule by name.

Plan structure overview

Every plan file is made up of a small set of building blocks. They can appear in any order, but the conventional layout below makes plans easy to scan:

; ── 1. Plan metadata ────────────────────────────────────────── (meta plan ...) ; ── 2. Agent availability facts ────────────────────────────── (given agent-coder-available) (given agent-reviewer-available) ; ── 3. Task existence facts ─────────────────────────────────── (given task-auth) (given task-tests) ; ── 4. Task metadata ────────────────────────────────────────── (meta task-auth ...) (meta task-tests ...) ; ── 5. Dependency facts ─────────────────────────────────────── (given no-deps-auth) ; auth has no prerequisites ; completed-auth is derived by the engine ; ── 6. Arbitrary task-property facts ───────────────────────── (given security-sensitive-auth) ; ── 7. Readiness rules ──────────────────────────────────────── (normally r-ready-auth ...) (normally r-ready-tests ...) ; ── 8. Assignment rules ─────────────────────────────────────── (normally r-assign-auth-coder ...) (normally r-assign-auth-security ...) ; ── 9. Superiority declarations ─────────────────────────────── (prefer r-assign-auth-security r-assign-auth-coder) ; ── 10. Rule metadata ───────────────────────────────────────── (meta r-assign-auth-security ...) ; ── 11. Runtime-asserted facts (appended by hence) ─────────── (claims ...)

The sections below cover each building block in detail, followed by a complete annotated example.

Plan metadata block

Every plan should start with a (meta plan ...) block. This block is not evaluated as logic — it is structured documentation that hence tools can query directly. The hence plan info command reads it to display a human-readable summary of the plan.

(meta plan
  (id     "IMPL-014")
  (title  "User Authentication")
  (version "1.0.0")
  (status  "active")
  (created "2026-02-14")
  (updated "2026-02-20")
  (author  "agent:architect")
  (spec    "SPEC-014")
  (description "Implement login, session management, and token refresh"))

Query this block from the command line:

hence plan info auth.spl

Standard properties

Property Required Description
id required Unique plan identifier, e.g. "IMPL-014". Used in log output and cross-references.
title required Short human-readable name for the plan.
description required One or two sentences describing what this plan delivers. Shown in hence plan info.
version recommended Semver string. Bump when the plan's structure changes significantly.
status recommended Lifecycle state: "draft", "active", "paused", "done".
created recommended ISO 8601 date the plan was first authored.
updated recommended ISO 8601 date of the last structural edit.
author recommended Who wrote the plan. Use "agent:name" for agent-generated plans or a human name.
spec optional Reference to an upstream specification document (e.g. a SPEC id or URL).

Declaring agents

An agent is declared available by asserting a fact of the form agent-NAME-available. This fact is what readiness and assignment rules test against — if the fact is present, the agent can be assigned work; if it is absent, no assignment rule that depends on it will fire.

; These facts say which agents are currently available to take work.
(given agent-coder-available)
(given agent-reviewer-available)
(given agent-security-available)
(given agent-ops-available)

Naming convention

The naming pattern agent-NAME-available is a convention enforced by hence's query commands. NAME must be a valid kebab-case identifier — lowercase letters, digits, and hyphens. Examples:

  • agent-coder-available
  • agent-senior-reviewer-available
  • agent-ml-ops-available

The agent name used in the fact must match the value of the HENCE_AGENT environment variable (or the --agent flag) when agents invoke hence commands. Hence uses this match to route assignments correctly.

Tip
You can remove an agent from the pool at runtime by retracting the availability fact — or by simply not asserting it in the first place. Tasks that require that agent will remain unassigned until the fact is present.

Declaring tasks

A task is declared by asserting a fact of the form task-NAME. This tells the reasoner that the task exists and should participate in readiness and assignment evaluation.

; Declare four tasks for the authentication plan.
(given task-design)
(given task-auth)
(given task-tests)
(given task-review)

Naming convention

Task names are kebab-case identifiers. Keep them short and descriptive. The name becomes part of every rule name, fact name, and CLI argument that references the task, so brevity pays off. Avoid abbreviations that lose meaning outside context.

GoodAvoid
task-authtask-a (too short)
task-db-migrationtask-database-migration-step-one (too long)
task-reviewtask-ReviewCode (wrong case)
task-deploy-prodtask_deploy_prod (underscores not supported)

Task metadata

Like the plan itself, every task should have a (meta task-NAME ...) block. This block carries the human-readable description and acceptance criteria for the task. The hence query tool surfaces this information to agents at runtime.

(meta task-auth
  (description  "Implement JWT authentication with refresh tokens")
  (acceptance   "Login endpoint returns valid JWT; refresh endpoint works; all auth tests pass"))

Query task metadata from the command line:

hence query describe auth.spl task-auth

Output:

task-auth
  description  Implement JWT authentication with refresh tokens
  acceptance   Login endpoint returns valid JWT; refresh endpoint works;
               all auth tests pass

Metadata properties

Property Required Description
description required What the task involves. Written for the agent that will execute it.
acceptance required Measurable criteria that define done. Agents check these before calling hence task complete.
Tip
Write acceptance criteria as a checklist of verifiable conditions, not vague statements like "works correctly." An agent (or LLM) should be able to read the acceptance string and determine whether the task is actually done without any other context.

Dependency facts

Hence models task sequencing through two kinds of facts: no-deps facts, which you assert manually to declare that a task has no prerequisites, and completed facts, which the engine derives automatically when a task is marked done.

No-prerequisite tasks

For tasks that can start immediately (no blocking predecessors), assert a no-deps-NAME fact:

; Design has no prerequisites — it can start right away.
(given no-deps-design)

; Auth also has no prerequisites (design is a soft input, not a hard dep).
(given no-deps-auth)

The no-deps-NAME fact is what you test in the readiness rule for that task. Think of it as saying "I declare this task unblocked."

Derived completion facts

When an agent completes a task with hence task complete NAME plan.spl, the engine derives the fact completed-NAME. Downstream readiness rules test for this fact to sequence work:

; You do NOT assert completed-* manually.
; The engine derives it when the task is marked done.
; You reference it in readiness rules for dependent tasks:

(normally r-ready-tests
  (and task-tests completed-auth)
  ready-tests)

(normally r-ready-review
  (and task-review completed-tests)
  ready-review)

This gives you a simple, composable way to express arbitrary dependency graphs without a separate dependency-declaration syntax.

Note
A task with multiple predecessors just ands all the completion facts together in its readiness rule. There is no limit on the number of conditions in an and expression.

Readiness rules

A readiness rule derives a ready-NAME fact when all the conditions for a task to begin are satisfied. Once ready-NAME is derived, assignment rules can fire and the task will appear in agent task queues.

Basic readiness rule

; A task with no prerequisites: ready as soon as it exists.
(normally r-ready-auth
  (and task-auth no-deps-auth)
  ready-auth)

The form is: (normally RULE-NAME CONDITIONS CONCLUSION).

  • RULE-NAME — a unique identifier for this rule, used in prefer declarations and meta blocks.
  • CONDITIONS — an (and ...) expression of facts that must all hold.
  • CONCLUSION — the fact to derive when the conditions are satisfied.

Sequential dependency

; Tests are ready only after auth is complete.
(normally r-ready-tests
  (and task-tests completed-auth)
  ready-tests)

; Review is ready only after tests pass.
(normally r-ready-review
  (and task-review completed-tests)
  ready-review)

Multi-predecessor dependency

When a task depends on multiple predecessors, list all completion facts in a single and:

; Deploy is ready only after both review and integration-tests are complete.
(normally r-ready-deploy
  (and task-deploy completed-review completed-integration-tests)
  ready-deploy)

Readiness gated by a custom fact

You can also gate readiness on arbitrary domain facts. For example, a deployment task might only become ready once an environment-provisioned flag is asserted:

; Deploy is ready only after review completes AND staging env is provisioned.
(normally r-ready-deploy
  (and task-deploy completed-review env-staging-provisioned)
  ready-deploy)

The fact env-staging-provisioned can be asserted by a human, by a CI system, or by another agent using hence task assert.

Assignment rules

Assignment rules derive facts of the form assign-to-TASK-AGENT. When hence evaluates the plan for a given agent and task, it checks whether this fact is derived to determine if that agent is assigned.

Basic assignment rule

; Assign auth task to coder if auth is ready and coder is available.
(normally r-assign-auth-coder
  (and ready-auth agent-coder-available)
  assign-to-auth-coder)

Conditional assignment (skill routing)

A more specific rule can route a task to a specialist agent when a certain property holds:

; If auth is also marked security-sensitive, prefer the security agent.
(normally r-assign-auth-security
  (and ready-auth security-sensitive-auth agent-security-available)
  assign-to-auth-security)

Without a superiority declaration, both rules can fire simultaneously and the task appears assigned to both agents. Use prefer to resolve this conflict — see the Superiority section below.

Assignment naming convention

Assignment rule names follow the pattern r-assign-TASK-AGENT, and the derived fact follows assign-to-TASK-AGENT. This is a convention enforced by hence's query engine when resolving hence task next and hence plan board output.

ComponentExampleMeaning
Rule namer-assign-auth-coderThe rule that assigns auth to coder
Derived factassign-to-auth-coderAuth is assigned to coder
Task partauthMust match the task's declared name (task-auth)
Agent partcoderMust match the agent availability fact (agent-coder-available)

Superiority and conflict resolution

In defeasible logic, two rules that derive conflicting conclusions are said to be in conflict. Hence uses a superiority relation — declared with prefer — to resolve conflicts without requiring you to add negation conditions to every rule.

Basic prefer declaration

; When both assignment rules fire, security agent wins over coder.
(prefer r-assign-auth-security r-assign-auth-coder)

This reads: "rule r-assign-auth-security is superior to rule r-assign-auth-coder." When both rules fire, the inferior rule is defeated and its conclusion is not derived. The auth task will be assigned only to the security agent.

When to use prefer

  • Skill-based routing — a specialist rule beats a generalist rule for sensitive work.
  • Priority overrides — a high-priority assignment rule beats a normal one.
  • Environment-specific routing — a production rule beats a default rule when the prod fact is present.
  • Temporary overrides — assert a new fact at runtime to enable a new rule that has superiority over the default.

Chained superiority

You can declare multiple preference relationships. Superiority is not automatically transitive, so if you need A > B > C, declare both:

(prefer r-assign-auth-lead   r-assign-auth-security)
(prefer r-assign-auth-security r-assign-auth-coder)
Tip
Prefer prefer over adding negation conditions (not security-sensitive-auth) to inferior rules. Negation-based routing makes rules harder to read and creates maintenance burden as conditions multiply. A superiority declaration is a single line that clearly expresses intent.

Rule metadata

Rules, like tasks and the plan itself, can carry a (meta RULE-NAME ...) block. This is especially valuable for non-obvious routing decisions — it lets you explain why a rule exists and point to the policy document that mandates it.

(meta r-assign-auth-security
  (description  "Security-sensitive tasks must go to the security agent")
  (justification "Per security policy section 3.2: all auth work requires security review"))

(meta r-assign-auth-coder
  (description  "Default: assign auth task to the coder agent")
  (justification "Fallback routing when no special conditions apply"))

Query rule metadata:

hence query describe auth.spl r-assign-auth-security

Supported metadata properties

PropertyDescription
description What the rule does in plain English.
justification Why the rule exists — policy reference, rationale, or architectural decision.

Arbitrary facts for task properties

SPL has no built-in concept of task attributes or tags. Instead, you simply declare additional facts and reference them in rule conditions. This is intentionally open-ended: any identifier that doesn't collide with the reserved patterns (task-*, agent-*-available, ready-*, assign-to-*, completed-*, no-deps-*) can be used as a task property fact.

; Task properties are just named facts.
(given security-sensitive-auth)
(given high-priority-deploy)
(given requires-prod-access-deploy)
(given requires-migration-review-db-migration)
(given experimental-feature-ml-inference)

Reference them in any rule condition:

; High-priority deploy always goes to ops, never to the default coder.
(normally r-assign-deploy-ops-priority
  (and ready-deploy high-priority-deploy agent-ops-available)
  assign-to-deploy-ops)

(prefer r-assign-deploy-ops-priority r-assign-deploy-coder)
Note
Property facts are also useful for readiness gating. For example, a task tagged requires-approval-deploy might only become ready once a human asserts approval-granted-deploy at runtime.

The (and ...) conjunction

The body of every normally rule is an (and ...) expression — a conjunction of facts that must all be present for the rule to fire. The and form takes two or more fact identifiers.

; Two-condition conjunction
(normally r-ready-auth
  (and task-auth no-deps-auth)
  ready-auth)

; Three-condition conjunction
(normally r-assign-auth-security
  (and ready-auth security-sensitive-auth agent-security-available)
  assign-to-auth-security)

; Four-condition conjunction (multi-predecessor + property gate)
(normally r-ready-deploy
  (and task-deploy completed-review completed-tests env-staging-approved)
  ready-deploy)

There is no or form. If you need a task to become ready through multiple paths, write one readiness rule per path. Each rule derives the same ready-NAME conclusion:

; Task can start if either design is done OR a fast-track fact is asserted.
(normally r-ready-auth-after-design
  (and task-auth completed-design)
  ready-auth)

(normally r-ready-auth-fast-track
  (and task-auth fast-track-auth)
  ready-auth)

Adding facts at runtime

Agents and humans can assert new facts into a plan file without editing it manually. The hence task assert command appends a (claims ...) block to the file, recording who asserted the fact and when.

# Assert that the staging environment is approved for deployment
hence task assert '(given env-staging-approved)' deploy.spl

# Assert a priority escalation mid-project
hence task assert '(given high-priority-deploy)' deploy.spl

# Any agent can assert completion of custom conditions
hence task assert '(given security-audit-passed-auth)' auth.spl

After running the first command, the file gains a new block at the end:

(claims agent:ops :at "2026-02-20T14:32:11Z"
  (given env-staging-approved))

Claims blocks are semantically identical to given facts declared in the static plan. The reasoner sees them as part of the current fact database. Multiple claims can accumulate in a file over the lifetime of a project.

Note
Claims are append-only. Hence never removes a claim once it is written. If you need to retract a fact, you must edit the file manually or use hence task retract (which appends a retraction record rather than deleting the original claim).

Full annotated example

The following is a complete plan for a four-task project: design → implement → review → deploy. It uses four agents (architect, coder, reviewer, ops), demonstrates superiority, includes metadata on every task and rule, and shows a priority escalation fact.

; ════════════════════════════════════════════════════════════
; auth-service.spl — User Authentication Service
; ════════════════════════════════════════════════════════════

; ── Plan metadata ────────────────────────────────────────────

(meta plan
  (id          "IMPL-014")
  (title       "User Authentication Service")
  (version     "1.0.0")
  (status      "active")
  (created     "2026-02-14")
  (author      "agent:architect")
  (spec        "SPEC-014")
  (description "Implement login, session management, and JWT token refresh"))

; ── Agent availability ───────────────────────────────────────

(given agent-architect-available)
(given agent-coder-available)
(given agent-reviewer-available)
(given agent-ops-available)

; ── Task existence ───────────────────────────────────────────

(given task-design)
(given task-implement)
(given task-review)
(given task-deploy)

; ── Task metadata ────────────────────────────────────────────

(meta task-design
  (description "Design the auth service API: endpoints, token format, session model")
  (acceptance  "API spec doc written; JWT claims defined; session lifecycle documented; architect sign-off"))

(meta task-implement
  (description "Implement JWT auth, refresh tokens, and session management endpoints")
  (acceptance  "POST /login returns signed JWT; POST /refresh works; POST /logout invalidates session; unit tests pass"))

(meta task-review
  (description "Security review of authentication implementation")
  (acceptance  "No critical findings; token signing uses approved algorithm; secrets not logged; review sign-off committed"))

(meta task-deploy
  (description "Deploy auth service to production with blue-green cutover")
  (acceptance  "Service healthy in prod; smoke tests pass; rollback plan documented; on-call notified"))

; ── Dependency facts ─────────────────────────────────────────

(given no-deps-design)    ; design starts immediately
; implement depends on completed-design  (derived by engine)
; review    depends on completed-implement
; deploy    depends on completed-review

; ── Arbitrary task-property facts ────────────────────────────

(given security-sensitive-implement) ; implementation touches auth — security agent preferred
(given security-sensitive-review)    ; review must also go to security agent
(given requires-prod-access-deploy)  ; deploy needs ops agent with prod credentials

; NOTE: high-priority-deploy is NOT asserted here.
; It can be asserted at runtime via: hence task assert '(given high-priority-deploy)' auth-service.spl

; ── Readiness rules ──────────────────────────────────────────

(normally r-ready-design
  (and task-design no-deps-design)
  ready-design)

(normally r-ready-implement
  (and task-implement completed-design)
  ready-implement)

(normally r-ready-review
  (and task-review completed-implement)
  ready-review)

(normally r-ready-deploy
  (and task-deploy completed-review)
  ready-deploy)

; ── Assignment rules — design ─────────────────────────────────

(normally r-assign-design-architect
  (and ready-design agent-architect-available)
  assign-to-design-architect)

; ── Assignment rules — implement ──────────────────────────────

; Default: coder handles implementation.
(normally r-assign-implement-coder
  (and ready-implement agent-coder-available)
  assign-to-implement-coder)

; Security-sensitive override: security agent handles it instead.
(normally r-assign-implement-security
  (and ready-implement security-sensitive-implement agent-reviewer-available)
  assign-to-implement-reviewer)

(prefer r-assign-implement-security r-assign-implement-coder)

; ── Assignment rules — review ─────────────────────────────────

; Default: reviewer handles review.
(normally r-assign-review-reviewer
  (and ready-review agent-reviewer-available)
  assign-to-review-reviewer)

; Security-sensitive review must additionally involve coder for context.
; (Both can be assigned; no prefer needed here — parallel work is fine.)

; ── Assignment rules — deploy ─────────────────────────────────

; Default: ops handles deployment.
(normally r-assign-deploy-ops
  (and ready-deploy agent-ops-available)
  assign-to-deploy-ops)

; High-priority override: if escalated, ops gets it AND coder is notified.
(normally r-assign-deploy-ops-priority
  (and ready-deploy high-priority-deploy agent-ops-available)
  assign-to-deploy-ops)

; (Same conclusion — ensures ops gets it even without the priority fact.)
; The priority fact can gate other rules, e.g. skip canary and go direct.

; ── Superiority declarations ──────────────────────────────────

(prefer r-assign-implement-security r-assign-implement-coder)

; ── Rule metadata ─────────────────────────────────────────────

(meta r-ready-design
  (description  "Design task is always unblocked at plan start")
  (justification "Design has no upstream dependencies; it is the root of the plan DAG"))

(meta r-ready-implement
  (description  "Implementation is unblocked once design is approved")
  (justification "Coders need a finalized API spec before writing auth logic"))

(meta r-ready-review
  (description  "Security review is unblocked once implementation is complete")
  (justification "Review cannot start without working code to inspect"))

(meta r-ready-deploy
  (description  "Deploy is unblocked once security review passes")
  (justification "No auth code ships to production without security sign-off"))

(meta r-assign-design-architect
  (description  "Architect owns the design task")
  (justification "API design decisions require system-level context held by the architect"))

(meta r-assign-implement-coder
  (description  "Default assignment: coder implements the auth endpoints")
  (justification "Fallback routing for implementation when no sensitivity flag is set"))

(meta r-assign-implement-security
  (description  "Security-sensitive implementation goes to the reviewer agent")
  (justification "Per SPEC-014 §4: all auth endpoint code must be written by a security-cleared agent"))

(meta r-assign-review-reviewer
  (description  "Reviewer agent conducts the security review")
  (justification "Code review for auth requires security expertise"))

(meta r-assign-deploy-ops
  (description  "Ops agent handles production deployment")
  (justification "Ops holds production credentials and owns the deployment runbook"))

What the reasoner derives

Given the facts above (before any tasks are completed), the reasoner produces this task board:

TaskReady?Assigned toBlocked by
design ready architect
implement blocked completed-design not yet derived
review blocked completed-implement not yet derived
deploy blocked completed-review not yet derived

Once the architect completes design (hence task complete design auth-service.spl), the engine derives completed-design, which makes r-ready-implement fire, deriving ready-implement, which makes r-assign-implement-security fire (since security-sensitive-implement is present), which defeats r-assign-implement-coder via the superiority declaration.

Runtime escalation example

To escalate the deploy task mid-project, assert the priority fact at runtime:

hence task assert '(given high-priority-deploy)' auth-service.spl

The file grows a claims block:

(claims agent:ops :at "2026-02-20T16:01:45Z"
  (given high-priority-deploy))

On the next evaluation, the reasoner sees high-priority-deploy and can use it in any rule that tests for it — for example, a rule that skips the canary deployment step when the priority flag is set.

Best practices

1. Always include a plan metadata block

Every plan file must have a (meta plan ...) block with at minimum id, title, and description. This information is surfaced by hence plan info, indexed by hence.run, and used by LLM agents to understand what they are working on without reading the full file.

2. Add description and acceptance to every task

A task without a (meta task-NAME ...) block is opaque. An agent claiming the task has no way to know what done looks like. Always write an acceptance string that is concrete, checkable, and unambiguous. If you cannot write it, the task is not well-defined enough to be worked.

3. Name rules descriptively

Follow the conventions: r-ready-TASK for readiness rules, r-assign-TASK-AGENT for assignment rules. This makes the file scannable and enables hence's query commands to parse rule names correctly. Avoid generic names like rule1 or assign-default.

4. Use prefer instead of complex negation

Do not write guards like (and ready-auth (not security-sensitive-auth) agent-coder-available). Instead, write a positive general rule and a more specific rule that wins via prefer. This keeps conditions shorter, avoids double-negation bugs, and makes the policy intent explicit.

; Bad — negation guards couple rules together
(normally r-assign-auth-coder
  (and ready-auth not-security-sensitive-auth agent-coder-available)
  assign-to-auth-coder)

; Good — independent rules, prefer resolves conflict
(normally r-assign-auth-coder
  (and ready-auth agent-coder-available)
  assign-to-auth-coder)

(normally r-assign-auth-security
  (and ready-auth security-sensitive-auth agent-security-available)
  assign-to-auth-security)

(prefer r-assign-auth-security r-assign-auth-coder)

5. Keep readiness conditions positive

Readiness rules should test for the presence of facts, not the absence of problems. Instead of "task is ready when no blocker exists," write "task is ready when approval is granted" and assert the approval fact when it happens. Positive conditions are easier to trace and debug with hence query explain.

6. Use no-deps for root tasks explicitly

Every root task (one with no predecessors) needs a (given no-deps-NAME) fact. Without it, the readiness rule has no way to fire since it always needs at least one condition beyond the bare task existence fact. This is a common source of "why isn't my task ready?" confusion.

7. Validate before running agents

Before spawning agents against a new or modified plan, run the validator:

hence plan validate auth-service.spl

The validator checks for: unreferenced task names in rules, missing no-deps facts for apparent root tasks, malformed prefer references, and duplicate rule names.

8. Keep the plan file in version control

A plan file is a living document. Commit it to git alongside the code it coordinates. The accumulation of (claims ...) blocks gives you a full audit trail of what was asserted, by whom, and when. Do not regenerate the plan from scratch — append to it.

9. One plan per coherent unit of work

A plan works best when it represents a single coherent deliverable. If you find yourself writing more than eight or ten tasks, consider whether the work should be split into separate plans. Each plan gets its own id, and plans can reference each other through custom facts (e.g., (given upstream-IMPL-013-complete)).

Tip
Run hence plan board auth-service.spl after editing the plan to see the current task board. This is the fastest way to check that your rules are firing as expected before agents are involved.

Quick reference

ConstructSyntaxPurpose
Plan metadata (meta plan (id "...") (title "...") ...) Human-readable plan info, queried by hence plan info
Agent availability (given agent-NAME-available) Declares an agent is in the pool
Task existence (given task-NAME) Declares a task in the plan
Task metadata (meta task-NAME (description "...") (acceptance "...")) Queried by hence query describe
Root task flag (given no-deps-NAME) Marks task as having no prerequisites
Completion fact completed-NAME Derived by engine when task is marked done; used in downstream readiness rules
Task property (given PROPERTY-NAME) Arbitrary fact used in rule conditions for routing
Readiness rule (normally r-ready-NAME (and ...) ready-NAME) Derives ready-NAME when conditions hold
Assignment rule (normally r-assign-TASK-AGENT (and ...) assign-to-TASK-AGENT) Derives assignment fact for an agent
Superiority (prefer RULE-A RULE-B) RULE-A defeats RULE-B when both fire
Rule metadata (meta RULE-NAME (description "...") (justification "...")) Explains the rule's intent and policy basis
Runtime fact hence task assert '(given FACT)' plan.spl Appends a (claims ...) block to the file

Related pages