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
13 changes: 11 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
# Changelog

## 0.17.1 - 2026-09-01

- Standardized Goal Contract and Goal review language across source, release
checks, and public product surfaces without changing runtime behavior.
- Added a release-blocking product-language guard for all four Across source
trees and renamed release-train lock surfaces to Across-owned terminology.
- Preserved the released Context `v0.12.0`, Orchestrator `v0.12.2`, and
Autopilot `v0.6.0` plugin payloads.

## 0.17.0 - 2026-08-30

- Completed the GoalBoard-inspired Goal Contract loop with criterion-scoped
- Completed the independently designed Goal Contract loop with criterion-scoped
execution evidence, human result review, repair Attempts, and selective
invalidation/revalidation without importing a second Goal authority.
- Added server-authoritative Goal actions and immediate macOS state
Expand All @@ -23,7 +32,7 @@
- Added versioned Goal Contracts with immutable Goal Store revisions,
human-confirmed acceptance criteria, stale-evidence invalidation, and
host-authoritative dispatch validation.
- Added Goal Board inspection and recovery journeys for contract review,
- Added Goal review and recovery journeys for contract review,
revision changes, execution evidence, and explicit human acceptance.
- Integrated Orchestrator `v0.11.0` with crash-safe terminal transactions and
hash-valid, review-required Goal receipts; Autopilot `v0.6.0` with
Expand Down
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ supervised workflow capabilities only after they are installed and healthy.

| Component | What it adds | Current release |
| --- | --- | --- |
| Across Agents Assistant | macOS workspace, generic task entry, approvals, devices, settings, and plugin lifecycle | `v0.17.0` |
| Across Agents Assistant | macOS workspace, generic task entry, approvals, devices, settings, and plugin lifecycle | `v0.17.1` |
| Across Context | shared memory, provenance, review, forgetting, context packs, and governed Worker experience | `v0.12.0` |
| Across Orchestrator | task execution, remote Workers, quality gates, sandbox policy, and evidence receipts | `v0.12.2` |
| Across Autopilot | goal-driven workflow resolution, LoopSpec supervision, repair, and release readiness | `v0.6.0` |
Expand All @@ -74,12 +74,21 @@ than a fourth first-party managed component.
| --- | --- |
| <img src="assets/readme/product-workers.png" alt="Approved local and remote Worker devices"> | <img src="assets/readme/product-plugins.png" alt="One-click managed first-party plugins, all ready"> |

## What `v0.17.0` Includes
## What `v0.17.1` Includes

- Uses only Across-owned Goal Contract and Goal review terminology across
source, release checks, and public product surfaces.
- Adds a release-blocking product-language guard that checks all four Across
source trees during coordinated acceptance.
- Keeps the v0.17 Goal Contract behavior and released producer plugin versions
unchanged.

### Previous `v0.17.0` release

- Added versioned Goal Contracts with immutable revisions, explicit human
confirmation, stale-evidence invalidation, and host-authoritative execution
checks before any Orchestrator dispatch.
- Added Goal Board review and recovery paths that keep required acceptance
- Added Goal review and recovery paths that keep required acceptance
criteria, execution bindings, receipts, and revalidation visible without
allowing a runtime to self-approve delivery.
- Integrated Context `v0.12.0`, Orchestrator `v0.12.2`, and Autopilot `v0.6.0`
Expand Down
14 changes: 7 additions & 7 deletions across.product.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
"summary": "Across starts as a simple project workspace and grows through optional memory, durable execution, goal-driven workflows, and approved remote Worker capacity, while keeping evidence, credentials, and final approval under local user control.",
"current_releases": {
"across_agents_assistant": {
"version": "v0.17.0",
"published_at": "2026-08-30",
"tag": "v0.17.0",
"release_url": "https://github.com/fantasyce/across-agents-assistant/releases/tag/v0.17.0"
"version": "v0.17.1",
"published_at": "2026-09-01",
"tag": "v0.17.1",
"release_url": "https://github.com/fantasyce/across-agents-assistant/releases/tag/v0.17.1"
},
"across_autopilot": {
"version": "v0.6.0",
Expand All @@ -19,10 +19,10 @@
"release_url": "https://github.com/fantasyce/across-autopilot/releases/tag/v0.6.0"
},
"across_orchestrator": {
"version": "v0.11.0",
"version": "v0.12.2",
"published_at": "2026-08-30",
"tag": "v0.11.0",
"release_url": "https://github.com/fantasyce/across-orchestrator/releases/tag/v0.11.0"
"tag": "v0.12.2",
"release_url": "https://github.com/fantasyce/across-orchestrator/releases/tag/v0.12.2"
},
"across_context": {
"version": "v0.12.0",
Expand Down
2 changes: 1 addition & 1 deletion backend/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "across-agents-assistant"
version = "0.17.0"
version = "0.17.1"
description = "macOS desktop assistant with a Swift client, local Python backend, project chat, and agent orchestration"
requires-python = ">=3.10,<3.14"
dependencies = [
Expand Down
2 changes: 1 addition & 1 deletion backend/src/across_agents_assistant/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__all__ = ["__version__"]

__version__ = "0.17.0"
__version__ = "0.17.1"
2 changes: 1 addition & 1 deletion backend/tests/test_goal_contract_real_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def real_orchestrator_runtime(tmp_path_factory):
provider_root = Path(
os.environ.get(
"ACROSS_ORCHESTRATOR_PROVIDER_ROOT",
str(aaa_root.parents[1] / "goal-contract-v2" / "across-orchestrator"),
str(aaa_root.parent / "across-orchestrator"),
)
).resolve()
assert (provider_root / "pyproject.toml").is_file(), provider_root
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from __future__ import annotations

import json
import os
import subprocess
import sys
from copy import deepcopy
Expand All @@ -13,7 +12,7 @@
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / "scripts"))

from write_goalboard_release_train_lock import ( # noqa: E402
from write_goal_release_train_lock import ( # noqa: E402
ReleaseTrainError,
build_lock,
verify_lock,
Expand Down Expand Up @@ -50,7 +49,7 @@ def _candidate_fixture(tmp_path: Path) -> dict:
"orchestrator": "0.12.2",
"context": "0.12.0",
"autopilot": "0.6.0",
"aaa": "0.17.0",
"aaa": "0.17.1",
}
for component_id, version in versions.items():
repository = _git_repo(tmp_path / component_id, version)
Expand Down Expand Up @@ -93,7 +92,7 @@ def _candidate_fixture(tmp_path: Path) -> dict:
def test_lock_captures_clean_immutable_candidate_and_verifies(tmp_path):
lock = build_lock(_candidate_fixture(tmp_path))

assert lock["schema_version"] == "goalboard-release-train-lock/1.0"
assert lock["schema_version"] == "across-goal-release-train-lock/1.0"
assert set(lock["components"]) == {"orchestrator", "context", "autopilot", "aaa"}
assert all(component["dirty"] is False for component in lock["components"].values())
assert all(len(component["source_sha256"]) == 64 for component in lock["components"].values())
Expand Down Expand Up @@ -158,27 +157,24 @@ def test_lock_rejects_executable_and_app_drift(tmp_path):
verify_lock(restored)


def test_candidate_versions_are_goalboard_train_versions():
def test_candidate_versions_match_release_train():
aaa_pyproject = (ROOT / "backend/pyproject.toml").read_text(encoding="utf-8")
orchestrator_root = Path(
os.environ.get(
"ACROSS_ORCHESTRATOR_PROVIDER_ROOT",
str(ROOT.parents[1] / "goal-contract-v2/across-orchestrator"),
)
).resolve()
orchestrator_pyproject = (orchestrator_root / "pyproject.toml").read_text(encoding="utf-8")
payload_preparation = (ROOT / "scripts/prepare_managed_plugin_payloads.sh").read_text(
encoding="utf-8"
)

assert 'version = "0.17.0"' in aaa_pyproject
assert 'version = "0.12.2"' in orchestrator_pyproject
assert 'version = "0.17.1"' in aaa_pyproject
assert 'ORCHESTRATOR_VERSION="0.12.2"' in payload_preparation
assert 'ORCHESTRATOR_CANDIDATE_VERSION="0.12.2"' in payload_preparation


def test_packaged_acceptance_requires_and_records_release_train_lock():
script = (ROOT / "scripts/run_vnext_single_release_acceptance.sh").read_text(encoding="utf-8")

assert "ACROSS_GOALBOARD_RELEASE_TRAIN_CANDIDATE" in script
assert "write_goalboard_release_train_lock.py" in script
assert "ACROSS_GOAL_RELEASE_TRAIN_CANDIDATE" in script
assert "write_goal_release_train_lock.py" in script
packaged_e2e = script.index("packaged_app_cross_plugin_e2e")
lock_gate = script.index("goalboard_release_train_lock")
lock_gate = script.index("goal_release_train_lock")
assert packaged_e2e < lock_gate
assert "'$ROOT_DIR/backend/.venv/bin/python' scripts/write_goalboard_release_train_lock.py" in script
assert "python3 scripts/write_goalboard_release_train_lock.py" not in script
assert "'$ROOT_DIR/backend/.venv/bin/python' scripts/write_goal_release_train_lock.py" in script
assert "python3 scripts/write_goal_release_train_lock.py" not in script
144 changes: 144 additions & 0 deletions backend/tests/test_product_language_guard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
from __future__ import annotations

import sys
from pathlib import Path

import pytest


ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / "scripts"))

import check_product_language as product_language # noqa: E402
from check_product_language import find_language_violations, main # noqa: E402


FORBIDDEN_EXAMPLE = "goal" + "board"


@pytest.mark.parametrize(
"text",
[
FORBIDDEN_EXAMPLE,
"Goal" + " " + "Board",
"goal" + "-" + "board",
"goal" + "_" + "board",
],
)
def test_detects_prohibited_product_language_in_text(tmp_path: Path, text: str) -> None:
(tmp_path / "README.md").write_text(text, encoding="utf-8")

violations = find_language_violations([tmp_path])

assert [violation.kind for violation in violations] == ["content"]


def test_detects_prohibited_product_language_in_filename(tmp_path: Path) -> None:
path = tmp_path / f"{FORBIDDEN_EXAMPLE}-release.json"
path.write_text("safe content", encoding="utf-8")

violations = find_language_violations([tmp_path])

assert [violation.kind for violation in violations] == ["path"]


@pytest.mark.parametrize(
("filename", "content"),
[
("safe.py", f"ACROSS_{FORBIDDEN_EXAMPLE.upper()}_RELEASE_TRAIN_CANDIDATE=1"),
(f"write_{FORBIDDEN_EXAMPLE}_release.py", "safe content"),
],
)
def test_detects_prohibited_language_embedded_in_identifiers(
tmp_path: Path,
filename: str,
content: str,
) -> None:
(tmp_path / filename).write_text(content, encoding="utf-8")

violations = find_language_violations([tmp_path])

assert violations


def test_ignores_generated_dependency_directories(tmp_path: Path) -> None:
ignored = tmp_path / "node_modules" / "example.txt"
ignored.parent.mkdir(parents=True)
ignored.write_text(FORBIDDEN_EXAMPLE, encoding="utf-8")

assert find_language_violations([tmp_path]) == []


def test_fails_closed_when_source_cannot_be_read(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
source = tmp_path / "README.md"
source.write_text("safe content", encoding="utf-8")
original_read_bytes = Path.read_bytes

def fail_for_source(path: Path) -> bytes:
if path == source:
raise OSError("fixture read failure")
return original_read_bytes(path)

monkeypatch.setattr(Path, "read_bytes", fail_for_source)

with pytest.raises(RuntimeError, match="could not inspect a source file"):
find_language_violations([tmp_path])


def test_fails_closed_when_source_is_not_utf8(tmp_path: Path) -> None:
(tmp_path / "README.md").write_bytes(b"\xff\xfe")

with pytest.raises(RuntimeError, match="could not decode a source file"):
find_language_violations([tmp_path])


def test_fails_closed_when_source_tree_cannot_be_traversed(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
def failing_walk(root: Path, onerror=None):
assert root == tmp_path.resolve()
assert onerror is not None
onerror(OSError("fixture traversal failure"))
return iter(())

monkeypatch.setattr(product_language.os, "walk", failing_walk)

with pytest.raises(RuntimeError, match="could not traverse a source tree"):
find_language_violations([tmp_path])


def test_cli_reports_only_repo_label_and_relative_path(
tmp_path: Path,
capsys: pytest.CaptureFixture[str],
) -> None:
repository = tmp_path / "example-repo"
repository.mkdir()
(repository / "README.md").write_text(FORBIDDEN_EXAMPLE, encoding="utf-8")

assert main([str(repository)]) == 1

output = capsys.readouterr().out
assert "example-repo:README.md:1 [content]" in output
assert str(tmp_path) not in output


def test_open_source_check_enforces_product_language_guard() -> None:
check_script = (ROOT / "scripts" / "open_source_check.sh").read_text(encoding="utf-8")

assert "python3 scripts/check_product_language.py ." in check_script


def test_release_acceptance_scans_all_four_source_trees() -> None:
acceptance_script = (
ROOT / "scripts" / "run_vnext_single_release_acceptance.sh"
).read_text(encoding="utf-8")

assert "product_language_guard" in acceptance_script
assert (
"scripts/check_product_language.py '$ROOT_DIR' '$ORCHESTRATOR_ROOT' "
"'$CONTEXT_ROOT' '$AUTOPILOT_ROOT'"
) in acceptance_script
Loading