This is the multi-page printable view of this section.
Click here to print.
Return to the regular view of this page.
Goal
Versioned goal resources, local storage, and agent workflow
This project owns the goal execution skill and the deterministic local store
that supports it. Ordinary tasks use init for the objective and acceptance,
checkpoint --summary --subject --next-action for local progress, and show
for bounded continuation. The store derives IDs and digests while retaining
explicit resource-version checks. Detailed attempt plans and histories remain
available for maintained goals, repeated failures, and complex coordination.
projects/goal/
api/v1alpha1/ Portable resource types and domain validation
cmd/goal/ Cobra command wiring and command tests
docs/ Current architecture and generated diagrams
internal/fsstore/ Local persistence, per-goal locking, and store tests
skills/goal/ Model-facing execution protocol and evaluations
Go dependencies flow from cmd/goal to internal/fsstore to
api/v1alpha1. The API package has no filesystem or command dependency.
The Go toolchain is Bazel’s @rules_go//go; run the package targets below
rather than a host-installed go.
The command stores Kubernetes-inspired goals.alwaldend.com/v1alpha1 YAML
resources and canonical, digest-bound attempt Markdown in ordinary repository
files. Each attempt.yaml binds plan.md, result.md, and evidence/*.md by
SHA-256. Goal.status.plans records durable plans with one active plan at a
time and states active, accepted, rejected, or superseded; attempts can
bind to a plan with spec.planID. Each goal record’s README.md is a generated, bounded,
replaceable projection. The portable API treats resource versions as opaque
and excludes local ownership paths from desired state. The filesystem backend
supplies one cooperative lock per goal, local numeric resource versions,
atomic file replacement by sibling temporary-file rename, session bindings,
promotion, non-destructive unversioned-record import, and recoverable
multi-file publication. Before a multi-file mutation, the backend stages exact
after-images and installs a .goal-publication.yaml intent in the goal
record; goal doctor classifies the publication state and goal recover
replays or discards a pending intent. It is a local file editor with atomic
per-file replacement; it does not claim cross-file transaction semantics
without the publication intent.
These files are not installed CRDs. A future Kubernetes adapter can convert
the API types after adding Kubernetes metadata types, structural schemas,
status-subresource behavior, generated runtime methods, and reconciliation of
API-server-owned metadata.
Run the command through Bazel:
bazel_agent bazel run //projects/goal/cmd/goal -- --help
Diagnose and finish an interrupted publication:
bazel_agent bazel run //projects/goal/cmd/goal -- doctor --goal-dir $GOAL_DIR
bazel_agent bazel run //projects/goal/cmd/goal -- recover --goal-dir $GOAL_DIR
The canonical skill lives at skills/goal; .agents/skills/goal is only its
repository discovery symlink.
1 -
Goal graph organization
Use this reference when one objective may need related goals, parallel work,
or dependency-aware resumption. The graph is a view over ordinary local Goal
resources. The execution harness remains responsible for scheduling and
permissions.
Decide whether to decompose
Keep ordinary ordered steps in one attempt plan. Create a separate goal only
when the subgoal benefits from at least one independent property:
- lifecycle or resumption;
- acceptance criteria and evidence;
- ownership or retention;
- substantial parallel execution; or
- reuse by more than one objective.
The coordination cost of another goal and edge must be lower than the value of
making that boundary explicit.
Use typed relationships
Goal.spec.relationships supports three same-catalog references:
parentGoalRef expresses hierarchy, not execution order;
dependsOnGoalRefs names prerequisites; and
supersedesGoalRefs records lineage.
A parent is not a Kubernetes owner reference and never implies garbage
collection. Supersession does not redirect dependencies or authorize mutation
of the older goal. Only an achieved prerequisite satisfies a dependency;
an open prerequisite waits, while an abandoned or superseded prerequisite
blocks until the relationship is explicitly revised.
Keep each relationship kind acyclic independently. Do not test the union of
different edge kinds as one directed acyclic graph. Missing targets make a
catalog view unknown, rather than making one otherwise well-formed Goal
resource invalid.
Change structure deliberately
Treat inferred relationships as proposals, never facts. Bind a proposal to
its source evidence and the goal generation it inspected. A coordinator must
review it, analyze the complete catalog for cycles, and publish it through the
normal resource-version compare-and-swap path. A worker must not rewrite
canonical relationships.
Relationship publication completely replaces the dependency and supersession
lists. Omitting the parent preserves it; clearing the parent is an explicit
operation. Close an active attempt before changing relationships: each attempt
is bound to the goal generation and portable goal-state digest that existed
when work began. An accepted relationship request advances generation and
resource version even when normalization makes it a semantic no-op.
Per-goal locks do not make a catalog transaction. Cycle prevention checks a
snapshot assembled by reading each goal under its own lock. Concurrent writes
to different goals can both pass against older snapshots and jointly create a
cycle. The single coordinator must avoid those conflicting writes; always run
the deterministic graph projection again after parallel structural changes and
repair any reported cycle.
Dispatch only goals whose prerequisites are satisfied. Agent selection,
capability discovery, delegation, communication topology, and runtime
scheduling belong to the execution harness.
Use the complete deterministic graph analysis for inspection. A catalog with
unresolved references is Unknown; a cyclic catalog is Invalid. Graph
analysis does not truncate its input, so report a catalog read or validation
failure instead of inferring readiness from a partial view.
Attempt input bindings, criteria and goal-state digests, structured reviews,
and artifact digests form the provenance graph for evidence.
2 -
Attempt lifecycle and evidence
Use this reference for maintained goals, repeated failures, and complex
coordination. Ordinary progress uses the compact checkpoint workflow in the
skill entry point.
Plan before work
One attempt is a bounded work unit with one reviewable outcome. Establish before implementation:
- the observed Goal resource version as the next checkpoint’s
compare-and-swap token; the store derives generation, lifecycle, criteria,
and digest bindings rather than requiring manual transcription;
- the uncertainty or stable defect being targeted;
- work type and hypothesis when the work tests one;
- the smallest high-leverage module or question, its stop condition, and the
minimum whole-result context needed to detect interface or integration harm;
- the plan, inputs, parameters, intended result identity, and review packet;
- affected criteria and the fixed regression checks; and
- parent checkpoint or prior attempt when one exists.
Preflight deterministic geometry, numeric gates, controlling references,
paths, APIs, tools, permissions, and whether the proposed artifact can satisfy
its own acceptance gate. Preserve material preflight evidence without turning
discarded prose drafts into fake attempts.
Prefer a bounded module whose result can change the next decision. Do not
polish it in isolation: retain enough surrounding context to judge composition,
interfaces, and fixed regressions. If a cheap check decisively falsifies the
module, interface, or approach, stop work and close the attempt with that
evidence rather than completing the original plan mechanically.
Bind evidence to what was tested
A criterion verdict is one of pass, fail, or unverified. Evidence must
identify its criterion revision and the exact tested subject:
- content: a digest or immutable version;
- source change: source revision and relevant configuration;
- stateful operation: target identity, environment/config revision, operation
receipt, and observed postcondition;
- research or decision: source set, retrieval date where relevant, analysis
artifact, and review method.
A later change that can affect a verdict invalidates it until rerun. Never
combine visual evidence from one candidate with technical evidence from
another. Command success alone is not evidence of the desired postcondition.
For a monolithic shared artifact without stable mergeable interfaces, derive
each candidate from an exact immutable baseline and keep it isolated. Use an
approved baseline when the task requires approval. Compare the candidate in
the minimum whole-result context needed to detect integration regressions.
Bind acceptance or approval evidence to its exact identity; the canonical
writer may promote it only with existing mutation authority and when the
task’s acceptance and approval policy permits. Do not manufacture a permanent
file split solely to make concurrent editing possible.
Review and close
Inspect likely failure views and edge cases, not only the best output. For
subjective work, use consistent comparisons and independent review when
available. Judge absolute quality against the target rather than improvement
from a weak predecessor.
Close the attempt with:
- work actually performed;
- artifacts and raw verification evidence;
- a verdict for every affected criterion and regression check;
- the dominant remaining failure;
- the decision to accept, refine, or reset; and
- a process audit covering measurable movement and feedback bottlenecks.
Publish the close only if the goal resource version and lifecycle generation
still match. A stale attempt remains useful evidence, but it cannot silently
become canonical or reopen a closed lifecycle.
Retry and finish
When a required criterion fails or remains unverified, keep the outcome open,
update the stable failure count, and choose the highest-leverage next attempt.
After the same defect survives twice, materially change strategy.
At least every three closed attempts, and immediately on a stalled trend,
examine the complete attempt history and the end-to-end process, not only the
latest failure. Identify systemic delays, repeated assumptions, weak evidence,
poor decomposition, and unhelpful tooling. Process changes are justified only
when they improve the speed or reliability of producing the requested result.
Record the review and the last attempt it covers in attempt evidence or the
attempt result so its cadence survives a session change.
For final acceptance, freeze one exact result, run every required criterion
and the complete regression set, deliver or export it, and verify that delivery
did not change its identity. If delivery changes the tested subject, rerun the
full evidence plan.
3 -
Use this reference only when promoting a workspace goal or importing an
unversioned record.
Promotion changes storage and retention, not goal identity. It must:
- require an explicit maintained-project decision and an owning project root;
- lock the source and destination paths in canonical order, then validate the
source at an expected resource version;
- preserve the goal ID and closed attempt history;
- record source scope and promotion provenance;
- require repository-relative or stable external links;
- retain, copy, or make reproducible every acceptance-critical artifact;
- scan for absolute local paths, credentials, private environment details,
and ignored evidence that would become broken; and
- validate and render the destination before making it canonical.
Do not promote caches or routine coordination logs. Refuse a destination that
would shadow another goal with the same ID or that cannot retain critical
evidence. Leave the workspace source intact until the destination is verified.
Migrate an unversioned record
A README-only goal has no claimed schema version. Migration is intentionally
conservative and is always a non-destructive import. Supply a distinct legacy
source directory and destination goals root; the command never converts the
source in place.
- keep every source byte and source directory entry unchanged;
- derive the stable goal ID from the source directory name and publish only to
<destination-goals-root>/<goal-id>;
- reject canonical source/target equality or ancestry overlap, then lock both
paths in canonical sorted order;
- preserve the original prose as an immutable legacy snapshot;
- import only fields that are unambiguous;
- mark imported criteria unverified in the closed migration attempt and begin
a new structured lifecycle at the migration checkpoint;
- build a complete
<goal-id> directory in hidden staging under the
destination root and run the normal record validator there;
- re-read and digest the source immediately before publishing the staged
directory with one rename;
- make rerunning the same provenance and options idempotent; and
- refuse an existing target with different provenance, options, ownership, or
invalid content.
Do not pretend a prose history can be losslessly normalized. The snapshot is
evidence of what was recorded; new structured attempts begin from the migration
checkpoint.
4 -
Use this reference for checkpoint payloads, criteria changes, record semantics,
or validation errors. Ordinary resume uses the CLI’s bounded view. The files
use Kubernetes-style resource envelopes for familiar versioning and evolution;
they are portable local records, not Kubernetes objects or CRDs.
Layout
The same directory shape is used in both scopes:
<goal-id>/
goal.yaml
criteria.yaml
criteria-revisions/
<revision>.yaml
README.md
attempts/
<attempt-id>/
attempt.yaml
plan.md
result.md
evidence/
Workspace records normally live at out/<task>/goals/<goal-id>/. The
recommended direct-initialization layout for project records is
<owner-root>/goals/<goal-id>/. This is guidance rather than a CLI constraint;
direct initialization, promotion, and migration accept other safe in-workspace
roots. A goal ID is stable across promotion and must not be reused.
Resource envelope
Every canonical YAML resource uses the Kubernetes-style envelope. For example,
a newly initialized local Goal has this shape:
apiVersion: goals.alwaldend.com/v1alpha1
kind: Goal
metadata:
name: example-goal
resourceVersion: "1"
generation: 1
creationTimestamp: "2026-08-30T00:00:00Z"
annotations:
goals.alwaldend.com/local-owner-root: out/example
spec:
title: Example goal
scope: workspace
retention:
policy: ephemeral
relationships:
dependsOnGoalRefs: []
supersedesGoalRefs: []
status:
lifecycleGeneration: 1
outcome: open
execution: active
criteriaRevision: 1
observedAt: "2026-08-30T00:00:00Z"
The allowed kinds are Goal, GoalCriteria, GoalAttempt, and
GoalSessionBinding. metadata.name is a DNS-compatible stable resource
identity. Use only standard Kubernetes ObjectMeta field names. In the local
store, metadata.resourceVersion emulates an API-server optimistic-concurrency
token: carry it literally and do not perform arithmetic on it.
The local CLI advances metadata.generation for spec changes and for every
accepted relationship replacement, including a normalized no-op. Lifecycle
generation is separate because criteria replacements and outcome or execution
changes can invalidate in-flight work without changing desired relationships.
Authority and projections
goal.yaml, criteria.yaml, immutable criteria-revisions/<revision>.yaml
snapshots, each attempt.yaml, and the exact plan.md, result.md, and
evidence/*.md bytes bound by its artifact manifest are canonical. Validation
checks those Markdown sidecars against their SHA-256 digests. README.md is
generated, bounded, and replaceable. It is never canonical input; regenerate
it from the canonical record instead of editing it by hand.
Use the repository tool for canonical operations when it is available:
bazel_agent bazel run //projects/goal/cmd/goal -- <command> --help
The command owns IDs, resource validation, per-goal locking,
resource-version checks, atomic file replacement, path normalization, and
rendering. Do not emulate those mechanics with direct YAML edits. If the tool
is unavailable, keep exactly one writer and state that revision and
concurrency safety were not verified.
The local adapter keeps a deterministic, persistent per-goal lock file under
$XDG_RUNTIME_DIR/alwaldend/goal/locks/. It is outside the workspace and
cannot be committed. While held, it contains the current process PID for
diagnostics; clean release clears it, while a crash can leave a stale PID.
flock, never the PID text, is authoritative, and the tool never unlinks a
lock based on that text. The lock is not a resource or part of the portable
record format. A mutation writes a sibling temporary file, closes it, and
renames it over the destination while holding that goal’s lock. Each file
replacement is atomic; a command that changes several files does not claim a
cross-file transaction.
Goal
Goal.spec owns the requested configuration: title, workspace or project
scope, retention policy, and stable parentGoalRef, dependsOnGoalRefs, and
supersedesGoalRefs relationships. The workspace-relative owner root is
local adapter metadata in the goals.alwaldend.com/local-owner-root
annotation. The filesystem backend requires it, but portable goal-state
digests exclude it.
Each local goal reference is an object containing name, rather than a path or
bare ID. Relations do not grant permission to change related goals or imply a
scheduler in v1alpha1.
Goal.status owns observed state: outcome, execution, active and accepted
attempt IDs, accepted result digest, current criteria revision, lifecycle
generation, promotion or migration provenance, and update time. Criteria
replacement and accepted outcome or execution changes advance lifecycle
generation. Starting, updating, or closing an attempt without such a transition
changes goal state but does not advance lifecycle generation.
Criteria
GoalCriteria.spec identifies its goal by a {name: ...} reference and a
revision. Each item has a stable criterion ID, its own revision, a required
flag, statement, and evidence method. Every accepted complete criteria
replacement advances the criteria resource version, generation, and spec
revision. An item’s revision advances only when its meaning changes. Evidence
proves exactly one item revision; an earlier pass becomes historical rather
than current.
Keep a fixed regression set in the criteria resource. Run it after every
attempt that can affect it and against the frozen final result.
Attempts
GoalAttempt.spec binds the work to a {name: ...} goal reference, goal and
lifecycle generations, criteria revision, portable criteria and goal-state
digests, optional planID, and work type. It does not store a Goal resource version: that value
is the checkpoint caller’s mutation-time compare-and-swap token, not a durable
attempt input. Attempt status records whether it is open or closed, relevant
timestamps, the SHA-256 artifact manifest, and the structured close review.
Review evidence references may name only the manifest’s plan, result, or
evidence paths. The digest-bound Markdown bytes live beside the resource.
An attempt may carry optional structured resume fields so a fresh agent can
resume an open goal without free-form archaeology:
stableDefect: the reproducible problem under investigation.
hypothesis: the candidate explanation being tested.
subject: the exact system, artifact, or reference under test.
affectedCriteria: criterion IDs this attempt exercises; unique and sorted.
regressionRefs: reviewed regression set or fixtures; unique and sorted.
priorAttemptID: an earlier attempt this one resumes or corrects.
dominantFailure: the single most useful failure signal observed.
measurableDelta: the measured difference this attempt produces.
nextAction: the deterministic next step for a resuming agent.
blocker: an external condition preventing progress, if any.
resumeCondition: what must hold before a resuming agent resumes this
attempt.
Plans
Goal.status.plans stores durable plan summaries. Each entry has a portable
planID, a bounded strategy, and a state of active, accepted,
rejected, or superseded. A rejected plan carries a bounded
rejectionReason. At most one plan is active. Create or transition a plan
with goal checkpoint --plan-id ... --plan-strategy ... or
--plan-state ... --plan-only; a new plan supersedes the previous active plan.
An attempt may bind to one of these summaries with spec.planID; the plan is
input context and the attempt review remains the acceptance evidence.
These fields are advisory input, not acceptance evidence: a closed attempt may
omit them entirely. Prose fields must be trimmed, bounded, and free of NUL;
identifiers must be portable record IDs; lists must be unique, sorted, and
bounded. The generated README projection surfaces stableDefect,
dominantFailure, nextAction, blocker, and resumeCondition for open
attempts so an agent can resume directly from the goal record.
For the registered repository goals root, use the goal command’s catalog-backed
resume view instead of scanning goal directories:
bazel_agent bazel run //projects/goal/cmd/goal -- resume \
--goals-root projects/agents/goals \
--catalog ../../../tools/agents/catalogs/goal.json
The output is a bounded GoalResumePacket decoded from the checked,
digest-verified goal catalog. It contains only open goals with a resumable
open attempt and their exact candidate paths and continuation fields.
--catalog is required and resolves relative to --goals-root; a task-specific
root needs its own generated catalog. The command validates catalog structure
and its stored digest, not freshness against owning goal records. Revalidate
the selected record before acting on its continuation fields.
Goal.status.acceptedResultDigest is the accepted attempt’s result.md
SHA-256, not automatically the identity of an external deliverable. Record an
external subject’s exact identity in the result or evidence when acceptance
depends on it.
Allowed work types are investigation, candidate, change, integration,
validation, and decision. Not every work unit has a file
candidate or hypothesis. Stateful work should identify source revision, target
and environment/config revision, plus an operation receipt.
An open attempt may receive isolated evidence. Closing it publishes its final
resource, plan, result, and evidence set. After close, treat its directory as
immutable; a correction or additional evidence is a new attempt that refers to
the old one.
Close review
checkpoint --close-attempt --review-file <path> takes plain YAML with
exactly two keys, decision and criteria; do not add resource-envelope
headers. Use the actual IDs and revisions returned by show:
decision: accept
criteria:
- criterionID: criterion-001
criterionRevision: 1
verdict: pass
evidenceRefs:
- evidence/acceptance.md
Decisions are accept, refine, or reset. Verdicts are pass, fail, or
unverified; passing or failing requires a reference to plan.md,
result.md, or an imported evidence/*.md artifact. Sort criteria by ID and
keep evidence references unique and sorted. Evidence must describe the exact
tested candidate and actual observations. To mark the goal achieved, add
--outcome achieved only when this accepting close passes every current
required criterion. Ordinary summaries do not supply these verdicts.
Bounds and paths
List, goal-detail, and README views have explicit item or byte limits and
report truncation. Graph analysis instead consumes the complete accepted
catalog or fails. Prefer useful recent entries in bounded views over failing
because history is large. Canonical and migrated Markdown files must be
bounded regular files containing valid UTF-8 and no NUL bytes. Store links
relative to the workspace or owner root, or as stable external URLs. Never
place local absolute paths, credentials, or private environment values in a
project goal.
Kubernetes compatibility boundary
The resources deliberately follow Kubernetes API conventions, but the local
files are not directly managed by a Kubernetes API server. The filesystem
adapter supplies resourceVersion, generation, and local annotations. A
future Kubernetes adapter must let the API server populate its read-only
metadata, install structural OpenAPI schemas, enable the status subresource,
and reconcile or strip local-only annotations during import and export.
Keep portable identity and desired state in the resource. Store a session’s
workspace-relative goal path only in the namespaced annotation
goals.alwaldend.com/local-goal-ref; GoalSessionBinding.spec.goalRef remains
a normal {name: ...} object. Digest-bound plan, result, and evidence sidecars
are a local storage representation; a cluster adapter must map their bytes to
cluster-accessible objects or artifact references.
This boundary lets a future CRD/controller reuse the API types. Do not claim
that a local record can be passed to kubectl apply without that adapter and
the CRDs.
When implementing that adapter, verify the current Kubernetes
custom-resource
and ObjectMeta
contracts rather than copying the local store’s metadata behavior blindly.
5 -
Sessions and concurrency
Use this reference when resuming, switching goals, delegating, or reconciling
a stale update.
Many records, one focus
A goal is identified by its stable goal ID and scope, not by a chat thread. A
GoalSessionBinding is a replaceable pointer to the current goal. Its
attachment-time generation, revision, and digest fields only detect whether
that pointer’s view has become stale. List and inspect stored goals before
attaching when identity is uncertain. Starting a new session must not create a
duplicate merely because the old thread is unavailable.
Attaching does not mutate the goal. Detaching or switching focus does not
pause, close, or delete it. Verify the goal resource version and current
attempt after every fresh attach or context recovery.
Canonical writer protocol
Use one coordinator for canonical state. For an attempt or lifecycle
checkpoint mutation within one existing goal record, the goal tool:
- acquires the selected goal’s cooperative cross-process file lock;
- rereads canonical state under the lock;
- checks the caller’s expected resource version; when publishing an existing
attempt, it also verifies its goal reference, lifecycle generation, and
digest-bound criteria snapshot;
- validates the complete prospective state and fully stages a new attempt in
a hidden sibling directory when needed;
- advances
goal.yaml as the optimistic-concurrency commit point;
- publishes the staged directory with one rename, or replaces existing
attempt files through sibling temporary-file renames;
- finalizes an immediately closed new attempt in
goal.yaml at the same
resource version; and
- writes the derived projection last and releases the lock.
The intermediate Goal for an immediately closed new attempt keeps an active
pointer until that attempt exists. An interruption before attempt publication
or Goal finalization therefore fails validation rather than silently dropping
the attempt. After the commit point, an error returns the committed Goal
reference and names its advanced resource version. Validate before resuming;
never retry with the caller’s stale token.
A checkpoint --criteria-file update uses the same goal lock and expected
resource version. It installs the immutable criteria snapshot, replaces the
current criteria, advances goal.yaml, and writes the projection last.
Atomic rename prevents torn files; it does not prevent lost updates by itself.
The lock and expected resource version are both required for this
compare-and-swap path. A process that ignores the lock is outside this
cooperative trust boundary.
The lock is keyed by the canonical goal path under
$XDG_RUNTIME_DIR/alwaldend/goal/locks/, so it is never tracked and work on
one goal does not block an unrelated sibling goal. If a process exits between
two file renames, validate the record before resuming instead of inferring
state from temporary files or generated Markdown.
The lock file contains the current holder PID for diagnostics. Clean release
truncates the PID while still holding flock; a crash releases the kernel
lock automatically and leaves the PID for diagnosis. The next holder
overwrites a stale PID. flock, not PID existence or process liveness, is the
authority. Never unlink and recreate a lock path based on PID metadata while
other processes may be waiting on its existing inode.
The key uses the full canonical absolute goal path rather than only its ID.
Same-named goals in parallel workspaces are independent; symlink aliases of
one path share a lock.
Other commands use locks according to the paths they coordinate. Promotion and
migration acquire distinct source and destination locks in canonical-path
order. Attachment reads the goal under its goal lock, then writes the session
binding under a separate path lock.
This is cooperative same-user coordination, not a hostile-process sandbox.
The runtime lock inode is checked without following a final symlink, but the
workspace and goal pathnames must remain stable for the duration of a command.
A process with the same user identity that races workspace renames or symlink
replacement is outside the tool’s trust boundary.
When an error reports goal publication is incomplete, run goal doctor on
the goal directory to classify the pending intent, then goal recover to
replay or discard it. Doctor states: stable,
committed-projection-stale (a README-only refresh), discardable-intent
(nothing canonical changed; recover removes it), staged-intent or
partial-intent (recover replays the after-images), and conflict (refused).
Never retry a mutation over a pending intent and never delete the intent or
staging directory by hand.
On a stale update, retain the isolated attempt output, reread the new canonical
state, and decide whether to rebase the evidence into a new attempt, revise the
plan, or discard publication. Never retry blindly with a newly read version.
Workers
Workers receive immutable inputs: goal ID, resource version, lifecycle
generation, criteria revision, attempt ID, bounded task, and an isolated
scratch/output location. They do not write into the canonical goal directory.
The coordinator reviews their output and imports selected Markdown evidence
through the canonical checkpoint.
For a long-running goal, the coordinator explicitly lists ready independent
workstreams at start, resume, and before each attempt. If at least two can
produce useful reviewable outputs without racing canonical state, delegate
them concurrently. Remaining sequential requires a concrete reason recorded
with the attempt, such as an unresolved dependency, unsafe shared mutation, or
an integration queue that would erase the latency benefit. Recheck when an
attempt closes, stalls, changes strategy, or exposes another independent
workstream.
For a monolithic artifact with no stable mergeable interfaces, give each
worker an isolated copy of the same exact frozen baseline instead of splitting
the canonical artifact; use an approved baseline when the task requires
approval. Bound each copy to one falsifiable change and enough whole-result
context to expose integration regressions. The coordinator may promote a
candidate only when the task already authorizes the mutation and the task’s
acceptance and approval policy permits it; workers never merge competing
copies directly. Copy only data permitted for that worker boundary and
minimize unrelated context.
Before publication, recheck that the goal remains open, the execution state
permits publication, and all versions still match. This prevents a late worker
from publishing after pause, completion, abandonment, or supersession.
Parallelize only independent work with disjoint output locations. Allocate
workers and compute to critical-path uncertainty, and cap work in progress at
the coordinator’s review and integration capacity. Idle slots are not a
failure when no additional useful workstream is ready. Suitable work includes
bounded candidate variants, research, disjoint modules, blind review,
verification, and artifact preparation. Bound fanout and depth; do not split
trivial operations merely to fill slots, and require a separate benefit for
recursive delegation. Stop workers whose output can no longer affect the next
decision or required acceptance and delivery evidence.
Registered goal discovery
For registered repository goals, use the explicit checked catalog:
bazel_agent bazel run //projects/goal/cmd/goal -- resume \
--goals-root projects/agents/goals \
--catalog ../../../tools/agents/catalogs/goal.json
--catalog is required and resolves relative to --goals-root. The bounded
view selects open goals with resumable attempts and verifies the catalog’s
stored digest, not freshness against current records. Use show --goal-dir
for a selected local record or workspace goal before continuing its next
action.
6 -
Persist resumable research or implementation work with local checkpoints and evidence. Use when work benefits from state that survives interruption; skip simple tasks and one-response questions.
Goal
Keep the objective, acceptance check, current candidate, evidence, and next
action inspectable. The CLI owns record identity, revisions, digests, locking,
and interrupted-write recovery. Use it instead of editing canonical YAML or
generated README projections.
Ordinary tasks
Use an ignored workspace goal under out/<task>/goals/<goal-id>/. A project
goal under <owner-root>/goals/<goal-id>/ is appropriate only when the user
requests, or the project establishes, maintained history. Length alone does
not justify committed records. A question grants no new mutation authority.
Select an existing goal by stable ID and path before initializing another.
When identity is uncertain, use list --goals-root <root>. Initialize once:
bazel_agent bazel run //projects/goal/cmd/goal -- init \
--goals-root "out/<task>/goals" \
--title "Requested outcome" --criterion "Observable acceptance check"
For routine progress, supply a short summary, the exact candidate identity,
and the next action. Mark missing checks and inferred criteria honestly:
bazel_agent bazel run //projects/goal/cmd/goal -- checkpoint \
--goal-dir "<record>" --expected-resource-version "<observed-version>" \
--subject "<exact candidate>" \
--summary "<work performed, evidence, and checks still needed>" \
--next-action "<next concrete step>"
bazel_agent bazel run //projects/goal/cmd/goal -- show --goal-dir "<record>"
--summary accepts up to 8192 bytes of inline Markdown and creates ordinary
plan/result artifacts internally. Optional --evidence <file.md> imports
immutable evidence. Later checkpoints replace the open attempt’s result and
update its candidate and next action. The initial plan and imported evidence
remain intact. Routine progress needs no separate plan ID, payload file,
process audit, or new attempt.
show returns objective, criteria, current resource version, and active
attempt progress with a bounded result preview, source paths, and digests.
Inspect the referenced result when truncated. Candidate and prose fields are
caller declarations, not live observations or proof of acceptance. Revalidate
only mutable inputs required by the recorded next action after interruption.
Carry the literal observed --expected-resource-version into each write.
If stale, reread and reconcile the intervening changes; never blindly replace
it with the latest version. Keep one coordinator for canonical goal writes.
A changed candidate needs a new summary or result; old evidence applies only
to the candidate it tested. Neither a checkpoint nor a successful command
establishes acceptance.
Finish or continue
Keep outcome (open, achieved, abandoned, superseded) separate from
execution (active, paused, waiting, blocked). Questions and scheduling
interruptions do not silently pause a goal or expand its authority. Honor
explicit pause or cancellation and resume authorized work after incidental
interruptions. Difficulty alone is not a blocker.
At acceptance, verify the exact result against every required criterion and
applicable regression. Close the attempt with checkpoint --close-attempt --review-file <review.yaml> and the final result/evidence; use --outcome achieved only when all required criteria pass. The review records criterion
IDs, revisions, verdicts, and evidence references. Read the
close review format when preparing it.
Closed attempts are immutable; corrections use a new attempt.
Checkpoint locally when work needs a reliable continuation point. A
conversational yield does not require committing, pushing, or running the
full delivery workflow. Use repo-delivery at a meaningful review-ready
milestone or final implementation handoff, and for an explicitly requested
remote backup. Deliver all legitimate source changes before final handoff;
keep scratch and workspace records ignored. Report a blocked or incomplete
publication honestly and distinguish a local checkpoint from a verified
remote copy.
Detailed and conditional workflows
For maintained project goals, repeated failures, or complex coordination,
read lifecycle and evidence for bounded
plans, attempt histories, review, and strategy changes. After the same defect
survives two attempts, change the approach and preserve its failure evidence.
Process reviews support that work; they are not routine checkpoint fields.
- For checkpoint payloads, criteria changes, or validation errors, read
record format.
- For registered goals, catalog-backed
resume, session switching, or worker
coordination, read
sessions and concurrency.
- For related goals and dependency-aware dispatch, read
graph organization.
- For promotion, imports, or public evidence retention, read
promotion and migration.
7 - Goal architecture
Local storage, locking, formats, and graph boundaries
Goal architecture
The goal tool is a local CLI for one coordinator working on a goal at a time.
It edits ordinary files inside the workspace. It does not require a service,
daemon, database, controller, or network connection.
The tool release is 0.0.1. The independent resource-schema version is
goals.alwaldend.com/v1alpha1; v1alpha1 describes schema stability and does
not imply a stable tool release.
Goal pursuit loop
The pursuit loop belongs to the goal skill and its coordinator. The CLI does
not schedule or perform the work; it validates and checkpoints the state
transitions chosen by the coordinator.

The editable Mermaid source is goal-loop.mmd.
System flow

The editable Mermaid source is goal-tool.mmd.
Files
Structured resources use Kubernetes-style YAML envelopes. Attempt plans,
results, and evidence are canonical, digest-bound Markdown artifacts;
README.md is a generated Markdown projection. A publication intent is
backend recovery metadata written before each multi-file mutation. A catalog
has this shape:
goals/
<goal-id>/
goal.yaml
criteria.yaml
criteria-revisions/<revision>.yaml
README.md
.goal-publication.yaml
.goal-publication-stage/
attempts/<attempt-id>/
attempt.yaml
plan.md
result.md
evidence/*.md
Lock files live under $XDG_RUNTIME_DIR/alwaldend/goal/locks/, outside the
workspace. They are local adapter state, not Kubernetes resources, and can
never be added to version control.
Update model
The orchestrator assigns one coordinator to a goal. The CLI independently
prevents overlapping cooperating processes. An attempt or lifecycle mutation
through checkpoint, a criteria update, or a relationship update follows this
path:
- Resolve the canonical goal path, hash it into a lock path under
$XDG_RUNTIME_DIR/alwaldend/goal/locks/, and take an exclusive flock for
that goal.
- Refuse to run while a publication intent is pending; read and validate the
current resources under the lock.
- Reject a stale expected
resourceVersion.
- Build and validate the proposed resource values in memory.
- Stage the exact after-images under
.goal-publication-stage/ and install a
.goal-publication.yaml intent recording each path and its before/after
digest. The new-attempt directory is staged as one directory tree.
- Publish
goal.yaml with the advanced resource version as the optimistic-
concurrency commit point.
- Publish the staged attempt directory with one rename when a new attempt
exists, or replace existing attempt files through sibling temporary-file
renames.
- For a newly created attempt closed by the same checkpoint, finalize
goal.yaml at the same resource version.
- Write the replaceable
README.md projection last, remove the intent and
staging directory, and release the lock.
The first goal write for an immediately closed new attempt deliberately keeps
an active-attempt pointer until the attempt directory exists. The intent
remains on disk until the projection writes; after the commit point, an error
returns the committed Goal reference, names its advanced resource version, and
leaves the intent pending for recovery. A failure before the first goal.yaml
rename discards the intent and preserves the exact prior record.
goal doctor is a read-only classification command. With a pending intent it
reports the per-target state: stable, discardable-intent, staged-intent,
partial-intent, or conflict. Without one it reports stable, a
committed-projection-stale README-only issue, or a validation failure.
goal recover replays every remaining after-image for a staged or partial
intent, refuses a conflict, or discards a discardable intent to restore the
prior record. Normal mutations fail closed with a
goal publication is incomplete error while an intent is pending, so the
coordinator must run goal doctor then goal recover before continuing.
Reads and mutations use the same exclusive per-goal lock, matching the
single-coordinator ownership model and leaving one unambiguous holder PID.
Different canonical goal paths have different lock files and therefore do not
block one another. Lock files are never deleted as part of normal operation;
deleting a lock pathname while a process holds its inode would allow a second,
unrelated lock to be created.
The holder writes its PID into the lock file after acquiring flock for
diagnostics. The kernel lock, not PID-file existence, remains authoritative;
a clean release truncates the PID while still holding flock, while a crash
releases the kernel lock and leaves the last PID for diagnosis. The next
holder overwrites that stale PID. A PID is never authority to unlink or
recreate the persistent lock path because another waiter may already have its
inode open.
The operating system may clear XDG_RUNTIME_DIR after all processes have
stopped; no canonical state lives there. The command fails clearly when a
usable XDG_RUNTIME_DIR is unavailable.
The key includes the full canonical absolute path, not only the goal ID. Two
parallel workspaces can therefore operate on same-named goals independently,
while two symlink spellings of the same goal still coordinate through one
lock.
Legacy migration is a non-destructive two-path operation. It rejects equal or
overlapping canonical source and target paths, acquires their locks in sorted
order, and never writes into the source. The command builds a complete
<goal-id> below a hidden staging directory in the destination goals root,
runs the normal record validator there, rechecks that the target is absent,
and re-digests the source. It then publishes the staged goal with one directory
rename. A matching existing target is an idempotent result; any other existing
target is refused.
Promotion likewise acquires the distinct source and destination goal locks in
canonical-path order before it validates and publishes the destination.
The runtime directory is private to the current user. Each lock path is opened
without following a final symlink, then checked to be a regular, same-user,
single-link inode before use. The workspace side remains a cooperative
pathname boundary: the workspace and goal path must remain stable during a
command. The CLI rejects path escapes and observed symlinks, but it is not a
sandbox against another process running as the same user that races pathname
replacement.
Atomic rename prevents a reader from seeing a partially written file. A
command that replaces multiple files publishes them in a defined order; if it
is interrupted between renames, the pending publication intent records exactly
which after-images remain. Canonical state consists of validated YAML
resources and the exact plan.md, result.md, and evidence/*.md bytes whose
SHA-256 digests are stored in each attempt.yaml. Recognized temporary-file
residue is removed before validation; the generated README.md projection is
replaceable and non-canonical, and .goal-publication.yaml plus
.goal-publication-stage/ are transitive recovery state that must never be
committed to version control.
Graph model
Goal relationships are typed references in Goal.spec: parent, dependency,
and supersession. Catalog graph analysis is a pure, deterministic projection
of fully validated goal records, including their digest-bound Markdown
artifacts. Each edge kind is checked separately for cycles; missing references
remain unknown instead of being invented or silently accepted as resolved.
set-relationships replaces the dependency and supersession lists completely;
omitting the parent flag preserves the parent, while --clear-parent removes
it. Every accepted request advances metadata.generation and
metadata.resourceVersion, even when the requested normalized relationships
equal the current values. The command fully validates the target record under
its goal lock and rejects relationship changes while an attempt is active,
because attempts are bound to the goal generation and portable state digest.
It writes the authoritative goal.yaml first and refreshes the replaceable
README projection last.
Relationship updates reject a newly introduced cycle or an expansion of an
existing cycle for the affected edge kind. Existing unrelated cycles do not
block a repair, and shrinking the target’s existing cycle is allowed.
Cycle prevention is deliberately snapshot-scoped. The command assembles a
catalog view by reading each member under that member’s per-goal lock, then
validates and writes the target under its lock; it does not take a catalog-wide
lock. Two coordinators that concurrently change different goals can therefore
each validate before the other write and jointly create a cycle. This is the
accepted consequence of independent per-goal locking and the single-
coordinator ownership model, not a serializable graph transaction. After the
writers settle, a subsequent graph call reports the cycle deterministically
so a coordinator can revise one edge.
Task scheduling, live agent topology, messages, and runtime state remain
responsibilities of the agent harness. The local tool stores durable goal
relationships and evidence provenance as ordinary resources.
Kubernetes boundary
The YAML resources follow Kubernetes API conventions so a future adapter can
map them to CRDs. Local files are not directly applicable cluster objects.
Cluster use would still require structural CRDs, API-server-owned metadata,
status-subresource behavior, and a controller or client that maps Markdown
artifacts and local annotations.
That adapter would replace the filesystem lock and rename mechanism with
API-server concurrency. It must not carry the local lock files or emulate the
filesystem backend inside Kubernetes.
8 - Goal command
CLI for versioned local goal records
goal is the command surface for the repository goal skill. Portable resource
types and domain validation live in api/v1alpha1; deterministic local
persistence lives in internal/fsstore. The command keeps authoritative
Kubernetes-inspired YAML envelopes and digest-bound attempt Markdown separate
from the bounded generated README.md projection, uses the same record format
for workspace and project goals, and stores session focus separately under
ignored workspace scratch.
The record layout is:
goals/<goal-id>/
goal.yaml
criteria.yaml
criteria-revisions/<revision>.yaml
README.md
attempts/<attempt-id>/
attempt.yaml
plan.md
result.md
evidence/
<owner-root>/goals/<goal-id>/ is the preferred project placement, not a CLI
constraint. Direct initialization, promotion, and migration also accept other
safe in-workspace goals roots while recording the explicit owner root.
The YAML objects use apiVersion: goals.alwaldend.com/v1alpha1 and the kinds
Goal, GoalCriteria, GoalAttempt, and GoalSessionBinding. Their
envelopes validate Kubernetes-compatible qualified metadata keys and label
values and use object-shaped local goal references. The shared API treats
resourceVersion as opaque. The filesystem backend allocates canonical local
numeric versions and requires complete persisted metadata. The files are not
raw kubectl input, and this project supplies neither CRDs nor a controller.
Run it through Bazel:
bazel_agent bazel run //projects/goal/cmd/goal -- init \
--goals-root out/example/goals \
--goal-id verify-the-release \
--title "Verify the release" \
--criterion "All affected tests pass"
Use a task-specific binding directory when changing session focus:
bazel_agent bazel run //projects/goal/cmd/goal -- attach \
--session-root out/example/goal-sessions \
--session-id current \
--goal-dir out/example/goals/verify-the-release
GoalSessionBinding.spec.goalRef contains the stable object name. The
namespaced annotation goals.alwaldend.com/local-goal-ref contains the
workspace-relative storage path. Session bindings never create a repository-
global catalog.
Goal.metadata.annotations[goals.alwaldend.com/local-owner-root] contains the
workspace-relative owner used only by the local adapter. It is deliberately
absent from portable Goal.spec and portable digests.
Commands are init, list, resume, show, attach, checkpoint,
learning-proposal, graph, set-relationships, validate, promote,
render, and migrate.
set-relationships replaces the complete dependency and supersession lists;
the parent is preserved unless explicitly set or cleared. It requires no
active attempt, advances Goal generation and resource version on every accepted
request, and refreshes the bounded README projection. Its cycle check is a
per-goal-locked catalog snapshot rather than a catalog-wide transaction, so a
graph call after concurrent writes settle is authoritative.
resume requires --catalog, resolved relative to --goals-root, and prints
a bounded GoalResumePacket containing only open goals with a resumable
attempt. The packet carries candidate paths and structured continuation
fields; it never mutates records or opens goal plans, results, or evidence.
The strict decoder validates catalog structure and its stored digest, not
freshness against current goal records. Use show --goal-dir for current
local state.
learning-proposal validates one Phase 5 proposal. Repeated friction references
are required, but the command never promotes the proposal or edits source; the
owning project adopts it through ordinary review and delivery.
checkpoint starts or publishes an attempt, changes execution/outcome state,
or applies a complete desired criteria-items file with --criteria-file.
For ordinary work, initialize the objective and acceptance once with init --title ... --criterion ..., then use an inline checkpoint:
bazel_agent bazel run //projects/goal/cmd/goal -- checkpoint \
--goal-dir out/example/goals/fix-parser \
--expected-resource-version 1 \
--subject "candidate digest or source revision" \
--summary "Parser repaired; focused checks remain unverified." \
--next-action "Run the parser acceptance check."
bazel_agent bazel run //projects/goal/cmd/goal -- show \
--goal-dir out/example/goals/fix-parser
--summary is nonblank UTF-8 Markdown, limited to 8192 bytes, and requires
--subject and --next-action. It cannot combine with --plan-file or
--result-file. The store generates the initial plan from the objective,
criteria, and next action, then writes progress into the existing canonical
result.md. Later checkpoints reuse the open attempt, preserve its initial
plan and imported evidence, and update the declared subject and next action.
Changing an existing subject requires a replacement summary or result;
retained evidence applies only to the candidate identified in that evidence.
A next-action-only checkpoint is also supported.
show includes activeAttempt with current continuation fields, an 8192-byte
UTF-8 result preview, complete result byte count/digest, evidence digests,
observation time, and source paths relative to the goal directory. Its
resultTruncated field indicates when the canonical result needs a separate
read. Subject and progress are caller declarations, not live Git observations
or criterion verdicts. Initialization, summary checkpoints, and bounded views
use the same store and recovery protocol as detailed attempts; there is no
separate compact record format. Closing still requires an explicit
--close-attempt --review-file with criterion verdicts and evidence; a
summary does not imply acceptance.
Detailed attempts can provide separate plan, result, and review payloads.
Plans are durable summaries in Goal.status.plans. Create one with
--plan-id ... --plan-strategy ... --plan-only; transition the active plan
with --plan-id ... --plan-state accepted|rejected|superseded --plan-only,
adding --plan-rejection-reason for a rejection. A newly created active plan
supersedes the prior active plan. Attempts may bind to a plan with
--plan-id when starting or by using the active plan of an existing attempt.
Criteria updates require paused execution and use ordinary atomic file
replacement. Immutable criteria snapshots retain the exact canonical criteria
revision for historical attempts; portable, domain-separated criteria and
goal-state digests avoid binding durable records to local resource-version
tokens. Closing an attempt requires --review-file with an accept, refine,
or reset decision and per-criterion verdicts linked to frozen plan, result,
or evidence artifacts.
The review file is plain YAML with exactly two keys. Criteria entries must be
sorted by criterionID, and evidenceRefs must be unique, sorted, and name
only frozen artifacts (plan.md, result.md, or files under evidence/).
Verdicts are pass, fail, or unverified; a non-unverified verdict
requires at least one evidence reference. Do not add apiVersion or kind
headers:
decision: accept
criteria:
- criterionID: friction-baseline
criterionRevision: 1
verdict: pass
evidenceRefs:
- evidence/friction-baseline.md
- criterionID: optimization-pipeline
criterionRevision: 1
verdict: pass
evidenceRefs:
- evidence/optimization-pipeline.md
An achieved outcome must close an accepting attempt whose exact passes cover
every current required criterion. Structured verdicts are kept in
attempt.yaml; richer narrative stays in result.md.
For attempt and lifecycle checkpoints, a new attempt is fully staged before
goal.yaml advances its resource version. That goal write is the optimistic-
concurrency commit point; canonical attempt content follows, and README.md
is last. An immediately closed new attempt uses an intermediate active pointer
until its directory is published and the Goal is finalized at the same
resource version, so interruption at either gap fails validation. The store
returns the committed Goal reference after any post-commit failure, and the CLI
error identifies that resource version.
Promotion also requires a paused workspace goal and preserves the goal name,
portable input bindings, immutable criteria history, and content-digest
provenance. It rejects known absolute workspace/file links in promoted attempt
artifacts; callers remain responsible for semantic privacy and credential
review.
Migration is a non-destructive import, not an in-place conversion. The legacy
source remains unchanged, while a complete validated record is published at
<destination-goals-root>/<goal-id> with one final directory rename. Repeating
the same import is idempotent only while source provenance and import options
match the existing target.
bazel_agent bazel run //projects/goal/cmd/goal -- migrate \
--source-goal-dir out/example/legacy/verify-the-release \
--destination-goals-root out/example/imported/goals
Mutations to an existing Goal’s canonical state take an exact expected local
resource version. Cooperating processes serialize them with an advisory lock
keyed to the canonical goal path under
$XDG_RUNTIME_DIR/alwaldend/goal/locks/, reread the manifest while holding the
lock, and replace individual files by renaming sibling temporary files. Lock
files are outside the workspace and cannot enter version control. Different
goals do not share a lock. Local owner and session-link annotations are
workspace-relative normalized paths; absolute host paths are never written to
manifests or command output.
Each individual file replacement is atomic; commands that update several files
do not claim cross-file transaction semantics. README.md is a derived
projection and is written last. Plan, result, and evidence Markdown is instead
canonical through the SHA-256 artifact manifest in attempt.yaml. Direct file
writers are outside this cooperative trust boundary, and digest validation
makes later edits detectable. Execution and outcome transitions advance
status.lifecycleGeneration, invalidating in-flight attempt input bindings.
This experimental v1alpha1 format is a local file protocol. One coordinator
should own a goal record. Delegated workers write only to isolated scratch;
the coordinator imports selected artifacts through checkpoint.
9 - Goal evaluations
Goal evaluations
This suite records the semantic contract for persistent, evidence-backed goal
pursuit. The required offline Bazel target validates the Promptfoo
configuration, referenced cases, and staged skill without making a model call.
The cases cover research routing, workspace-versus-project ownership, explicit
session focus, stale-update reconciliation, honest acceptance,
result-prioritized modular work, isolated candidate promotion, and
critical-path delegation, compact local checkpoints, meaningful publication
milestones, and explicitly requested remote backup. Concurrency cases require active delegation when a
long-running goal exposes multiple independently reviewable workstreams,
require a recorded reason for sequential execution, and reject fanout whose
only purpose is occupying available slots. The interruption case distinguishes
turn priority from lifecycle state: questions and additional tasks do not
silently stop an already-authorized active goal or expand its authority.
A live target is omitted because representative behavior spans multiple turns
and requires filesystem tools, safe writable fixtures, and fresh-session
resume. A tool-free single response cannot verify those longitudinal
postconditions. Promptfoo validation therefore proves only that these assets
load. Deterministic store behavior is covered by
//projects/goal/internal/fsstore:go_test; a future
model eval still needs an isolated multi-turn workspace fixture.
The static storage case covers the ordinary one-goal boundary: one path-keyed
goal lock plus atomic per-file rename. Promotion and migration use a separate
two-path protocol that acquires distinct source and destination locks in
canonical-path order.