Skip to content

feat: first-class goals — review-gated in-flight objectives #427

Description

@plind-junior

vouch models claims, pages, entities, and relations — everything the project knows. it has no representation of what the project is trying to do right now. ProposalKind is {claim, page, entity, relation} and ClaimType has no goal-shaped member. so an agent re-orienting after a compaction can recall facts and decisions but cannot recall in-flight intent — "we're mid-migration to typed config", "the release is blocked on the audit-race fix" — except as prose buried in a session summary. there's no queryable, status-carrying objective.

this adds goal as a first-class, review-gated artifact: proposed, approved, and status-tracked through exactly the same machinery as every other write.

proposed surface

  • a Goal model in models.py with a GoalStatus enum (open, done, abandoned, blocked) and optional links to the claims/entities it concerns.
  • GOAL added to ProposalKind; kb.propose_goal routes through proposals.propose_* like any other proposal.
  • kb.list_goals (read; defaults to open, oldest-first) and status transitions through lifecycle.*, each landing an event in audit.log.jsonl and a summary in decided/.
  • re-surfaced by the SessionStart digest and by vouch digest (feat: vouch digest — read-only briefing of pending proposals, recent decisions, and stale items #324) so a returning operator or a fresh agent session sees open objectives first.

four registration sites for each new method (server.py, jsonl_server.py, capabilities.py, cli.py) plus tests/test_goals.py. storage.py stays pure i/o — the goal is another artifact kind it persists; the lifecycle logic lives in lifecycle.py.

review gate & scope

the load-bearing rule holds: a goal is proposed and must be approved before it exists as an approved artifact, and every status transition goes through lifecycle.* + the audit log — there is no direct-write path. goals are reviewed knowledge, not a scratchpad; if a PR adds a way to set goal status outside lifecycle.*, it's wrong. local-first, yaml on disk, diffable in PRs like every other artifact.

acceptance criteria

  • Goal + GoalStatus added to models.py; empty/whitespace title rejected at the model layer (per the validation gap: Claim.text, Page.title, Entity.name accept empty/whitespace at the model layer; only propose_* enforces non-empty #155 validation posture).
  • kb.propose_goal creates a pending proposal via proposals.propose_*; no direct write.
  • status transitions go through lifecycle.*, append to audit.log.jsonl, and write a decided/ record.
  • kb.list_goals reads open goals oldest-first, viewer-scoped.
  • all new kb.* methods registered in the four sites; test_capabilities passes.
  • open goals appear in the SessionStart digest / vouch digest.
  • tests/test_goals.py covers propose→approve→transition and the no-bypass invariant.
  • make check green.

related: #324 (vouch digest surfaces open goals), #305 (background maintenance could propose goal-staleness), north-star (review gate).

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions