Skip to content

feat(goal): add the /goal durable objective ledger and gauntlet loop - #4

Draft
Nuropunk wants to merge 1 commit into
mainfrom
claude/brainbow-brain-data-security-be4zaa
Draft

feat(goal): add the /goal durable objective ledger and gauntlet loop#4
Nuropunk wants to merge 1 commit into
mainfrom
claude/brainbow-brain-data-security-be4zaa

Conversation

@Nuropunk

Copy link
Copy Markdown
Owner

What this adds

An objective that spans many sessions has had nowhere durable to live. The backlog tracks work items, and anything larger than a work item survived only in conversation memory - so it did not survive a context reset, a crash, or a change of harness.

/goal gives such an objective a record of its own:

  • bin/fm-goal.sh - the durable ledger and loop driver
  • .agents/skills/goal/SKILL.md - the judgement layer (when a goal is the right shape, how to design a gate, the automation handoff rules)
  • tests/fm-goal.test.sh - 12 behavior tests
  • one trigger line in AGENTS.md section 10, one row in the README skill table, one inventory entry

The design decisions worth reviewing

Completion is a verdict, not a claim. close refuses until every registered gate has a current pass, and a goal with no gates can never pass. The point is that "done" gets computed from evidence rather than asserted by whoever finished last.

Gates are content-bound. gate-add records a gate's exact bytes. Edited bytes report UNBOUND and refuse to run, and a re-registered gate must be re-proven because a result recorded against superseded bytes stops counting. This is the property the whole thing rests on: an unattended loop that can quietly rewrite its own success condition is not a verification loop. Both cases are covered by tests.

The loop is circular by design. The default stage list - ingest, verify, interpret, merge, render, evaluate, improve, archive - is a complete cycle and is configurable per goal with --stages. Reaching the last stage wraps to the first and journals a completed pass, because a goal that is not yet closed always has another pass to run.

Records are line-oriented, not JSON. goal.meta is key=value and journal.log is TSV, matching the existing state/<id>.meta convention and avoiding JSON escaping in shell. export-json is a separate command so the human-facing output stays free to change independently of the machine-readable one.

Scope boundaries

fm-goal.sh reaches no network, spawns no agent, and writes nothing outside the goal directory. It keeps the record; the caller does the work. The skill's automation section deliberately refuses to let an automated pass merge, push to a default branch, discard unlanded work, or answer an escalated decision.

Verification

Check Result
bin/fm-lint.sh on the changed files clean, ShellCheck 0.11.0 (pinned)
tests/fm-goal.test.sh 12/12 pass
bin/fm-test-run.sh --check-coverage ok, total=151 (the new test lands in portable-serial by derivation, no lane edit needed)
bin/fm-doc-audience-check.sh ok, surfaces=70, local_links=255

Two defects were found and fixed during development rather than shipped: a re-registered gate could inherit the previous version's pass (fixed by recording the gate hash in each result and requiring it to match the bytes on disk), and export-json raised a BrokenPipeError when piped into head (fixed by restoring default SIGPIPE handling). Both now have regression coverage.

Not included

No docs/ page. The script header owns the mechanics and the skill owns the judgement; a third prose surface would be the duplication the one-owner rule exists to prevent.


Generated by Claude Code

An objective that spans many sessions has had nowhere durable to live: the
backlog tracks work items, and anything larger survived only in conversation
memory. bin/fm-goal.sh gives such an objective a record of its own - objective
text, an ordered stage list, an append-only journal, and a set of executable
gates - so it survives a restart, a crash, and a change of harness.

Completion is a verdict computed from evidence rather than a claim. close
refuses until every registered gate has a current pass, and a goal with no
gates can never pass.

Gates are content-bound, which is the property an unattended loop depends on.
gate-add records a gate's exact bytes; edited bytes report UNBOUND and refuse
to run, and a re-registered gate must be re-proven because a result recorded
against superseded bytes stops counting. A loop that can quietly rewrite its
own success condition is not a verification loop.

The default stage list - ingest, verify, interpret, merge, render, evaluate,
improve, archive - is a complete cycle and is configurable per goal. Reaching
the last stage wraps and journals a completed pass.

The skill owns the judgement: when a goal is the right shape rather than a
backlog item, how to design a gate that fails closed, and the narrow rules for
handing a goal to a recurring trigger without compounding damage on top of
automation that is already running.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant