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.

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.

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.

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.

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.

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.

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:

  1. work actually performed;
  2. artifacts and raw verification evidence;
  3. a verdict for every affected criterion and regression check;
  4. the dominant remaining failure;
  5. the decision to accept, refine, or reset; and
  6. 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.

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 -

Promotion and migration

Use this reference only when promoting a workspace goal or importing an unversioned record.

Promotion changes storage and retention, not goal identity. It must:

  1. require an explicit maintained-project decision and an owning project root;
  2. lock the source and destination paths in canonical order, then validate the source at an expected resource version;
  3. preserve the goal ID and closed attempt history;
  4. record source scope and promotion provenance;
  5. require repository-relative or stable external links;
  6. retain, copy, or make reproducible every acceptance-critical artifact;
  7. scan for absolute local paths, credentials, private environment details, and ignored evidence that would become broken; and
  8. 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.

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 -

Goal resource format v1alpha1

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.

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.

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.

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.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.

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.

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.

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.

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.

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.

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.

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.

Use one coordinator for canonical state. For an attempt or lifecycle checkpoint mutation within one existing goal record, the goal tool:

  1. acquires the selected goal’s cooperative cross-process file lock;
  2. rereads canonical state under the lock;
  3. 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;
  4. validates the complete prospective state and fully stages a new attempt in a hidden sibling directory when needed;
  5. advances goal.yaml as the optimistic-concurrency commit point;
  6. publishes the staged directory with one rename, or replaces existing attempt files through sibling temporary-file renames;
  7. finalizes an immediately closed new attempt in goal.yaml at the same resource version; and
  8. 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 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.

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.

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.

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.

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.

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.

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.

Goal pursuit loop

The editable Mermaid source is goal-loop.mmd.

Goal tool architecture

The editable Mermaid source is goal-tool.mmd.

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.

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:

  1. 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.
  2. Refuse to run while a publication intent is pending; read and validate the current resources under the lock.
  3. Reject a stale expected resourceVersion.
  4. Build and validate the proposed resource values in memory.
  5. 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.
  6. Publish goal.yaml with the advanced resource version as the optimistic- concurrency commit point.
  7. Publish the staged attempt directory with one rename when a new attempt exists, or replace existing attempt files through sibling temporary-file renames.
  8. For a newly created attempt closed by the same checkpoint, finalize goal.yaml at the same resource version.
  9. 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.

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.

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.