Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
394 changes: 179 additions & 215 deletions .agent-runtime/goals/first-v0-release.json

Large diffs are not rendered by default.

30 changes: 27 additions & 3 deletions .agent-runtime/goals/held-path-identity.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"schema_version": "v1alpha1",
"revision": 11,
"revision": 12,
"goal": {
"id": "held-path-identity",
"intent": "The JSONL sink writes to the file it read, and every remaining check-then-use gap is stated rather than implied",
Expand Down Expand Up @@ -47,9 +47,33 @@
"Redesign the sandbox model",
"Defend against a compromised host"
],
"state": "active",
"state": "completed",
"current_phase": "closure",
"receipts": {
"closure": {
"phase": "closure",
"summary": "Merged as PR #57 with all four required checks green.",
"evidence": [
{
"type": "link",
"reference": "https://github.com/NDDev-it-com/agent-runtime/pull/57",
"result": "Merged to main."
}
],
"recorded_at": "2026-08-14T23:54:44.325258889Z",
"closure": {
"achieved_outcome": "The JSONL sink validates and extends one object, and the remaining check-then-use gaps are stated with the trigger that would make them real.",
"cleanup": "Task branch deleted locally and on origin; the dead scanJSONL wrapper removed.",
"remaining": [],
"next_work": [
{
"type": "issue",
"reference": "self verified journals",
"result": "Give the Goal contract the executable checker every other contract in this repository already has, and repair the tracked journal that the product rejects."
}
]
}
},
"completeness_omission_audit": {
"phase": "completeness_omission_audit",
"summary": "Checked the other direction as well: a destination whose name resolves elsewhere, such as a symlink, is refused rather than followed, because a caller configured a destination and not whatever currently answers to it.",
Expand Down Expand Up @@ -136,6 +160,6 @@
}
},
"created_at": "2026-08-14T23:50:11.022035863Z",
"updated_at": "2026-08-14T23:50:11.311924048Z"
"updated_at": "2026-08-14T23:54:44.325258889Z"
}
}
123 changes: 123 additions & 0 deletions .agent-runtime/goals/self-verified-journals.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
{
"schema_version": "v1alpha1",
"revision": 8,
"goal": {
"id": "self-verified-journals",
"intent": "This repository's tracked Goal journals are proven valid by an executable checker",
"acceptance": [
{
"id": "journal-checker",
"acceptance": "A required CI checker validates every tracked journal against the Go contract and the published schema",
"status": "pending"
},
{
"id": "journals-valid",
"acceptance": "goal status succeeds on every tracked journal",
"status": "pending"
},
{
"id": "first-release-true",
"acceptance": "The first-release journal states the published outcome and closes on its evidence",
"status": "pending"
},
{
"id": "no-shipped-invalid",
"acceptance": "A built source archive contains no artifact the product rejects",
"status": "pending"
}
],
"non_goals": [
"Widen the phase vocabulary to accommodate a malformed journal"
],
"state": "active",
"current_phase": "closure",
"receipts": {
"completeness_omission_audit": {
"phase": "completeness_omission_audit",
"summary": "Registered the checker in the CI contract as well as the workflow, so it cannot be dropped from ci.yml while the contract stays green -- the same failure class the workflow model was built to close.",
"evidence": [
{
"type": "file",
"reference": "internal/cicontract/contract.go",
"result": "The workflow must invoke the goal journal verifier exactly once, counted over executed run scripts."
}
],
"recorded_at": "2026-08-15T00:02:27.460260274Z"
},
"execute": {
"phase": "execute",
"summary": "Added journalverify and its command, wired it into ci.yml, the CI contract and the GDS anchor, and migrated first-v0-release.json by replaying its history through goal.New and Advance.",
"evidence": [
{
"type": "command",
"reference": "go run ./cmd/check-goal-journals",
"result": "goal journals valid: 9 tracked, accepted by the Goal contract and the published schema."
}
],
"recorded_at": "2026-08-15T00:02:27.376942287Z"
},
"gap_plan": {
"phase": "gap_plan",
"summary": "Write the checker first so the repair is proven rather than asserted, then fold each recovery cycle into the phase receipt it already named, replaying through the product's own API so the result is legitimate by construction.",
"evidence": [
{
"type": "file",
"reference": "internal/journalverify; cmd/check-goal-journals",
"result": "The Goal contract was the only contract in this repository without an executable checker over its tracked artifacts, which is why a schema change could invalidate a shipped file unnoticed."
}
],
"recorded_at": "2026-08-15T00:02:27.3515585Z"
},
"orient": {
"phase": "orient",
"summary": "The module shipped a Goal journal its own CLI refuses to load. Twenty-two of the twenty-four receipt keys in first-v0-release.json were invented recovery-cycle labels rather than phases, so goal status failed with invalid_goal; the file is tracked, so it travelled inside the published v0.1.3 source archive and its SPDX inventory. The same journal also claimed the first release had never happened.",
"evidence": [
{
"type": "command",
"reference": "agent-runtime goal status --journal .agent-runtime/goals/first-v0-release.json",
"result": "invalid_goal: invalid receipt for phase recovery_cycle_verification_orient; state active with all seven acceptance criteria pending while v0.1.3 was published and issue #9 closed."
}
],
"recorded_at": "2026-08-15T00:02:27.320657917Z"
},
"reconcile": {
"phase": "reconcile",
"summary": "The migration is lossless in substance: all twenty-four evidence records survive on the phase receipts, and every cycle summary is retained and labelled. One evidence type of 'source' was never in the contracted vocabulary and is recorded as a link.",
"evidence": [
{
"type": "file",
"reference": ".agent-runtime/goals/first-v0-release.json",
"result": "Seven phase receipts carrying 1, 6, 5, 2, 2, 3 and 5 evidence records, then a closure receipt recording both the release outcome and the migration itself."
}
],
"recorded_at": "2026-08-15T00:02:27.406493571Z"
},
"self_review": {
"phase": "self_review",
"summary": "The checker asserts the pair, not one side: Go alone would let the published schema drift, and the schema alone would skip the durable invariants the type enforces. It also refuses an empty directory and requires the file to be the canonical encoding of what it decodes to, so a journal cannot drift each time the runtime rewrites it.",
"evidence": [
{
"type": "test",
"reference": "internal/journalverify TestVerifyRejectsJournalsEitherContractRefuses; TestVerifyRefusesToPassVacuously",
"result": "Five damaged shapes are refused, including the exact one that shipped, and an empty directory fails rather than passing."
}
],
"recorded_at": "2026-08-15T00:02:27.435047951Z"
},
"verify": {
"phase": "verify",
"summary": "gofmt, go vet, staticcheck, full tests under umask 002, and all seven contract checkers including the new one.",
"evidence": [
{
"type": "test",
"reference": "local gate transcript 2026-08-15",
"result": "All green; 9 journals verified."
}
],
"recorded_at": "2026-08-15T00:02:27.487402016Z"
}
},
"created_at": "2026-08-15T00:02:27.292800011Z",
"updated_at": "2026-08-15T00:02:27.487402016Z"
}
}
3 changes: 3 additions & 0 deletions .gds/repository.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ verification:
- "go run ./cmd/check-ci-contract"
- "go run ./cmd/check-governance-contract"
- "go run ./cmd/check-release-contract"
# The Goal journal is this module's own product, and its tracked
# instances are held to the same contract a consumer would apply.
- "go run ./cmd/check-goal-journals"
full:
# govulncheck resolves the live vulnerability database and needs the
# patched security toolchain, not the compatibility one, so CI runs it in
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ jobs:
fi
- name: Verify module metadata is tidy without mutation
run: go run ./cmd/check-module-tidy
- name: Verify every tracked Goal journal against the contract and its schema
run: go run ./cmd/check-goal-journals
- run: go run ./cmd/check-ci-contract
- run: go run ./cmd/check-governance-contract
- name: Reproduce release bundle without publication
Expand Down
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,39 @@ contract.

## [Unreleased]

### Added

- `cmd/check-goal-journals` holds every tracked Goal journal to the Go contract
and to the published schema, and is a required CI step recorded in the GDS
anchor. Every other contract here — CI, governance, release, provenance, fuzz,
cold compile — was already proven by an executable checker; the Goal contract,
which is the product rather than a property of the repository, was the one
without. It refuses to pass on an empty directory, because a gate that
succeeds by finding nothing to inspect cannot be told from one that inspected
everything.

### Fixed

- `.agent-runtime/goals/first-v0-release.json` is a journal this module accepts.
It recorded each recovery cycle under an invented receipt key, which the Goal
contract does not permit, so `agent-runtime goal status` refused to load it —
and because it is tracked, it shipped inside the published source archive and
its SPDX inventory. The module distributed an artifact its own CLI rejects.
Tightening the schema so receipt keys must be phases is what made an existing
file invalid, and nothing compared the two.

Every recovery cycle's summary and evidence is preserved, folded into the
phase receipt each cycle already named in its own `phase` field, and the
replay went through the product's own API so the result is legitimate by
construction rather than by hand. One evidence record carried a `source` type
that was never in the contracted vocabulary and is recorded as a link. The
pre-migration form is in this repository's Git history.

The journal also now states what happened: the first verifiable v0 release
shipped, as `v0.1.2` and then `v0.1.3` rather than `v0.1.0`, and issue #9 is
closed. It had stood at `state: active` with all seven acceptance criteria
pending since 13 August, claiming the first release had never happened.

- A JSONL sink holds one descriptor for its lifetime. It used to open the path
to scan the existing history, close it, and open it again to append, so a
rename in between left the recovered duplicate-identity and size state
Expand Down
20 changes: 20 additions & 0 deletions cmd/check-goal-journals/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: AGPL-3.0-only

package main

import (
"fmt"
"os"

"github.com/NDDev-it-com/agent-runtime/internal/journalverify"
)

func main() {
result, err := journalverify.Verify(".", journalverify.DefaultDirectory, journalverify.SchemaPath)
if err != nil {
fmt.Fprintln(os.Stderr, "goal journal invalid:", err)
os.Exit(1)
}
fmt.Printf("goal journals valid: %d tracked under %s, accepted by the Goal contract and the published schema\n",
len(result.Journals), result.Directory)
}
7 changes: 7 additions & 0 deletions internal/cicontract/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ func VerifyWorkflow(c Contract, workflow []byte) error {
if w.CountRunOccurrences("go run ./cmd/check-fuzz") != 1 {
return errors.New("workflow must invoke the canonical fuzz verifier exactly once")
}
// The Goal contract is this module's product, so its tracked artifacts are
// held to it in CI like every other contract here. Leaving it unchecked is
// how a schema change came to invalidate a journal that ships inside the
// release archive.
if w.CountRunOccurrences("go run ./cmd/check-goal-journals") != 1 {
return errors.New("workflow must invoke the goal journal verifier exactly once")
}
return verifyReleaseReproductionCommand(test)
}

Expand Down
127 changes: 127 additions & 0 deletions internal/journalverify/verify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// SPDX-License-Identifier: AGPL-3.0-only

// Package journalverify proves the Goal journals this repository tracks are
// artifacts the product accepts.
//
// Every other contract here is proven by an executable checker under cmd/ — CI,
// governance, release, provenance, fuzz, cold compile. The Goal contract, which
// is the product rather than a property of the repository, had none, and that
// is exactly how it came to ship a journal it rejects: tightening the schema so
// receipt keys must be phases made an existing tracked file invalid, and
// nothing compared the two. The file travels inside the published source
// archive and is listed in its SPDX inventory, so the release carried an
// artifact its own CLI refuses to load.
//
// The check is deliberately double. The Go contract and the published schema
// are two statements of one vocabulary, and a journal has to satisfy both: Go
// alone would let the schema drift, and the schema alone would let the durable
// invariants the type enforces go unchecked.
package journalverify

import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"sort"
"strings"

"github.com/santhosh-tekuri/jsonschema/v6"

"github.com/NDDev-it-com/agent-runtime/goal"
)

// DefaultDirectory is where this repository keeps its tracked journals.
const DefaultDirectory = ".agent-runtime/goals"

// SchemaPath is the published description of the same vocabulary.
const SchemaPath = "schemas/goal-journal-v1alpha1.schema.json"

// Result reports what was proven, so a caller can print evidence rather than a
// bare success.
type Result struct {
Directory string
Journals []string
}

// Verify loads every journal in directory and holds it to both the Go contract
// and the published schema. An empty directory is a failure: a checker that
// passes because it found nothing to check is the kind of control this
// repository exists to remove.
func Verify(root, directory, schemaPath string) (Result, error) {
pattern := filepath.Join(root, directory, "*.json")
paths, err := filepath.Glob(pattern)
if err != nil {
return Result{}, fmt.Errorf("scan %s: %w", directory, err)
}
sort.Strings(paths)
if len(paths) == 0 {
return Result{}, fmt.Errorf("no goal journals under %s; this check would pass vacuously", directory)
}
schema, err := compileSchema(filepath.Join(root, schemaPath))
if err != nil {
return Result{}, err
}
result := Result{Directory: directory}
for _, path := range paths {
name := filepath.Base(path)
if err := verifyOne(path, schema); err != nil {
return Result{}, fmt.Errorf("%s: %w", filepath.Join(directory, name), err)
}
result.Journals = append(result.Journals, name)
}
return result, nil
}

func verifyOne(path string, schema *jsonschema.Schema) error {
data, err := os.ReadFile(path)
if err != nil {
return err
}
// The Go contract first: it is what a caller's CLI and library will apply,
// so a journal that fails here is unusable whatever the schema says.
journal, err := goal.Store{Path: path}.Load()
if err != nil {
return fmt.Errorf("the Goal contract rejects this journal: %w", err)
}
document, err := jsonschema.UnmarshalJSON(strings.NewReader(string(data)))
if err != nil {
return fmt.Errorf("not valid JSON: %w", err)
}
if err := schema.Validate(document); err != nil {
return fmt.Errorf("the published schema rejects this journal: %w", err)
}
// Re-encoding must reproduce the file. A journal that only round-trips
// approximately would drift each time the runtime rewrote it, and it ships
// inside the release archive byte for byte.
encoded, err := json.MarshalIndent(journal, "", " ")
if err != nil {
return fmt.Errorf("re-encode: %w", err)
}
if strings.TrimSpace(string(data)) != strings.TrimSpace(string(encoded)) {
return errors.New("the journal is not the canonical encoding of what it decodes to")
}
return nil
}

func compileSchema(path string) (*jsonschema.Schema, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read goal schema: %w", err)
}
document, err := jsonschema.UnmarshalJSON(strings.NewReader(string(data)))
if err != nil {
return nil, fmt.Errorf("parse goal schema: %w", err)
}
compiler := jsonschema.NewCompiler()
compiler.AssertFormat()
if err := compiler.AddResource(path, document); err != nil {
return nil, fmt.Errorf("load goal schema: %w", err)
}
schema, err := compiler.Compile(path)
if err != nil {
return nil, fmt.Errorf("compile goal schema: %w", err)
}
return schema, nil
}
Loading