From ddaee058efe4c8381f60f5a2ebcae0de9ee9203d Mon Sep 17 00:00:00 2001 From: mattamior Date: Thu, 3 Sep 2026 14:29:24 +0800 Subject: [PATCH 1/6] feat: prepare Agnir Core 0.2 consumer support --- src/svif/continuity/agnir.py | 171 ++++++++++++++++++++++++--------- tests/test_agnir_continuity.py | 163 ++++++++++++++++++++++++++++--- 2 files changed, 280 insertions(+), 54 deletions(-) diff --git a/src/svif/continuity/agnir.py b/src/svif/continuity/agnir.py index 4a6e38a..8c5f722 100644 --- a/src/svif/continuity/agnir.py +++ b/src/svif/continuity/agnir.py @@ -4,7 +4,7 @@ import json import os import re -from dataclasses import asdict +from dataclasses import asdict, dataclass from pathlib import Path from svif.runtime import BindingError, ContinuitySnapshot, OperationOutcome @@ -18,20 +18,45 @@ def __init__(self, code: str, message: str) -> None: self.code = code +@dataclass(frozen=True) +class _ResolvedAgnir: + version: str + profile: str + lineage_identity: str | None + vcs_selector: str | None + state: Path + next_actions: Path + decisions: Path | None + evidence: Path | None + + class AgnirFilesystemContinuityProvider: - """Agnir `repository-filesystem/0.1` Continuity Provider for Svif. + """Agnir repository/filesystem Continuity Provider for Svif. - The Project root is the authorized Project Entry Point. This adapter - intentionally implements only Agnir's current repository/filesystem profile; - that profile is not part of the generic Svif Orchestrator contract. + The adapter supports the published Core/profile `0.1` line and the + experimental Core/profile `0.2` lineage-aware line. Provider-specific + lineage and selector semantics stay inside this adapter; the Svif + Orchestrator remains Continuity-Provider-neutral. """ provider_id = "agnir" - _CORE_VERSION = "0.1" - _PROFILE = "repository-filesystem/0.1" + _SUPPORTED_PROFILES = { + "0.1": "repository-filesystem/0.1", + "0.2": "repository-filesystem/0.2", + } - def __init__(self, project_root: str | Path) -> None: + def __init__( + self, + project_root: str | Path, + *, + expected_core_version: str | None = None, + expected_profile: str | None = None, + selected_vcs_selector: str | None = None, + ) -> None: self.project_root = Path(project_root).resolve() + self.expected_core_version = expected_core_version + self.expected_profile = expected_profile + self.selected_vcs_selector = selected_vcs_selector @staticmethod def _strip_scalar(value: str) -> str | None: @@ -43,28 +68,33 @@ def _strip_scalar(value: str) -> str | None: return value @classmethod - def _parse_discovery(cls, text: str) -> dict[tuple[str, str], str | None]: - """Parse only the scalar subset required by repository-filesystem/0.1. + def _parse_discovery(cls, text: str) -> dict[tuple[str, ...], str | None]: + """Parse the scalar YAML subset used by the repository profiles. - This is deliberately not a general YAML parser. Unsupported YAML forms - fail later as missing/inconsistent required semantics rather than being - guessed by the adapter. + This is deliberately not a general YAML parser. It recognizes nested + mapping/scalar paths by indentation and ignores list items/complex YAML. + Unsupported forms fail later as missing or inconsistent semantics. """ - values: dict[tuple[str, str], str | None] = {} - section: str | None = None + values: dict[tuple[str, ...], str | None] = {} + stack: list[tuple[int, str]] = [] for raw in text.splitlines(): if not raw.strip() or raw.lstrip().startswith("#"): continue - top = re.match(r"^([A-Za-z0-9_/-]+):\s*$", raw) - if top: - section = top.group(1) + if raw.lstrip().startswith("-"): + continue + indent = len(raw) - len(raw.lstrip(" ")) + match = re.match(r"^\s*([A-Za-z0-9_./-]+):\s*(.*?)\s*$", raw) + if not match: continue - if section is None: + key, scalar_text = match.groups() + while stack and indent <= stack[-1][0]: + stack.pop() + if scalar_text == "": + stack.append((indent, key)) continue - scalar = re.match(r"^\s{2}([A-Za-z0-9_/-]+):\s*(.*?)\s*$", raw) - if scalar: - values[(section, scalar.group(1))] = cls._strip_scalar(scalar.group(2)) + path = tuple([item[1] for item in stack] + [key]) + values[path] = cls._strip_scalar(scalar_text) return values @staticmethod @@ -99,7 +129,7 @@ def _resolve_locator( ) return candidate - def _discover(self, project_identity: str) -> dict[str, Path | None]: + def _discover(self, project_identity: str) -> _ResolvedAgnir: discovery = self.project_root / "AGNIR.yaml" if not discovery.is_file(): raise self._fail( @@ -108,16 +138,29 @@ def _discover(self, project_identity: str) -> dict[str, Path | None]: ) values = self._parse_discovery(discovery.read_text(encoding="utf-8")) + version = values.get(("agnir", "version")) + profile = values.get(("agnir", "discovery_profile")) - if values.get(("agnir", "version")) != self._CORE_VERSION: + if not isinstance(version, str) or version not in self._SUPPORTED_PROFILES: + raise self._fail( + "AGNIR_DISCOVERY_UNSUPPORTED_VERSION", + f"unsupported Agnir Core version: {version!r}", + ) + expected_profile_for_version = self._SUPPORTED_PROFILES[version] + if profile != expected_profile_for_version: + raise self._fail( + "AGNIR_DISCOVERY_INCONSISTENT", + f"Core {version} requires discovery profile {expected_profile_for_version!r}, discovered {profile!r}", + ) + if self.expected_core_version is not None and version != self.expected_core_version: raise self._fail( "AGNIR_DISCOVERY_UNSUPPORTED_VERSION", - f"expected Agnir Core {self._CORE_VERSION}", + f"Svif binding expects Agnir Core {self.expected_core_version}, discovered {version}", ) - if values.get(("agnir", "discovery_profile")) != self._PROFILE: + if self.expected_profile is not None and profile != self.expected_profile: raise self._fail( "AGNIR_DISCOVERY_INCONSISTENT", - f"expected discovery profile {self._PROFILE}", + f"Svif binding expects discovery profile {self.expected_profile!r}, discovered {profile!r}", ) discovered_identity = values.get(("project", "identity")) @@ -127,6 +170,31 @@ def _discover(self, project_identity: str) -> dict[str, Path | None]: f"expected {project_identity!r}, discovered {discovered_identity!r}", ) + lineage_identity = values.get(("continuity", "lineage")) + if version == "0.2": + if not isinstance(lineage_identity, str) or not lineage_identity: + raise self._fail( + "AGNIR_LINEAGE_REQUIRED", + "Core 0.2 repository/filesystem discovery requires continuity.lineage", + ) + else: + lineage_identity = None + + binding_selector = values.get( + ("extensions", "agnir/vcs", "lineage_binding", "selector") + ) + if self.selected_vcs_selector is not None and version == "0.2": + if not isinstance(binding_selector, str) or not binding_selector: + raise self._fail( + "AGNIR_VCS_LINEAGE_BINDING_REQUIRED", + "selected VCS context has no durable lineage selector binding", + ) + if binding_selector != self.selected_vcs_selector: + raise self._fail( + "AGNIR_VCS_LINEAGE_BINDING_MISMATCH", + f"selected VCS selector {self.selected_vcs_selector!r} conflicts with durable binding {binding_selector!r}", + ) + paths = { "state": self._resolve_locator( values.get(("memory", "state")), required=True, kind="Current State" @@ -157,7 +225,16 @@ def _discover(self, project_identity: str) -> dict[str, Path | None]: "Evidence locator is not a directory", ) - return paths + return _ResolvedAgnir( + version=version, + profile=profile, + lineage_identity=lineage_identity, + vcs_selector=binding_selector if isinstance(binding_selector, str) else None, + state=paths["state"], + next_actions=paths["next_actions"], + decisions=paths["decisions"], + evidence=paths["evidence"], + ) @staticmethod def _read_optional(path: Path | None) -> str | None: @@ -173,14 +250,18 @@ def _read_evidence(path: Path | None) -> dict[str, str]: if item.is_file() } + def resolve_lineage(self, project_identity: str) -> str | None: + """Return the selected logical Agnir lineage, if the compatibility line has one.""" + return self._discover(project_identity).lineage_identity + def load(self, project_identity: str) -> ContinuitySnapshot: - paths = self._discover(project_identity) + resolved = self._discover(project_identity) return ContinuitySnapshot( project_identity=project_identity, - state=self._read_optional(paths["state"]), - next_actions=self._read_optional(paths["next_actions"]), - decisions=self._read_optional(paths["decisions"]), - evidence=self._read_evidence(paths["evidence"]), + state=self._read_optional(resolved.state), + next_actions=self._read_optional(resolved.next_actions), + decisions=self._read_optional(resolved.decisions), + evidence=self._read_evidence(resolved.evidence), ) @staticmethod @@ -198,7 +279,7 @@ def _atomic_write(path: Path, content: str) -> None: os.replace(tmp, path) def checkpoint(self, outcome: OperationOutcome) -> None: - paths = self._discover(outcome.project_identity) + resolved = self._discover(outcome.project_identity) update = outcome.continuity_update state = self._require_text_update(update.state, "Current State") @@ -206,26 +287,29 @@ def checkpoint(self, outcome: OperationOutcome) -> None: decisions = self._require_text_update(update.decisions, "Decisions") if state is not None: - self._atomic_write(paths["state"], state) + self._atomic_write(resolved.state, state) if next_actions is not None: - self._atomic_write(paths["next_actions"], next_actions) + self._atomic_write(resolved.next_actions, next_actions) if decisions is not None: - if paths["decisions"] is None: + if resolved.decisions is None: raise self._fail( "AGNIR_DISCOVERY_UNRESOLVABLE", "cannot persist Decisions because the Discovery Record has no Decisions locator", ) - self._atomic_write(paths["decisions"], decisions) + self._atomic_write(resolved.decisions, decisions) - evidence_dir = paths["evidence"] - if evidence_dir is not None: + if resolved.evidence is not None: digest = hashlib.sha256( - f"{outcome.project_identity}\0{outcome.operation_id}".encode("utf-8") + ( + f"{outcome.project_identity}\0{resolved.lineage_identity or ''}\0" + f"{outcome.operation_id}" + ).encode("utf-8") ).hexdigest()[:16] - evidence_path = evidence_dir / f"svif-operation-{digest}.json" + evidence_path = resolved.evidence / f"svif-operation-{digest}.json" payload = { "svif_runtime_checkpoint": "0.1", "project_identity": outcome.project_identity, + "agnir_lineage": resolved.lineage_identity, "operation_id": outcome.operation_id, "subject_identity": outcome.subject_identity, "externally_effectful": outcome.externally_effectful, @@ -236,5 +320,6 @@ def checkpoint(self, outcome: OperationOutcome) -> None: json.dumps(payload, indent=2, sort_keys=True) + "\n", ) - # Do not claim resumability until the resulting locator chain resolves. + # Do not claim resumability until the resulting locator chain, Project + # identity, logical lineage, and optional VCS selector binding resolve. self._discover(outcome.project_identity) diff --git a/tests/test_agnir_continuity.py b/tests/test_agnir_continuity.py index 13377e9..70332a9 100644 --- a/tests/test_agnir_continuity.py +++ b/tests/test_agnir_continuity.py @@ -25,25 +25,52 @@ def write_project( root: Path, *, version: str = "0.1", + profile: str | None = None, identity: str = PROJECT, state_locator: str = ".agnir/state.md", + lineage: str | None = None, + vcs_selector: str | None = None, ) -> None: + if profile is None: + profile = f"repository-filesystem/{version}" if version in {"0.1", "0.2"} else "repository-filesystem/0.1" (root / ".agnir/evidence").mkdir(parents=True) (root / ".agnir/state.md").write_text("# State\nold\n", encoding="utf-8") (root / ".agnir/next-actions.md").write_text("# Next\nold\n", encoding="utf-8") (root / ".agnir/decisions.md").write_text("# Decisions\nold\n", encoding="utf-8") (root / ".agnir/evidence/seed.md").write_text("# Seed\n", encoding="utf-8") + + continuity = "" + if version == "0.2" or lineage is not None: + continuity = ( + "\ncontinuity:\n" + f' lineage: "{lineage or "urn:test:lineage:default"}"\n' + ) + + extensions = "" + if vcs_selector is not None: + extensions = ( + "\nextensions:\n" + " agnir/vcs:\n" + ' branch_continuity: "lineage-bound"\n' + ' integration_reconciliation: "required"\n' + " lineage_binding:\n" + ' kind: "vcs-ref"\n' + f' selector: "{vcs_selector}"\n' + ) + (root / "AGNIR.yaml").write_text( "agnir:\n" f' version: "{version}"\n' - ' discovery_profile: "repository-filesystem/0.1"\n\n' + f' discovery_profile: "{profile}"\n\n' "project:\n" - f' identity: "{identity}"\n\n' - "memory:\n" + f' identity: "{identity}"\n' + + continuity + + "\nmemory:\n" f' state: "{state_locator}"\n' ' next_actions: ".agnir/next-actions.md"\n' ' decisions: ".agnir/decisions.md"\n' - ' evidence: ".agnir/evidence/"\n', + ' evidence: ".agnir/evidence/"\n' + + extensions, encoding="utf-8", ) @@ -65,7 +92,7 @@ def execute(self, context, request) -> WorkResult: class AgnirFilesystemContinuityTests(unittest.TestCase): - def test_loads_repository_filesystem_profile(self) -> None: + def test_loads_repository_filesystem_profile_0_1(self) -> None: with tempfile.TemporaryDirectory() as directory: root = Path(directory) write_project(root) @@ -74,11 +101,32 @@ def test_loads_repository_filesystem_profile(self) -> None: snapshot = provider.load(PROJECT) self.assertEqual(snapshot.project_identity, PROJECT) + self.assertIsNone(provider.resolve_lineage(PROJECT)) self.assertIn("old", snapshot.state) self.assertIn("old", snapshot.next_actions) self.assertIn("old", snapshot.decisions) self.assertIn("seed.md", snapshot.evidence) + def test_loads_repository_filesystem_profile_0_2_and_resolves_logical_lineage(self) -> None: + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + selector = "refs/heads/feature/parallel" + lineage = "urn:test:lineage:parallel" + write_project(root, version="0.2", lineage=lineage, vcs_selector=selector) + provider = AgnirFilesystemContinuityProvider( + root, + expected_core_version="0.2", + expected_profile="repository-filesystem/0.2", + selected_vcs_selector=selector, + ) + + snapshot = provider.load(PROJECT) + + self.assertEqual(snapshot.project_identity, PROJECT) + self.assertEqual(provider.resolve_lineage(PROJECT), lineage) + self.assertIn("old", snapshot.state) + self.assertIn("seed.md", snapshot.evidence) + def test_project_mismatch_preserves_agnir_failure_class(self) -> None: with tempfile.TemporaryDirectory() as directory: root = Path(directory) @@ -101,6 +149,68 @@ def test_unsupported_version_preserves_agnir_failure_class(self) -> None: self.assertEqual(raised.exception.code, "AGNIR_DISCOVERY_UNSUPPORTED_VERSION") + def test_svif_binding_can_require_core_0_2(self) -> None: + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + write_project(root, version="0.1") + provider = AgnirFilesystemContinuityProvider( + root, + expected_core_version="0.2", + expected_profile="repository-filesystem/0.2", + ) + + with self.assertRaises(AgnirDiscoveryError) as raised: + provider.load(PROJECT) + + self.assertEqual(raised.exception.code, "AGNIR_DISCOVERY_UNSUPPORTED_VERSION") + + def test_core_0_2_requires_logical_lineage(self) -> None: + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + write_project(root, version="0.2", lineage="urn:test:lineage:temporary") + text = (root / "AGNIR.yaml").read_text(encoding="utf-8") + text = text.replace('\ncontinuity:\n lineage: "urn:test:lineage:temporary"\n', "") + (root / "AGNIR.yaml").write_text(text, encoding="utf-8") + provider = AgnirFilesystemContinuityProvider(root) + + with self.assertRaises(AgnirDiscoveryError) as raised: + provider.load(PROJECT) + + self.assertEqual(raised.exception.code, "AGNIR_LINEAGE_REQUIRED") + + def test_selected_vcs_selector_must_match_durable_binding(self) -> None: + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + write_project( + root, + version="0.2", + lineage="urn:test:lineage:parallel", + vcs_selector="refs/heads/feature/a", + ) + provider = AgnirFilesystemContinuityProvider( + root, + selected_vcs_selector="refs/heads/feature/b", + ) + + with self.assertRaises(AgnirDiscoveryError) as raised: + provider.load(PROJECT) + + self.assertEqual(raised.exception.code, "AGNIR_VCS_LINEAGE_BINDING_MISMATCH") + + def test_selected_vcs_selector_requires_durable_binding(self) -> None: + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + write_project(root, version="0.2", lineage="urn:test:lineage:parallel") + provider = AgnirFilesystemContinuityProvider( + root, + selected_vcs_selector="refs/heads/feature/a", + ) + + with self.assertRaises(AgnirDiscoveryError) as raised: + provider.load(PROJECT) + + self.assertEqual(raised.exception.code, "AGNIR_VCS_LINEAGE_BINDING_REQUIRED") + def test_locator_cannot_escape_project_root(self) -> None: with tempfile.TemporaryDirectory() as directory: root = Path(directory) @@ -113,7 +223,7 @@ def test_locator_cannot_escape_project_root(self) -> None: self.assertEqual(raised.exception.code, "AGNIR_DISCOVERY_UNRESOLVABLE") - def test_orchestrator_checkpoints_explicit_update_through_agnir(self) -> None: + def test_orchestrator_checkpoints_explicit_update_through_agnir_0_1(self) -> None: with tempfile.TemporaryDirectory() as directory: root = Path(directory) write_project(root) @@ -140,15 +250,46 @@ def test_orchestrator_checkpoints_explicit_update_through_agnir(self) -> None: (root / ".agnir/state.md").read_text(encoding="utf-8"), "# State\nnew durable truth\n", ) - self.assertEqual( - (root / ".agnir/next-actions.md").read_text(encoding="utf-8"), - "# Next\ncontinue product integration\n", - ) evidence_files = list((root / ".agnir/evidence").glob("svif-operation-*.json")) self.assertEqual(len(evidence_files), 1) payload = json.loads(evidence_files[0].read_text(encoding="utf-8")) self.assertEqual(payload["operation_id"], "op-agnir-1") - self.assertEqual(payload["subject_identity"], SUBJECT) + self.assertIsNone(payload["agnir_lineage"]) + + def test_orchestrator_checkpoint_preserves_core_0_2_lineage_and_binding(self) -> None: + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + selector = "refs/heads/feature/parallel" + lineage = "urn:test:lineage:parallel" + write_project(root, version="0.2", lineage=lineage, vcs_selector=selector) + provider = AgnirFilesystemContinuityProvider( + root, + expected_core_version="0.2", + expected_profile="repository-filesystem/0.2", + selected_vcs_selector=selector, + ) + surface = UpdatingSurface() + orchestrator = Orchestrator( + continuity_providers=(provider,), + execution_surfaces=(surface,), + ) + binding = ProjectBinding( + project_identity=PROJECT, + continuity=ProviderBinding("agnir"), + execution_surface="chatgpt", + ) + + orchestrator.run( + binding, + OperationRequest(operation_id="op-agnir-lineage", intent="advance selected lineage"), + ) + + self.assertEqual(provider.resolve_lineage(PROJECT), lineage) + evidence_files = list((root / ".agnir/evidence").glob("svif-operation-*.json")) + self.assertEqual(len(evidence_files), 1) + payload = json.loads(evidence_files[0].read_text(encoding="utf-8")) + self.assertEqual(payload["agnir_lineage"], lineage) + self.assertEqual(payload["operation_id"], "op-agnir-lineage") if __name__ == "__main__": From eac2ab0dd70695d972b99afad084614eae26c77c Mon Sep 17 00:00:00 2001 From: mattamior Date: Thu, 3 Sep 2026 14:32:37 +0800 Subject: [PATCH 2/6] migrate: move Svif Project to Agnir v0.2.0 stable --- ...-agnir-v0.2.0-real-downstream-migration.md | 65 +++++++++ .agnir/next-actions.md | 97 ++++---------- .agnir/state.md | 124 +++++------------- AGNIR.yaml | 17 ++- SVIF.yaml | 4 +- tests/test_agnir_stable_migration.py | 61 +++++++++ 6 files changed, 200 insertions(+), 168 deletions(-) create mode 100644 .agnir/evidence/2026-09-03-agnir-v0.2.0-real-downstream-migration.md create mode 100644 tests/test_agnir_stable_migration.py diff --git a/.agnir/evidence/2026-09-03-agnir-v0.2.0-real-downstream-migration.md b/.agnir/evidence/2026-09-03-agnir-v0.2.0-real-downstream-migration.md new file mode 100644 index 0000000..51db49f --- /dev/null +++ b/.agnir/evidence/2026-09-03-agnir-v0.2.0-real-downstream-migration.md @@ -0,0 +1,65 @@ +# Published Agnir v0.1.1 -> v0.2.0 real downstream migration — 2026-09-03 + +Status: **migration-line evidence; not authoritative-main acceptance until target reconciliation completes.** + +## Purpose + +Exercise Svif as a real downstream Project crossing the published Agnir compatibility boundary from repository `v0.1.1` / Core-profile `0.1` to repository `v0.2.0` / Core-profile `0.2`. This is distinct from the earlier experimental Core 0.2 consumer validation and from synthetic Agnir migration fixtures. + +## Captured source Project + +- repository: `iorLab/svif`; +- authoritative source ref: `main`; +- captured source revision: `dac058789a27f32f4ed1949874c1954f31f12bd8`; +- Project identity: `urn:svif:project:svif-core`; +- source `AGNIR.yaml` blob: `3c94e5b5a342c3515f1ef67e3aec323029d64665`; +- source `SVIF.yaml` blob: `d7e360f747afa9f9844c6b722ef0b35b476b29a5`; +- source State blob: `d70d21e3ab8b29828b89edf38b514f91d12160d2`; +- source Next Actions blob: `486e09ab08821169b72ea7d6473abd2d383a20cd`; +- source Agnir repository release: `v0.1.1`; +- source Agnir immutable applied revision: `e9712357ab590e5c1e5357b3cf3219d07d789aff`; +- source Core/profile: `0.1` / `repository-filesystem/0.1`. + +The source Project already had durable State, Next Actions, Decisions, Evidence and one stable Project identity. This migration therefore MUST NOT be treated as clean initialization. + +## Target published Agnir package + +- repository release: `v0.2.0`; +- exact stable tag target: `fc84095ed5d500be9e1b43a4af0e93356571bbd4`; +- Core compatibility: `0.2`; +- discovery profile: `repository-filesystem/0.2`. + +## Preparation + +Commit `ddaee058efe4c8381f60f5a2ebcae0de9ee9203d` was created from the captured Svif main baseline before changing Project compatibility. It imports the previously real-consumer-validated dual-line repository/filesystem adapter and its 0.2 lineage/binding tests onto the current Svif source tree. At that preparatory revision, `AGNIR.yaml` and `SVIF.yaml` still declared Core/profile `0.1`. + +## Atomic Project-truth migration + +The migration commit changes the Project-owned compatibility surfaces coherently: + +- `AGNIR.yaml`: Core `0.2`, profile `repository-filesystem/0.2`; +- `SVIF.yaml`: continuity compatibility/profile `0.2`; +- Project identity remains `urn:svif:project:svif-core`; +- memory locators remain `.agnir/state.md`, `.agnir/next-actions.md`, `.agnir/decisions.md`, `.agnir/evidence/`; +- logical lineage becomes `urn:svif:lineage:agnir-v0.2.0-stable-migration`; +- backend selector is separately bound to `refs/heads/migration/agnir-v0.2.0-stable`; +- Agnir operational provenance becomes published stable `0.2.0` at `fc84095...`; +- Current State and Next Actions are reconciled to describe the migration while preserving unrelated Svif product/distribution obligations; +- existing Decisions and historical Evidence remain present. + +The migration branch is temporary and cannot silently become canonical continuity merely because it is writable. Its lineage/selector and branch-local State/Next are validation inputs only until a target-main reconciliation is staged and accepted. + +## Required validation + +Before any main publication, require: + +1. all active Svif first-use/bootstrap/activation surfaces to converge from Core/profile `0.1` to `0.2`; +2. adapter discovery of Project `urn:svif:project:svif-core` and the logical lineage without predecessor-private context; +3. selector/lineage separation and mismatch failures; +4. checkpoint/resume on Core/profile `0.2`; +5. repository integrity, portable contracts, founding E2E, Plugin package/discovery/first-use tests and full suite; +6. exact migration candidate CI on a non-authoritative validation surface; +7. fresh source/target stale checks before target-main advancement; +8. authoritative-main fresh resume after acceptance. + +If the migration exposes an Agnir product defect rather than a Svif binding defect, record it as downstream evidence and repair Agnir rather than weakening the Svif test to hide it. diff --git a/.agnir/next-actions.md b/.agnir/next-actions.md index 486e09a..f9be8c2 100644 --- a/.agnir/next-actions.md +++ b/.agnir/next-actions.md @@ -1,72 +1,29 @@ # Svif Next Actions -1. **Retain the release branch until cleanup is explicitly confirmed.** Record and report its final remote tip after the post-release checkpoint reaches `main`; do not delete the local or remote branch without the Principal's confirmation. -2. **Continue the separate public/personal ChatGPT path.** Resolve or formally clarify the publisher-verification gate, then submit the exact tested Skills-only package to the universal Plugins Directory. Record scan/review evidence, explicitly Publish after approval, and validate the first individual-user ChatGPT Web installation without conflating it with the Repository Preview. -3. **Use a new Preview tag for any fix.** Keep `v0.2.0-preview.1` immutable; repair observed defects into `v0.2.0-preview.2` rather than moving the released tag. -4. **Repair only observed friction, then expand neutrality/surface evidence.** Keep `plugin/skills/svif/SKILL.md` single-sourced; add no MCP merely for publication. Add broader non-repository and multi-project evidence without making GitHub, Cloudflare, ChatGPT, Cursor, or another execution environment universal dependencies. -5. Keep live Cloudflare delivery disabled unless explicitly authorized. If authorized later, preserve exact verified-subject delivery and require independent observation before success claims. - -## Current Agnir compatibility reference - -- Agnir Core compatibility consumed by Svif: `0.1`. -- Repository/filesystem profile: `repository-filesystem/0.1`. -- Agnir repository release SemVer: stable `0.1.1`, formally published as `v0.1.1`. -- Target-main Svif Project operational provenance: `agnir-agent-skill` release `0.1.1` from `iorLab/agnir`, immutable applied revision `e9712357ab590e5c1e5357b3cf3219d07d789aff`. -- Current Agent-operable activation route: `Project root -> AGENTS.md -> README.md / Agnir Project Instructions -> AGNIR.yaml -> declared durable memory`. -- Svif depends on Agnir continuity semantics/profile compatibility, not on Agnir's repository history, GitHub, or Skill repository at runtime. - -## Distribution and iteration rules - -- `plugin/` is an active product surface and the portable package targets Agent Plugins `1.0.0`. -- `plugin/skills/svif/SKILL.md` remains the shared Svif Project-orchestration workflow; product-specific packaging must reuse it rather than fork behavior. -- The initial public ChatGPT release is deliberately **Skills only**. Current OpenAI public submission accepts this shape directly; MCP/App packaging is not a publication prerequisite. -- `plugin/.codex-plugin/plugin.json` carries the current OpenAI/Codex public-listing metadata and points to the same `skills/` implementation. -- `.agents/plugins/marketplace.json` is the supported self-distributed `v0.2.0-preview.1` route for Codex CLI and ChatGPT desktop/Codex. It remains separate from universal-directory publication and personal ChatGPT Web onboarding. -- Repository CI validates package/conformance/distribution properties; do not call personal ChatGPT installation validated until an actual individual-user ChatGPT surface has installed and exercised the published Plugin. -- Publisher/account verification gates are external release constraints. They must not be mistaken for package/runtime failures or used as justification for speculative MCP/App changes. -- Plugin changes SHOULD be driven by real submission, installation, or execution friction whenever possible. -- **First-use onboarding is a Svif product responsibility.** When the selected Project is genuinely uninitialized and no durable binding chooses another Continuity Provider, Svif's founding repository/filesystem path establishes Agnir Core `0.1` / `repository-filesystem/0.1` continuity plus a matching minimal `project-binding/0.2` `SVIF.yaml` using one stable Project identity. A user MUST NOT have to pre-initialize Agnir as a prerequisite for first Svif use. -- Partial/broken Agnir/Svif artifacts are repair cases, not clean bootstrap cases. An intentionally configured different Continuity Provider must not be overwritten with Agnir. -- The first-use bootstrap consumes Agnir protocol/profile semantics through the founding Continuity Provider integration and must not make the Agnir Skill repository, prior installation chat, GitHub, or another execution surface a runtime prerequisite. -- Distribution MUST NOT reimplement `src/svif/runtime.py`, move Project truth out of the Continuity Provider, or grant protected authority through model-controlled payloads. -- ChatGPT Web availability is a product requirement for the current personal-user target. Any future packaging restriction that removes Web support must be surfaced as a deliberate product decision, not hidden as an implementation detail. - -### Auxiliary repository-marketplace evidence rule - -The GitHub marketplace path remains a useful secondary validation channel. **Package/conformance/distribution CI is not installation evidence.** For a revision-sensitive marketplace exercise, record the immutable commit SHA actually invoked when the client exposes enough evidence to establish it. Treat a repository ref's current SHA only as comparison evidence; when the surface cannot bind invocation to one immutable commit, preserve exact installed-revision provenance as unconfirmed. - -## Documentation maintenance rule - -- Architecture/runtime/distribution changes are incomplete until affected explanatory sections and diagrams in both `README.md` and `README.zh-CN.md` are updated in the same change set. -- Localized diagrams are comprehension-first rather than literal translations; important Simplified Chinese nodes explain both role and responsibility. -- README repository trees remain compact navigation views. -- `REPOSITORY_TREE.md` is the exhaustive file-level map. Tracked file additions/removals/moves or material responsibility changes must update it in the same change set; if the compact tree is also affected, both README language versions must update as well. - -## Branch governance - -- `main` is the only long-lived branch. -- Historical predecessor and retired work is indexed by immutable commit SHA in `history/BRANCH_ARCHIVE.md`; live legacy/feature/fix/tmp branch refs are not retained. - -## Completed in the current implementation sequence - -- Product Architecture `0.2` frozen around Orchestrator + Continuity Provider + Execution Surface + Capability Provider. -- Minimal executable Orchestrator implemented and CI-proven. -- Concrete Agnir repository/filesystem Continuity Provider implemented. -- ChatGPT structured Execution Surface bridge implemented with `begin()` / `complete()` handoff. -- Cloudflare provider ownership consolidated into `src/svif/capabilities/cloudflare.py` and `integrations/cloudflare/`. -- English and Simplified Chinese README entry points include synchronized Architecture and Runtime / Operation Flow diagrams. -- Founding credential-free E2E implemented at `tests/test_founding_e2e.py`. -- Skill-first Plugin MVP exists under `plugin/` using Agent Plugins `1.0.0` packaging. -- OpenAI/Codex repository marketplace metadata maps to the same Skill-first Plugin root without duplicating runtime semantics. -- The personal ChatGPT audience/distribution correction is recorded in `.agnir/evidence/2026-08-31-personal-chatgpt-distribution-checkpoint.md`. -- Current OpenAI public submission requirements have been re-verified: Skills-only public Plugins are accepted; the repository manifest, README guidance, listing metadata, and review-case preparation have been aligned to that route. -- A real publisher verification attempt reached the individual-developer verification flow but was blocked at the required accepted-default-payment-method gate before verification/submission. This is recorded as an external release blocker without storing private payment/account data. -- Svif first-use onboarding now handles an ordinary non-Agnir Project without requiring manual Agnir pre-initialization; regression pressure is `tests/test_plugin_first_use_bootstrap.py` and durable evidence is `.agnir/evidence/2026-08-31-plugin-first-use-bootstrap-fix.md`. -- The target-main candidate upgrades Svif's Agnir operational baseline compatibly to stable `v0.1.1`; `AGNIR.yaml` records immutable provenance without changing Core/profile compatibility, Project identity, memory locators, or `SVIF.yaml`. -- The immutable candidate passed Codex CLI and ChatGPT desktop/Codex installation, bootstrap, checkpoint, initialized-Project idempotency, and fresh-context recovery acceptance before release. -- Svif `v0.2.0-preview.1` is released from authoritative `main` as a GitHub Prerelease; immutable tag installation resolves to the verified release commit and exposes the Skills-only Plugin as installed/enabled. -- Main-only branch governance is complete. - -## Repository-retirement note - -The former `iorLab/svif-cloudflare-reference` project is retired. No future Svif work should target it. +Svif is performing a real downstream migration from published Agnir `v0.1.1` / Core-profile `0.1` to published stable Agnir `v0.2.0` / Core-profile `0.2` on temporary lineage `urn:svif:lineage:agnir-v0.2.0-stable-migration`. Authoritative `main` remains unchanged until the exact migration result is fully validated and reconciled. + +1. **Converge every active Svif surface onto Agnir Core/profile `0.2`.** Update the shared Plugin Skill first-use bootstrap/activation/current-binding text, first-use/bootstrap tests, repository checks, bilingual README and repository map where they describe the founding Agnir compatibility line. Do not change Orchestrator/Execution/Capability architecture merely because the Continuity Provider compatibility changed. +2. **Validate the migrated Svif Project as a real published-stable downstream consumer.** Require Project identity `urn:svif:project:svif-core`, logical lineage presence, selector != lineage identity, unchanged durable memory locators, stable Agnir provenance `v0.2.0` / `fc84095...`, Core/profile `0.2` discovery, checkpoint/resume, founding E2E, Plugin package/first-use behavior, repository integrity, contracts and full suite. +3. **Pressure upgrade usability rather than hiding friction.** Any failure attributable to Agnir `v0.2.0` migration semantics, Skill contract, discovery, lineage binding or recovery should be treated as Agnir product evidence and repaired in Agnir `v0.2.x` when appropriate rather than papered over in Svif. +4. **Construct a target-reconciled main candidate only after the migration branch is fully green.** Main must receive the accepted Project/package result while creating/preserving its own authoritative logical lineage and `refs/heads/main` binding. Do not copy temporary migration-line State/Next as automatic target truth, and do not advance main before candidate validation and fresh source/target stale checks. +5. **After authoritative-main verification, record this as Agnir v1 downstream-upgrade evidence.** The receipt must distinguish synthetic fixtures and earlier pre-release experiments from this published `v0.1.1` -> published `v0.2.0` real Project upgrade. +6. **Preserve existing Svif distribution obligations.** Keep `v0.2.0-preview.1` immutable; continue the separate personal ChatGPT publisher-verification path; use a new Preview tag for any Svif release fix; keep live Cloudflare delivery disabled unless explicitly authorized. +7. **Retire temporary migration/validation refs after accepted reconciliation when a safe delete-ref path is available.** `main` remains the only long-lived Svif branch. + +## Captured migration receipts + +- authoritative Svif baseline: `main@dac058789a27f32f4ed1949874c1954f31f12bd8`; +- previous Agnir operational release: `v0.1.1` -> `e9712357ab590e5c1e5357b3cf3219d07d789aff`; +- target Agnir stable release: `v0.2.0` -> `fc84095ed5d500be9e1b43a4af0e93356571bbd4`; +- preparatory dual-line adapter/tests commit: `ddaee058efe4c8381f60f5a2ebcae0de9ee9203d`; +- migration branch selector: `refs/heads/migration/agnir-v0.2.0-stable`; +- migration logical lineage: `urn:svif:lineage:agnir-v0.2.0-stable-migration`. + +## Invariants + +- Project identity remains stable across migration. +- Durable State/Next/Decisions/Evidence remain Project-owned. +- logical lineage identity != VCS selector != commit/checkpoint receipt. +- Core `0.1` -> `0.2` is explicit migration, not compatible upgrade. +- Source/migration continuity is reconciliation input, not automatic target-main truth. +- Svif product architecture remains Orchestrator + Continuity Provider + Execution Surface + Capability Provider. diff --git a/.agnir/state.md b/.agnir/state.md index d70d21e..57e7048 100644 --- a/.agnir/state.md +++ b/.agnir/state.md @@ -1,115 +1,55 @@ # Svif Current State -Svif is the authoritative active **Project orchestration product** in `iorLab/svif` on `main`. Agnir is the independent founding Continuity Provider in `iorLab/agnir`. The former `iorLab/svif-cloudflare-reference` project is retired. Historical ZeroLocal material, retired branches, and the retired Cloudflare reference are evidence only and are not active dependencies or release gates. +Svif is the authoritative active Project orchestration product in `iorLab/svif`. The canonical long-lived ref remains `main`; this temporary migration lineage exists only to validate a real downstream upgrade of Svif's founding Agnir Continuity Provider from published stable `v0.1.1` / Core `0.1` to published stable `v0.2.0` / Core `0.2`. ## Product architecture -Svif coordinates four first-class components: +Svif continues to coordinate the same four first-class components: Orchestrator (`src/svif/runtime.py`), Continuity Provider (`src/svif/continuity/agnir.py`), Execution Surface (`src/svif/execution/chatgpt.py`), and Capability Provider (`src/svif/capabilities/cloudflare.py`). The Project persists; Executors and execution environments may change. No execution surface becomes canonical Project truth merely because execution occurred there. -1. **Orchestrator** — `src/svif/runtime.py`; -2. **Continuity Provider** — founding Agnir implementation at `src/svif/continuity/agnir.py`; -3. **Execution Surface** — founding ChatGPT bridge at `src/svif/execution/chatgpt.py`; -4. **Capability Provider** — founding Cloudflare Workers provider at `src/svif/capabilities/cloudflare.py`. +## Released Svif product state -Stable rule: - -> The Project persists; Executors and execution environments may change. - -No execution surface becomes canonical Project truth merely because execution occurred there. - -## Active contracts and released Preview - -- Svif product line: `0.2`; the current released Repository Preview is `0.2.0-preview.1`. +- Svif product line: `0.2`. +- Released Repository Preview: immutable `v0.2.0-preview.1` from authoritative main commit `2b07b6b5ea0bc8feee59f9f647be9af3069d056e`. - Project Binding: `project-binding/0.2`. - Software Delivery profile: `software-delivery/0.2`. - Capability Adapter: `capability-adapter/0.2`. - Evidence record: `evidence-record/0.2`. -- Repository/filesystem binding serialization: `SVIF.yaml`. -- Agnir Core compatibility: `0.1`. -- Agnir discovery profile: `repository-filesystem/0.1`. -- The target-main candidate applies Agnir stable repository release `0.1.1` from `iorLab/agnir`, immutable revision `e9712357ab590e5c1e5357b3cf3219d07d789aff`, as a compatible operational upgrade recorded in `AGNIR.yaml`. -- Canonical Agnir activation route: `Project root -> AGENTS.md -> README.md / Agnir Project Instructions -> AGNIR.yaml -> declared durable memory`. -- Canonical Svif repository/ref: `iorLab/svif` / `main`. - -The Agnir `v0.1.1` upgrade is a **compatible operational upgrade**, not a migration: Core `0.1` and `repository-filesystem/0.1` are unchanged, while Project identity, memory locators/content, unrelated manifest extensions, and `SVIF.yaml` remain preserved. Its execution-surface activation handoff repair is directly relevant to the Repository Preview installation path. - -Svif `v0.2.0-preview.1` was released from authoritative `main` commit `2b07b6b5ea0bc8feee59f9f647be9af3069d056e`. Annotated tag object `2535cb89426c2d38c2e061948e81954a7c7c26d7` peels to that commit, and GitHub Release `RE_kwDOUEzlR84WteIK` is published as a non-draft Prerelease. Initial immutable candidate `1dbcce6582f218b0762fb655bc03455517c79802` passed real Codex CLI plus ChatGPT desktop/Codex installation, bootstrap, checkpoint, initialized-Project idempotency, and fresh-context recovery acceptance; the final evidence-only candidate preserved the Plugin tree, passed CI runs `33598513827` and `33598628565`, and passed a tag-based fresh installation smoke. The short-lived release branch remains a temporary evidence carrier only and is not a second continuity authority. - -## README entry architecture - -The README front section is now deliberately layered before architecture material: - -1. `Start Here` / `从这里开始` — minimal user actions for personal-ChatGPT availability, installation in compatible Agent environments, normal continuation, and upgrading the Agnir used by the Project; -2. `Agnir Project Instructions` — canonical Agent activation/operation guidance for this repository; -3. `What Svif Adds to a Project` / `Svif 会给 Project 增加什么` — concrete first-use Project surface, with `AGENTS.md` / `README.md` visibly marked as non-destructive EDIT/add-entry-only and `AGNIR.yaml` / `.agnir/` / `SVIF.yaml` as founding ADD surfaces; -4. `Architecture Diagram` / `架构图` — static product architecture plus the first-use boundary; -5. `Runtime / Operation Flow` / `运行流程` — post-bootstrap runtime behavior, intentionally free of install-mutation labels. - -A genuinely uninitialized Project does **not** require manual Agnir pre-initialization. The active Svif first-use contract remains that the shared Skill establishes founding Agnir continuity plus a matching minimal Svif Project Binding on the repository/filesystem path. Compatible existing artifacts are reused; partial/contradictory artifacts are repair cases; an intentional other Continuity Provider binding is preserved. - -Repository-integrity checks enforce the entry ordering, first-use ADD/EDIT distinction, architecture/runtime separation, and canonical user intents. Durable rationale is recorded in `.agnir/evidence/2026-09-01-readme-information-architecture.md` and `.agnir/decisions.md`. `README.md` and `README.zh-CN.md` remain synchronized entry points. - -## Runtime baseline - -- Externally driven execution uses `Orchestrator.begin()` / `Orchestrator.complete()`. -- Untrusted model/result payloads cannot self-grant protected authority. -- External actuation requires successful verification for the exact subject plus applicable trusted authority. -- External success requires independent observation before checkpoint. -- Agnir durable continuity remains Project-owned and execution-surface-neutral. -- Detached commits, PR checkouts, temporary branches, forks, mirrors, or other non-authoritative copies do not silently become canonical checkpoint targets. - -`tests/test_founding_e2e.py` proves the credential-free orchestration loop through Agnir continuity load -> Orchestrator -> ChatGPT bridge -> trusted authority -> exact-subject Cloudflare actuation through injected fake transport -> independent observation -> Agnir checkpoint -> continuity reload/resume. This is not live Cloudflare production-delivery evidence. - -## Plugin MVP and first-use onboarding - -Svif has a released **Skills-only `v0.2.0-preview.1` Plugin Preview** under `plugin/`: - -- `plugin/plugin.json` — portable Agent Plugins `1.0.0` manifest; -- `plugin/skills/svif/SKILL.md` — shared Svif Project-orchestration Skill; -- `plugin/.codex-plugin/plugin.json` — OpenAI/Codex manifest reusing the same Skill and carrying public-listing metadata; -- `.agents/plugins/marketplace.json` — repository-backed Preview catalog for Codex CLI and ChatGPT desktop/Codex; -- `plugin/README.md` — submission, installation, review-case, and evidence-boundary guidance. - -First-use onboarding is a Svif product responsibility. For a genuinely uninitialized ordinary Project, the shared Skill establishes one stable Project identity, Agnir Core `0.1` / `repository-filesystem/0.1` continuity, a matching minimal `project-binding/0.2` `SVIF.yaml`, then fresh-activates and continues the original task. Partial/broken Agnir/Svif artifacts remain repair cases, and a Project intentionally bound to another Continuity Provider is not overwritten with Agnir. - -Repository checks can prove package/conformance, runtime, repository integrity, Agnir discovery guardrails, distribution metadata consistency, documentation claim boundaries, and first-use bootstrap regression behavior. They do **not** prove supported-client installation, OpenAI review, universal-directory publication, or personal ChatGPT invocation. - -## Repository Preview distribution status - -The copy-ready user intent remains exactly: +- Existing public/personal ChatGPT publication work remains externally blocked at publisher verification/payment-method eligibility; this is not a runtime defect. +- Live Cloudflare delivery remains disabled unless explicitly authorized. -`Install and enable Svif for this Project: https://github.com/iorLab/svif` +## Real Agnir stable migration under validation — 2026-09-03 -The installer owns fixed-tag resolution, marketplace registration, client-capability checks, first-use bootstrap, and evidence. The released Preview supports Codex CLI and ChatGPT desktop/Codex through the repository marketplace fixed to immutable tag `v0.2.0-preview.1`. Moving `main` is not a released Preview, and ChatGPT Web/mobile cannot install this repository Preview through the prompt alone. +Captured authoritative Svif baseline before migration: `main@dac058789a27f32f4ed1949874c1954f31f12bd8`. -## Personal ChatGPT public distribution status +Before migration, Svif consumed Agnir Core `0.1`, `repository-filesystem/0.1`, and published Agnir repository release `v0.1.1` at `e9712357ab590e5c1e5357b3cf3219d07d789aff`. -The primary ChatGPT audience is individual/personal users. The mature consumer path is: +This migration lineage now declares: -`individual ChatGPT user -> universal Plugins Directory -> install -> invoke Svif in normal ChatGPT use` +- Agnir Core compatibility `0.2`; +- discovery profile `repository-filesystem/0.2`; +- unchanged Project identity `urn:svif:project:svif-core`; +- logical Continuity Lineage `urn:svif:lineage:agnir-v0.2.0-stable-migration`; +- VCS selector binding `refs/heads/migration/agnir-v0.2.0-stable` as backend selection metadata, not lineage identity; +- unchanged memory locators `.agnir/state.md`, `.agnir/next-actions.md`, `.agnir/decisions.md`, `.agnir/evidence/`; +- published Agnir stable package `v0.2.0` at immutable revision `fc84095ed5d500be9e1b43a4af0e93356571bbd4` as operational provenance; +- `SVIF.yaml` continuity binding updated to compatibility `0.2` / profile `repository-filesystem/0.2` while keeping `project-binding/0.2` and the same Project identity. -Svif is not publicly listed yet. The repository-side Skills-only package is aligned to the current OpenAI public-submission route, but the real publisher flow is externally blocked before individual developer verification because the Platform requires an accepted default payment method. This is an account/publisher eligibility blocker, not evidence of a Svif package, Skill, Orchestrator, or runtime defect. +The preceding preparatory commit `ddaee058efe4c8381f60f5a2ebcae0de9ee9203d` brought the previously real-consumer-validated dual-line Agnir adapter and lineage tests onto the current Svif baseline without changing Project compatibility. This migration commit changes Project truth only after that support exists. -Do not weaken the Skills-only package, add MCP merely to escape this gate, invent billing identity, or claim review/publication/install success while publisher verification is blocked. ChatGPT Web remains a first-class target. MCP/App packaging is a later capability increment, not a release gate. +This is an explicit incompatible Core migration, not the earlier compatible operational upgrade from Agnir repository `0.1.0` to `0.1.1`. Existing Decisions and Evidence remain durable history; unrelated Svif product/distribution obligations remain active. -## Current resume point +## Migration acceptance boundary -1. Preserve the temporary release branch and its final tip until the Principal explicitly confirms cleanup; then delete the local and remote branch without changing the tag or released `main` history. -2. Keep the later public/personal ChatGPT submission path separate: resolve the publisher gate, submit the same Skills-only package, explicitly Publish after approval, then validate the universal Plugins Directory and personal ChatGPT Web. -3. If the Preview requires a fix, create `v0.2.0-preview.2`; never move the released `v0.2.0-preview.1` tag. -4. Keep live Cloudflare delivery disabled unless explicitly authorized. +This temporary branch is not authoritative main. It must not silently checkpoint branch-local State back onto `main`. Before acceptance, the branch must converge all product surfaces that still encode the old founding Core/profile, including the shared Plugin Skill, first-use bootstrap tests, runtime/provider tests, repository checks, and bilingual documentation. The exact migration candidate must pass Svif CI and fresh resume. Only then may the accepted Project/package result be reconciled to main using target-owned main continuity and one coherent target publication. -## Evidence checkpoints +## Existing product obligations preserved -- Repository Preview release: `.agnir/evidence/2026-09-02-svif-v0.2.0-preview.1-release.md`; authoritative-main/tag identity, GitHub Prerelease, CI, and tag-based installation smoke are observed. -- Repository Preview candidate acceptance: `.agnir/evidence/2026-09-02-svif-v0.2.0-preview.1-candidate.md`; static checks, GitHub CI, and immutable-SHA Codex CLI plus ChatGPT desktop/Codex acceptance passed before release. -- Agnir `v0.1.1` compatible operational upgrade candidate: `.agnir/evidence/2026-09-02-agnir-v0.1.1-compatible-upgrade.md`; stable tag `e9712357ab590e5c1e5357b3cf3219d07d789aff`, Core/profile unchanged. -- README audience split / first-use Project surface: `.agnir/evidence/2026-09-01-readme-information-architecture.md`. -- Previous Agnir `v0.1.0` compatible operational upgrade: `.agnir/evidence/2026-09-01-agnir-v0.1.0-compatible-upgrade.md`; upgrade revision `c7cd42b6e94556a630570a54e22c72acc97f3ecf`, run `33466389590` success. -- Plugin first-use bootstrap fix: `.agnir/evidence/2026-08-31-plugin-first-use-bootstrap-fix.md`; final behavior baseline `b90d1f8976b0e03d2c5a3b70c9bbb4b032c37724`, run `33384858568` success. -- Personal ChatGPT distribution / publisher-gate checkpoint: `.agnir/evidence/2026-08-31-personal-chatgpt-distribution-checkpoint.md`. -- Plugin MVP hardening checkpoint: `.agnir/evidence/2026-08-31-plugin-mvp-hardening-checkpoint.md`. -- README/localization baseline: `.agnir/evidence/2026-08-28-readme-diagram-localization-checkpoint.md`. -- Founding E2E: `.agnir/evidence/2026-08-28-founding-e2e.md`, run `33143308949` success. +- `plugin/skills/svif/SKILL.md` remains the single-sourced orchestration workflow. +- A genuinely uninitialized Project must still be bootstrapped by Svif without requiring a separate Agnir initialization prompt. +- Existing Project content and instructions must be preserved; partial/broken continuity is repair, not clean bootstrap; another intentionally selected Continuity Provider must not be overwritten. +- Repository Preview `v0.2.0-preview.1` remains immutable; any Preview fix uses a new tag. +- Personal ChatGPT Web remains a first-class target. +- Repository CI/package validation is not personal ChatGPT installation evidence. +- `main` remains the only long-lived branch. -`.agnir/decisions.md` is authoritative for architecture and distribution decisions; `.agnir/next-actions.md` is the canonical ordered resume plan. +`.agnir/next-actions.md` is the canonical ordered resume plan for completing and validating this migration. diff --git a/AGNIR.yaml b/AGNIR.yaml index 3c94e5b..5c9fe26 100644 --- a/AGNIR.yaml +++ b/AGNIR.yaml @@ -1,12 +1,15 @@ agnir: - version: "0.1" - discovery_profile: "repository-filesystem/0.1" + version: "0.2" + discovery_profile: "repository-filesystem/0.2" project: identity: "urn:svif:project:svif-core" profiles: - generic +continuity: + lineage: "urn:svif:lineage:agnir-v0.2.0-stable-migration" + memory: state: ".agnir/state.md" next_actions: ".agnir/next-actions.md" @@ -20,8 +23,14 @@ extensions: agnir/repository: canonical: "iorLab/svif" authoritative_ref: "main" + agnir/vcs: + branch_continuity: "lineage-bound" + integration_reconciliation: "required" + lineage_binding: + kind: "vcs-ref" + selector: "refs/heads/migration/agnir-v0.2.0-stable" agnir/operations: distribution: "agnir-agent-skill" - release: "0.1.1" + release: "0.2.0" source: "iorLab/agnir" - applied_revision: "e9712357ab590e5c1e5357b3cf3219d07d789aff" + applied_revision: "fc84095ed5d500be9e1b43a4af0e93356571bbd4" diff --git a/SVIF.yaml b/SVIF.yaml index d7e360f..a5cffaf 100644 --- a/SVIF.yaml +++ b/SVIF.yaml @@ -8,8 +8,8 @@ project: bindings: continuity: provider: "agnir" - compatibility: "0.1" - profile: "repository-filesystem/0.1" + compatibility: "0.2" + profile: "repository-filesystem/0.2" config: discovery: "AGNIR.yaml" activation: "AGENTS.md -> README.md / Agnir Project Instructions -> AGNIR.yaml" diff --git a/tests/test_agnir_stable_migration.py b/tests/test_agnir_stable_migration.py new file mode 100644 index 0000000..566411a --- /dev/null +++ b/tests/test_agnir_stable_migration.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +import re +import unittest +from pathlib import Path + +from svif.continuity.agnir import AgnirFilesystemContinuityProvider + + +ROOT = Path(__file__).resolve().parents[1] +PROJECT = "urn:svif:project:svif-core" + + +class PublishedAgnirStableMigrationTests(unittest.TestCase): + def test_repository_self_consumes_published_core_0_2(self) -> None: + agnir = (ROOT / "AGNIR.yaml").read_text(encoding="utf-8") + svif = (ROOT / "SVIF.yaml").read_text(encoding="utf-8") + + self.assertIn('version: "0.2"', agnir) + self.assertIn('discovery_profile: "repository-filesystem/0.2"', agnir) + self.assertIn('release: "0.2.0"', agnir) + self.assertIn( + 'applied_revision: "fc84095ed5d500be9e1b43a4af0e93356571bbd4"', + agnir, + ) + self.assertIn('compatibility: "0.2"', svif) + self.assertIn('profile: "repository-filesystem/0.2"', svif) + + provider = AgnirFilesystemContinuityProvider( + ROOT, + expected_core_version="0.2", + expected_profile="repository-filesystem/0.2", + ) + snapshot = provider.load(PROJECT) + lineage = provider.resolve_lineage(PROJECT) + + self.assertEqual(snapshot.project_identity, PROJECT) + self.assertTrue(lineage) + self.assertIn("Svif", snapshot.state or "") + self.assertIn("Svif", snapshot.next_actions or "") + + lineage_match = re.search(r"^\s{2}lineage:\s*\"([^\"]+)\"", agnir, re.MULTILINE) + selector_match = re.search(r"^\s{6}selector:\s*\"([^\"]+)\"", agnir, re.MULTILINE) + self.assertIsNotNone(lineage_match) + self.assertIsNotNone(selector_match) + self.assertNotEqual(lineage_match.group(1), selector_match.group(1)) + + def test_migration_preserves_project_identity_and_memory_locators(self) -> None: + agnir = (ROOT / "AGNIR.yaml").read_text(encoding="utf-8") + for marker in ( + 'identity: "urn:svif:project:svif-core"', + 'state: ".agnir/state.md"', + 'next_actions: ".agnir/next-actions.md"', + 'decisions: ".agnir/decisions.md"', + 'evidence: ".agnir/evidence/"', + ): + self.assertIn(marker, agnir) + + +if __name__ == "__main__": + unittest.main() From 8aaed18dbbbbb857873500505ae941289f0029c4 Mon Sep 17 00:00:00 2001 From: mattamior Date: Thu, 3 Sep 2026 14:38:01 +0800 Subject: [PATCH 3/6] test: converge Svif current binding guards on Agnir 0.2 --- .agnir/next-actions.md | 17 ++- .agnir/state.md | 16 +-- SVIF.yaml | 3 + checks/check_repository.py | 162 ++++++++++++++++++--------- tests/test_plugin_agnir_discovery.py | 43 +++++-- tests/test_plugin_package.py | 41 ++++--- 6 files changed, 186 insertions(+), 96 deletions(-) mode change 100644 => 100755 checks/check_repository.py diff --git a/.agnir/next-actions.md b/.agnir/next-actions.md index f9be8c2..1d36134 100644 --- a/.agnir/next-actions.md +++ b/.agnir/next-actions.md @@ -1,14 +1,16 @@ # Svif Next Actions -Svif is performing a real downstream migration from published Agnir `v0.1.1` / Core-profile `0.1` to published stable Agnir `v0.2.0` / Core-profile `0.2` on temporary lineage `urn:svif:lineage:agnir-v0.2.0-stable-migration`. Authoritative `main` remains unchanged until the exact migration result is fully validated and reconciled. +Svif is performing a real downstream migration from published Agnir `v0.1.1` / Core-profile `0.1` to published stable Agnir `v0.2.0` / **Agnir Core 0.2** / `repository-filesystem/0.2` on temporary lineage `urn:svif:lineage:agnir-v0.2.0-stable-migration`. Authoritative `main` remains unchanged until the exact migration result is fully validated and reconciled. -1. **Converge every active Svif surface onto Agnir Core/profile `0.2`.** Update the shared Plugin Skill first-use bootstrap/activation/current-binding text, first-use/bootstrap tests, repository checks, bilingual README and repository map where they describe the founding Agnir compatibility line. Do not change Orchestrator/Execution/Capability architecture merely because the Continuity Provider compatibility changed. +1. **Converge current-Project guards onto Agnir Core/profile `0.2`.** Update repository integrity and current-binding tests to accept the explicit logical lineage + selector binding. Preserve the released Skills-only Preview.1 first-use bootstrap baseline at Core/profile `0.1`; changing that onboarding contract requires a later intentional distribution release, not this Project migration. 2. **Validate the migrated Svif Project as a real published-stable downstream consumer.** Require Project identity `urn:svif:project:svif-core`, logical lineage presence, selector != lineage identity, unchanged durable memory locators, stable Agnir provenance `v0.2.0` / `fc84095...`, Core/profile `0.2` discovery, checkpoint/resume, founding E2E, Plugin package/first-use behavior, repository integrity, contracts and full suite. -3. **Pressure upgrade usability rather than hiding friction.** Any failure attributable to Agnir `v0.2.0` migration semantics, Skill contract, discovery, lineage binding or recovery should be treated as Agnir product evidence and repaired in Agnir `v0.2.x` when appropriate rather than papered over in Svif. -4. **Construct a target-reconciled main candidate only after the migration branch is fully green.** Main must receive the accepted Project/package result while creating/preserving its own authoritative logical lineage and `refs/heads/main` binding. Do not copy temporary migration-line State/Next as automatic target truth, and do not advance main before candidate validation and fresh source/target stale checks. +3. **Pressure upgrade usability rather than hiding friction.** Any failure attributable to Agnir `v0.2.0` migration semantics, discovery, lineage binding or recovery should be treated as Agnir product evidence and repaired in Agnir `v0.2.x` when appropriate rather than papered over in Svif. +4. **Construct a target-reconciled main candidate only after the migration branch is fully green.** Main must receive the accepted Project/package result while establishing target-owned authoritative logical lineage and `refs/heads/main` binding. Do not copy temporary migration-line State/Next as automatic target truth, and do not advance main before candidate validation and fresh source/target stale checks. 5. **After authoritative-main verification, record this as Agnir v1 downstream-upgrade evidence.** The receipt must distinguish synthetic fixtures and earlier pre-release experiments from this published `v0.1.1` -> published `v0.2.0` real Project upgrade. -6. **Preserve existing Svif distribution obligations.** Keep `v0.2.0-preview.1` immutable; continue the separate personal ChatGPT publisher-verification path; use a new Preview tag for any Svif release fix; keep live Cloudflare delivery disabled unless explicitly authorized. -7. **Retire temporary migration/validation refs after accepted reconciliation when a safe delete-ref path is available.** `main` remains the only long-lived Svif branch. +6. **Preserve the released Repository Preview and distribution evidence.** Keep `v0.2.0-preview.1` immutable and preserve its **immutable candidate**, real **Codex CLI**, and **ChatGPT desktop/Codex** acceptance evidence. Any Preview fix uses a new tag such as `v0.2.0-preview.2`. +7. **Continue the separate public/personal ChatGPT path when the publisher gate is resolvable.** Submit the supported Skills-only package to the **universal Plugins Directory**, explicitly Publish after approval, then validate a real **individual-user ChatGPT surface**, with **ChatGPT Web** remaining a first-class target. +8. Keep live Cloudflare delivery disabled unless explicitly authorized. +9. **Retire temporary migration/validation refs after accepted reconciliation when a safe delete-ref path is available.** `main` remains the only long-lived Svif branch. ## Captured migration receipts @@ -16,6 +18,8 @@ Svif is performing a real downstream migration from published Agnir `v0.1.1` / C - previous Agnir operational release: `v0.1.1` -> `e9712357ab590e5c1e5357b3cf3219d07d789aff`; - target Agnir stable release: `v0.2.0` -> `fc84095ed5d500be9e1b43a4af0e93356571bbd4`; - preparatory dual-line adapter/tests commit: `ddaee058efe4c8381f60f5a2ebcae0de9ee9203d`; +- migration Project-truth commit: `eac2ab0dd70695d972b99afad084614eae26c77c`; +- initial Draft PR #6 run: `33723726831`; portable contracts success, failures limited to old current-binding/repository guards and preserved distribution-marker assertions; - migration branch selector: `refs/heads/migration/agnir-v0.2.0-stable`; - migration logical lineage: `urn:svif:lineage:agnir-v0.2.0-stable-migration`. @@ -26,4 +30,5 @@ Svif is performing a real downstream migration from published Agnir `v0.1.1` / C - logical lineage identity != VCS selector != commit/checkpoint receipt. - Core `0.1` -> `0.2` is explicit migration, not compatible upgrade. - Source/migration continuity is reconciliation input, not automatic target-main truth. +- The released Preview.1 bootstrap baseline and the Svif repository's current self-host binding are separate versioned facts. - Svif product architecture remains Orchestrator + Continuity Provider + Execution Surface + Capability Provider. diff --git a/.agnir/state.md b/.agnir/state.md index 57e7048..be89ded 100644 --- a/.agnir/state.md +++ b/.agnir/state.md @@ -1,6 +1,6 @@ # Svif Current State -Svif is the authoritative active Project orchestration product in `iorLab/svif`. The canonical long-lived ref remains `main`; this temporary migration lineage exists only to validate a real downstream upgrade of Svif's founding Agnir Continuity Provider from published stable `v0.1.1` / Core `0.1` to published stable `v0.2.0` / Core `0.2`. +Svif is the authoritative active **Project orchestration product** in `iorLab/svif`. The canonical long-lived ref remains `main`; this temporary migration lineage exists only to validate a real downstream upgrade of Svif's founding Agnir Continuity Provider from published stable `v0.1.1` / Core `0.1` to published stable `v0.2.0` / Core `0.2`. The former `iorLab/svif-cloudflare-reference` project is retired. ## Product architecture @@ -14,14 +14,16 @@ Svif continues to coordinate the same four first-class components: Orchestrator - Software Delivery profile: `software-delivery/0.2`. - Capability Adapter: `capability-adapter/0.2`. - Evidence record: `evidence-record/0.2`. +- The released **Plugin MVP** / Repository Preview remains immutable and its first-use bootstrap contract stays on its published Agnir Core/profile `0.1` baseline until a later Svif distribution release intentionally changes that onboarding contract. - Existing public/personal ChatGPT publication work remains externally blocked at publisher verification/payment-method eligibility; this is not a runtime defect. - Live Cloudflare delivery remains disabled unless explicitly authorized. +- `README.md` and `README.zh-CN.md` remain the synchronized user/Agent entry points. ## Real Agnir stable migration under validation — 2026-09-03 Captured authoritative Svif baseline before migration: `main@dac058789a27f32f4ed1949874c1954f31f12bd8`. -Before migration, Svif consumed Agnir Core `0.1`, `repository-filesystem/0.1`, and published Agnir repository release `v0.1.1` at `e9712357ab590e5c1e5357b3cf3219d07d789aff`. +Before migration, the Svif Project itself consumed Agnir Core `0.1`, `repository-filesystem/0.1`, and published Agnir repository release `v0.1.1` at `e9712357ab590e5c1e5357b3cf3219d07d789aff`. This migration lineage now declares: @@ -32,20 +34,20 @@ This migration lineage now declares: - VCS selector binding `refs/heads/migration/agnir-v0.2.0-stable` as backend selection metadata, not lineage identity; - unchanged memory locators `.agnir/state.md`, `.agnir/next-actions.md`, `.agnir/decisions.md`, `.agnir/evidence/`; - published Agnir stable package `v0.2.0` at immutable revision `fc84095ed5d500be9e1b43a4af0e93356571bbd4` as operational provenance; -- `SVIF.yaml` continuity binding updated to compatibility `0.2` / profile `repository-filesystem/0.2` while keeping `project-binding/0.2` and the same Project identity. +- `SVIF.yaml` continuity binding updated to compatibility `0.2` / profile `repository-filesystem/0.2` and explicitly carries the same lineage/selector binding while keeping `project-binding/0.2` and the same Project identity. -The preceding preparatory commit `ddaee058efe4c8381f60f5a2ebcae0de9ee9203d` brought the previously real-consumer-validated dual-line Agnir adapter and lineage tests onto the current Svif baseline without changing Project compatibility. This migration commit changes Project truth only after that support exists. +The preparatory commit `ddaee058efe4c8381f60f5a2ebcae0de9ee9203d` brought the previously real-consumer-validated dual-line Agnir adapter and lineage tests onto the current Svif baseline without changing Project compatibility. Migration commit `eac2ab0dd70695d972b99afad084614eae26c77c` then changed branch-local Project truth to the published stable Core/profile `0.2` line. Initial Draft PR #6 CI proved portable contracts green and localized the remaining failures to guards that still asserted the old current binding; no adapter/Core 0.2 runtime defect was observed. -This is an explicit incompatible Core migration, not the earlier compatible operational upgrade from Agnir repository `0.1.0` to `0.1.1`. Existing Decisions and Evidence remain durable history; unrelated Svif product/distribution obligations remain active. +This is an explicit incompatible Core migration, not the earlier compatible operational upgrade from Agnir repository `0.1.0` to `0.1.1`. Existing Decisions and historical Evidence remain durable history; unrelated Svif product/distribution obligations remain active. ## Migration acceptance boundary -This temporary branch is not authoritative main. It must not silently checkpoint branch-local State back onto `main`. Before acceptance, the branch must converge all product surfaces that still encode the old founding Core/profile, including the shared Plugin Skill, first-use bootstrap tests, runtime/provider tests, repository checks, and bilingual documentation. The exact migration candidate must pass Svif CI and fresh resume. Only then may the accepted Project/package result be reconciled to main using target-owned main continuity and one coherent target publication. +This temporary branch is not authoritative main. It must not silently checkpoint branch-local State back onto `main`. Before acceptance, current-project guards must converge to Core/profile `0.2` while released Preview.1 bootstrap guards remain explicitly `0.1`. The exact migration candidate must pass repository integrity, portable contracts, runtime/unit tests, fresh discovery/resume, founding E2E and Plugin regression pressure. Only then may the accepted Project/package result be reconciled to main using target-owned main continuity and one coherent target publication. ## Existing product obligations preserved - `plugin/skills/svif/SKILL.md` remains the single-sourced orchestration workflow. -- A genuinely uninitialized Project must still be bootstrapped by Svif without requiring a separate Agnir initialization prompt. +- A genuinely uninitialized Project using the released Preview.1 contract must still be bootstrapped by Svif without requiring a separate Agnir initialization prompt. - Existing Project content and instructions must be preserved; partial/broken continuity is repair, not clean bootstrap; another intentionally selected Continuity Provider must not be overwritten. - Repository Preview `v0.2.0-preview.1` remains immutable; any Preview fix uses a new tag. - Personal ChatGPT Web remains a first-class target. diff --git a/SVIF.yaml b/SVIF.yaml index a5cffaf..dc8e946 100644 --- a/SVIF.yaml +++ b/SVIF.yaml @@ -13,6 +13,8 @@ bindings: config: discovery: "AGNIR.yaml" activation: "AGENTS.md -> README.md / Agnir Project Instructions -> AGNIR.yaml" + lineage: "urn:svif:lineage:agnir-v0.2.0-stable-migration" + vcs_selector: "refs/heads/migration/agnir-v0.2.0-stable" execution: [] capabilities: [] @@ -42,6 +44,7 @@ checks: portable_contracts: "conformance/check_contracts.py" runtime_kernel: "tests/test_runtime.py" agnir_continuity: "tests/test_agnir_continuity.py" + agnir_stable_migration: "tests/test_agnir_stable_migration.py" chatgpt_surface: "tests/test_chatgpt_surface.py" cloudflare_capability: "tests/test_cloudflare_capability.py" founding_e2e: "tests/test_founding_e2e.py" diff --git a/checks/check_repository.py b/checks/check_repository.py old mode 100644 new mode 100755 index dc512e4..931f5bd --- a/checks/check_repository.py +++ b/checks/check_repository.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 from __future__ import annotations +import re import sys from pathlib import Path @@ -18,6 +19,29 @@ def require_text(text: str, needles: list[str], label: str) -> None: fail(f"{label} missing required product-architecture marker: {needle}") +def parse_scalar_paths(text: str) -> dict[tuple[str, ...], str | None]: + """Parse the small nested scalar YAML subset used by Svif/Agnir bindings.""" + values: dict[tuple[str, ...], str | None] = {} + stack: list[tuple[int, str]] = [] + for raw in text.splitlines(): + if not raw.strip() or raw.lstrip().startswith("#") or raw.lstrip().startswith("-"): + continue + indent = len(raw) - len(raw.lstrip(" ")) + match = re.match(r"^\s*([A-Za-z0-9_./-]+):\s*(.*?)\s*$", raw) + if not match: + continue + key, scalar = match.groups() + while stack and indent <= stack[-1][0]: + stack.pop() + if scalar == "": + stack.append((indent, key)) + continue + if len(scalar) >= 2 and scalar[0] == scalar[-1] and scalar[0] in {'"', "'"}: + scalar = scalar[1:-1] + values[tuple([item[1] for item in stack] + [key])] = None if scalar in {"null", "~"} else scalar + return values + + def require_readme_entry_guide( path: str, *, @@ -85,8 +109,6 @@ def require_readme_diagrams( for marker in runtime_forbidden_markers: if marker in runtime_text: fail(f"{path} Runtime / Operation Flow must not include installation mutation marker: {marker}") - - # Keep README Mermaid syntax deliberately conservative for GitHub rendering. diagram_text = architecture_text + runtime_text for risky in ("<-->", ".-> T", "\\n"): if risky in diagram_text: @@ -173,37 +195,81 @@ def require_full_repository_tree() -> None: def require_agnir_activation() -> None: agents = (ROOT / "AGENTS.md").read_text(encoding="utf-8") readme = (ROOT / "README.md").read_text(encoding="utf-8") - agnir = (ROOT / "AGNIR.yaml").read_text(encoding="utf-8") - svif = (ROOT / "SVIF.yaml").read_text(encoding="utf-8") + agnir_text = (ROOT / "AGNIR.yaml").read_text(encoding="utf-8") + svif_text = (ROOT / "SVIF.yaml").read_text(encoding="utf-8") + agnir = parse_scalar_paths(agnir_text) + svif = parse_scalar_paths(svif_text) require_text(agents, ["Agnir Project Instructions", "README.md", "AGNIR.yaml"], "AGENTS.md") if ".agnir/state.md" in agents or ".agnir/next-actions.md" in agents: fail("AGENTS.md must remain a locator and must not duplicate durable Project memory") - require_text(readme, [ - "## Agnir Project Instructions", - "authorized Project Entry Point", + require_text( + readme, + [ + "## Agnir Project Instructions", + "authorized Project Entry Point", + "AGNIR.yaml", + "Current State", + "Next Actions", + "Decisions", + "Evidence", + "Project root -> AGENTS.md -> README.md / Agnir Project Instructions -> AGNIR.yaml -> declared durable memory", + ], + "README.md Agnir activation", + ) + + project_identity = agnir.get(("project", "identity")) + svif_project_identity = svif.get(("project", "identity")) + if not project_identity or project_identity != svif_project_identity: + fail("AGNIR.yaml and SVIF.yaml must identify the same non-empty Project") + + version = agnir.get(("agnir", "version")) + profile = agnir.get(("agnir", "discovery_profile")) + compatibility = svif.get(("bindings", "continuity", "compatibility")) + bound_profile = svif.get(("bindings", "continuity", "profile")) + provider = svif.get(("bindings", "continuity", "provider")) + + if provider != "agnir": + fail("Svif founding continuity binding must identify provider agnir") + supported = { + "0.1": "repository-filesystem/0.1", + "0.2": "repository-filesystem/0.2", + } + if version not in supported: + fail(f"Svif repository selected unsupported Agnir Core compatibility: {version!r}") + if profile != supported[version]: + fail(f"Agnir Core {version} must use profile {supported[version]}, got {profile!r}") + if compatibility != version or bound_profile != profile: + fail("SVIF.yaml Continuity Provider compatibility/profile must match AGNIR.yaml") + + require_text( + agnir_text, + [ + 'state: ".agnir/state.md"', + 'next_actions: ".agnir/next-actions.md"', + 'decisions: ".agnir/decisions.md"', + 'evidence: ".agnir/evidence/"', + ], "AGNIR.yaml", - "Current State", - "Next Actions", - "Decisions", - "Evidence", - "Project root -> AGENTS.md -> README.md / Agnir Project Instructions -> AGNIR.yaml -> declared durable memory", - ], "README.md Agnir activation") - - require_text(agnir, [ - 'version: "0.1"', - 'discovery_profile: "repository-filesystem/0.1"', - 'state: ".agnir/state.md"', - 'next_actions: ".agnir/next-actions.md"', - 'decisions: ".agnir/decisions.md"', - 'evidence: ".agnir/evidence/"', - ], "AGNIR.yaml") - require_text(svif, [ - 'compatibility: "0.1"', - 'profile: "repository-filesystem/0.1"', - 'activation: "AGENTS.md -> README.md / Agnir Project Instructions -> AGNIR.yaml"', - ], "SVIF.yaml Agnir binding") + ) + require_text( + svif_text, + ['activation: "AGENTS.md -> README.md / Agnir Project Instructions -> AGNIR.yaml"'], + "SVIF.yaml Agnir binding", + ) + + if version == "0.2": + lineage = agnir.get(("continuity", "lineage")) + bound_lineage = svif.get(("bindings", "continuity", "config", "lineage")) + selector = agnir.get(("extensions", "agnir/vcs", "lineage_binding", "selector")) + bound_selector = svif.get(("bindings", "continuity", "config", "vcs_selector")) + if not lineage or bound_lineage != lineage: + fail("Core 0.2 requires one logical lineage and matching Svif provider binding") + if not selector or bound_selector != selector: + fail("Core 0.2 VCS validation requires matching durable selector binding") + if selector == lineage: + fail("VCS selector must remain distinct from logical lineage identity") for path in (".agnir/state.md", ".agnir/next-actions.md", ".agnir/decisions.md", ".agnir/evidence"): if not (ROOT / path).exists(): @@ -252,10 +318,8 @@ def main() -> None: upgrade_prompt="Upgrade the Agnir used by this Project to the latest stable release: https://github.com/iorLab/agnir", normal_use_marker="No recurring Svif installation prompt is required.", surface_markers=( - "[EDIT: add entry only]", - "[ADD] founding Agnir discovery anchor", - "[ADD] Project-owned durable continuity", - "[ADD] Svif Project Binding", + "[EDIT: add entry only]", "[ADD] founding Agnir discovery anchor", + "[ADD] Project-owned durable continuity", "[ADD] Svif Project Binding", "intentionally bound to another Continuity Provider", ), ) @@ -268,10 +332,8 @@ def main() -> None: upgrade_prompt="把这个 Project 使用的 Agnir 升级到最新稳定版:https://github.com/iorLab/agnir", normal_use_marker="不需要在每次对话里重复 Svif 安装提示。", surface_markers=( - "[编辑:仅添加入口]", - "[新增] founding Agnir discovery anchor", - "[新增] Project 自己拥有的 durable continuity", - "[新增] Svif Project Binding", + "[编辑:仅添加入口]", "[新增] founding Agnir discovery anchor", + "[新增] Project 自己拥有的 durable continuity", "[新增] Svif Project Binding", "明确绑定其他 Continuity Provider", ), ) @@ -279,15 +341,9 @@ def main() -> None: "README.md", ("## Architecture Diagram", "## Runtime / Operation Flow"), architecture_markers=( - "non-destructive first-use setup", - "EDIT: add activation locator only", - "EDIT: add Agnir instructions only", - "ADD: founding continuity", - "ADD: Project binding", - "Svif Orchestrator", - "Continuity Provider", - "Execution integration", - "Capability Providers", + "non-destructive first-use setup", "EDIT: add activation locator only", + "EDIT: add Agnir instructions only", "ADD: founding continuity", "ADD: Project binding", + "Svif Orchestrator", "Continuity Provider", "Execution integration", "Capability Providers", ), runtime_forbidden_markers=("EDIT: add", "ADD: founding", "ADD: Project binding"), ) @@ -295,15 +351,9 @@ def main() -> None: "README.zh-CN.md", ("## 架构图", "## 运行流程"), architecture_markers=( - "非破坏性 first-use setup", - "编辑:仅添加 activation locator", - "编辑:仅添加 Agnir instructions", - "新增:founding continuity", - "新增:Project binding", - "Svif 编排器", - "项目连续性提供者", - "执行环境适配层", - "能力提供层", + "非破坏性 first-use setup", "编辑:仅添加 activation locator", + "编辑:仅添加 Agnir instructions", "新增:founding continuity", "新增:Project binding", + "Svif 编排器", "项目连续性提供者", "执行环境适配层", "能力提供层", ), runtime_forbidden_markers=("编辑:仅添加", "新增:founding", "新增:Project binding"), ) @@ -341,8 +391,8 @@ def main() -> None: cloudflare = (ROOT / "src/svif/capabilities/cloudflare.py").read_text(encoding="utf-8") require_text(cloudflare, [ - 'provider_id = "cloudflare.workers"', "class CloudflareWorkersTransport", "class CloudflareWorkersCapabilityProvider", - "def actuate(", "def observe(", + 'provider_id = "cloudflare.workers"', "class CloudflareWorkersTransport", + "class CloudflareWorkersCapabilityProvider", "def actuate(", "def observe(", ], "src/svif/capabilities/cloudflare.py") plugin = (ROOT / "plugin/skills/svif/SKILL.md").read_text(encoding="utf-8") @@ -366,7 +416,7 @@ def main() -> None: "README.zh-CN.md", "Plugin MVP", ], "Agnir state") - print("PASS: Svif product repository integrity, Agnir activation, Plugin packaging, and single-repository architecture baseline") + print("PASS: Svif product repository integrity, coherent Agnir binding, Plugin packaging, and single-repository architecture baseline") if __name__ == "__main__": diff --git a/tests/test_plugin_agnir_discovery.py b/tests/test_plugin_agnir_discovery.py index 8d7d26e..4e52c6e 100644 --- a/tests/test_plugin_agnir_discovery.py +++ b/tests/test_plugin_agnir_discovery.py @@ -1,5 +1,6 @@ from __future__ import annotations +import re import unittest from pathlib import Path @@ -8,6 +9,13 @@ SKILL = ROOT / "plugin" / "skills" / "svif" / "SKILL.md" +def _quoted_scalar(text: str, key: str) -> str: + match = re.search(rf'^\s+{re.escape(key)}:\s+"([^"]+)"\s*$', text, re.MULTILINE) + if match is None: + raise AssertionError(f"missing quoted scalar {key!r}") + return match.group(1) + + class PluginAgnirDiscoveryTests(unittest.TestCase): def test_skill_requires_durable_agent_activation_route_before_discovery(self) -> None: text = SKILL.read_text(encoding="utf-8") @@ -98,10 +106,7 @@ def test_skill_requires_authority_to_select_one_project_root_before_discovery(se "a parent or child Project with its own `AGNIR.yaml` does not make that selected root ambiguous", text, ) - self.assertIn( - "MUST NOT be searched as a replacement", - text, - ) + self.assertIn("MUST NOT be searched as a replacement", text) def test_skill_selects_trusted_profile_before_resolving_discovery_record(self) -> None: text = SKILL.read_text(encoding="utf-8") @@ -282,21 +287,35 @@ def test_skill_surfaces_all_named_agnir_discovery_failures_without_fallback_sear self.assertIn("repair the earliest violated discovery invariant", text) self.assertIn("original authorized Project Entry Point", text) - def test_svif_binding_and_skill_agree_on_current_agnir_identity_and_compatibility(self) -> None: + def test_current_project_binding_can_migrate_without_rewriting_released_bootstrap_baseline(self) -> None: skill = SKILL.read_text(encoding="utf-8") agnir = (ROOT / "AGNIR.yaml").read_text(encoding="utf-8") svif = (ROOT / "SVIF.yaml").read_text(encoding="utf-8") + lineage = _quoted_scalar(svif, "lineage") + selector = _quoted_scalar(svif, "vcs_selector") - self.assertIn('version: "0.1"', agnir) - self.assertIn('discovery_profile: "repository-filesystem/0.1"', agnir) - self.assertIn('identity: "urn:svif:project:svif-core"', agnir) - self.assertIn('compatibility: "0.1"', svif) - self.assertIn('profile: "repository-filesystem/0.1"', svif) + for marker in ( + 'version: "0.2"', + 'discovery_profile: "repository-filesystem/0.2"', + 'identity: "urn:svif:project:svif-core"', + f'lineage: "{lineage}"', + f'selector: "{selector}"', + ): + self.assertIn(marker, agnir) + for marker in ( + 'compatibility: "0.2"', + 'profile: "repository-filesystem/0.2"', + f'lineage: "{lineage}"', + f'vcs_selector: "{selector}"', + ): + self.assertIn(marker, svif) + # The released Skills-only distribution still bootstraps new Projects on + # its published Agnir 0.1 baseline until a later distribution release says otherwise. for marker in ( "Agnir Core `0.1`", - "profile `repository-filesystem/0.1`", - "Project identity `urn:svif:project:svif-core`", + "repository-filesystem/0.1", + 'compatibility `"0.1"`', ): self.assertIn(marker, skill) diff --git a/tests/test_plugin_package.py b/tests/test_plugin_package.py index 52bb679..789b864 100644 --- a/tests/test_plugin_package.py +++ b/tests/test_plugin_package.py @@ -22,6 +22,13 @@ } +def _quoted_scalar(text: str, key: str) -> str: + match = re.search(rf'^\s+{re.escape(key)}:\s+"([^"]+)"\s*$', text, re.MULTILINE) + if match is None: + raise AssertionError(f"missing quoted scalar {key!r}") + return match.group(1) + + def validate_agent_plugins_1_0_manifest(manifest: object) -> tuple[list[str], list[str]]: """Return fatal errors and non-fatal diagnostics using Agent Plugins 1.0 rules. @@ -243,9 +250,7 @@ def test_manifest_validator_does_not_validate_unimplemented_extension_namespaces portable = { "$schema": SCHEMA_ID, "name": "svif", - "extensions": { - "com.example.client": "opaque-to-portable-validator", - }, + "extensions": {"com.example.client": "opaque-to-portable-validator"}, } errors, diagnostics = validate_agent_plugins_1_0_manifest(portable) self.assertEqual(errors, []) @@ -254,10 +259,7 @@ def test_manifest_validator_does_not_validate_unimplemented_extension_namespaces def test_plugin_package_paths_are_contained_within_plugin_root(self) -> None: result = inspect_plugin_filesystem(PLUGIN_ROOT) self.assertEqual(result, { - "plugin_errors": [], - "component_errors": [], - "skipped_skills": [], - "denied_paths": [], + "plugin_errors": [], "component_errors": [], "skipped_skills": [], "denied_paths": [], }) def test_manifest_escape_rejects_whole_plugin(self) -> None: @@ -268,7 +270,6 @@ def test_manifest_escape_rejects_whole_plugin(self) -> None: outside = base / "manifest.json" outside.write_text('{"$schema": "x", "name": "svif"}', encoding="utf-8") (plugin_root / "plugin.json").symlink_to(outside) - result = inspect_plugin_filesystem(plugin_root) self.assertTrue(any("plugin.json resolves outside" in error for error in result["plugin_errors"])) @@ -281,7 +282,6 @@ def test_skills_location_escape_invalidates_only_skill_component_type(self) -> N outside_skills = base / "outside-skills" outside_skills.mkdir() (plugin_root / "skills").symlink_to(outside_skills, target_is_directory=True) - result = inspect_plugin_filesystem(plugin_root) self.assertEqual(result["plugin_errors"], []) self.assertTrue(any("skills fixed component location escapes" in error for error in result["component_errors"])) @@ -296,7 +296,6 @@ def test_escaping_skill_is_skipped_without_rejecting_plugin(self) -> None: outside = base / "outside.md" outside.write_text("outside", encoding="utf-8") (skill_dir / "SKILL.md").symlink_to(outside) - result = inspect_plugin_filesystem(plugin_root) self.assertEqual(result["plugin_errors"], []) self.assertEqual(result["component_errors"], []) @@ -311,7 +310,6 @@ def test_unrelated_escape_is_denied_without_rejecting_plugin(self) -> None: outside = base / "outside.txt" outside.write_text("outside", encoding="utf-8") (plugin_root / "notes.txt").symlink_to(outside) - result = inspect_plugin_filesystem(plugin_root) self.assertEqual(result["plugin_errors"], []) self.assertIn("notes.txt", result["denied_paths"]) @@ -345,15 +343,28 @@ def test_svif_skill_has_required_core_guards(self) -> None: def test_current_agnir_binding_has_no_live_predecessor_ref(self) -> None: agnir = (ROOT / "AGNIR.yaml").read_text(encoding="utf-8") svif = (ROOT / "SVIF.yaml").read_text(encoding="utf-8") + lineage = _quoted_scalar(svif, "lineage") + selector = _quoted_scalar(svif, "vcs_selector") for text in (agnir, svif): self.assertNotIn("predecessor_ref:", text) self.assertNotIn("legacy/zerolocal-v0.1", text) - self.assertIn('discovery_profile: "repository-filesystem/0.1"', agnir) - self.assertIn('provider: "agnir"', svif) - self.assertIn('compatibility: "0.1"', svif) - self.assertIn('profile: "repository-filesystem/0.1"', svif) + for marker in ( + 'version: "0.2"', + 'discovery_profile: "repository-filesystem/0.2"', + f'lineage: "{lineage}"', + f'selector: "{selector}"', + ): + self.assertIn(marker, agnir) + for marker in ( + 'provider: "agnir"', + 'compatibility: "0.2"', + 'profile: "repository-filesystem/0.2"', + f'lineage: "{lineage}"', + f'vcs_selector: "{selector}"', + ): + self.assertIn(marker, svif) def test_svif_project_has_self_describing_agnir_cold_start_route(self) -> None: agents = (ROOT / "AGENTS.md").read_text(encoding="utf-8") From 5b2086bdc61cd5dad8397241565fbbda9592fc88 Mon Sep 17 00:00:00 2001 From: mattamior Date: Thu, 3 Sep 2026 14:43:25 +0800 Subject: [PATCH 4/6] docs: align Svif Skill with stable Agnir 0.2 self-host --- REPOSITORY_TREE.md | 8 +++++--- plugin/skills/svif/SKILL.md | 19 +++++++++++-------- tests/test_agnir_stable_migration.py | 22 ++++++++++++++++++++++ 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/REPOSITORY_TREE.md b/REPOSITORY_TREE.md index 42ed039..7cd6e13 100644 --- a/REPOSITORY_TREE.md +++ b/REPOSITORY_TREE.md @@ -37,6 +37,7 @@ svif/ # Svif 产品主仓库 │ ├── 2026-09-02-agnir-v0.1.1-compatible-upgrade.md # Agnir v0.1.1 compatible operational upgrade 与 immutable provenance │ ├── 2026-09-02-svif-v0.2.0-preview.1-candidate.md # Preview 候选、CI、Codex CLI 与桌面端真实安装验收证据 │ ├── 2026-09-02-svif-v0.2.0-preview.1-release.md # tag、GitHub Prerelease、main CI 与 tag-based 安装烟测证据 +│ ├── 2026-09-03-agnir-v0.2.0-real-downstream-migration.md # Svif 从发布版 Agnir v0.1.1/Core 0.1 迁移到 v0.2.0/Core 0.2 的真实下游证据 │ └── checkpoint-2026-08-28-validation-2.md # Validation 2 的持久 checkpoint 记录 │ ├── .github/ # GitHub 托管侧自动化配置 @@ -49,7 +50,7 @@ svif/ # Svif 产品主仓库 │ ├── runtime.py # Orchestrator 核心:begin/run/complete、验证、权限、reconcile、checkpoint │ ├── continuity/ # Continuity Provider 实现 / 适配层 │ │ ├── __init__.py # continuity 子包入口 -│ │ └── agnir.py # 当前 founding provider:Agnir repository/filesystem continuity +│ │ └── agnir.py # founding Agnir repository/filesystem Continuity Provider;兼容 0.1 并支持当前 0.2 lineage/binding │ ├── execution/ # Execution Surface 桥接层 │ │ ├── __init__.py # execution 子包入口 │ │ └── chatgpt.py # 当前 founding surface:ChatGPT 结构化 begin/complete bridge @@ -89,7 +90,8 @@ svif/ # Svif 产品主仓库 │ ├── tests/ # 可执行产品实现测试 │ ├── test_runtime.py # Orchestrator kernel、authority、verification、lifecycle 行为 -│ ├── test_agnir_continuity.py # Agnir Continuity Provider adapter 的 load / checkpoint / failure 行为 +│ ├── test_agnir_continuity.py # Agnir Continuity Provider adapter 的 0.1/0.2 load / lineage / checkpoint / failure 行为 +│ ├── test_agnir_stable_migration.py # 当前 Svif Project 对发布版 Agnir v0.2.0 的 self-consumption、identity/locator/lineage 迁移 guard │ ├── test_chatgpt_surface.py # ChatGPT Execution Surface materialize / parse / identity 约束 │ ├── test_cloudflare_capability.py # Cloudflare provider 的 actuation / observation / subject-target 约束 │ ├── test_founding_e2e.py # founding Agnir + ChatGPT + Cloudflare 完整产品闭环 @@ -134,4 +136,4 @@ svif/ # Svif 产品主仓库 如果只是第一次理解 Svif,优先看 README 里的简略树即可;需要定位某个具体 contract、fixture、test、Plugin artifact、evidence 或 integration 文件时,再查本页。 -本页不是第二套架构定义。**架构语义仍以 `ARCHITECTURE.md`、`spec/`、`SVIF.yaml` 和 canonical Agnir decisions/state 为准;本页负责把这些职责映射回仓库中的实际文件位置。** +本页不是第二套架构定义。**架构语义仍以 `ARCHITECTURE.md`、`spec/`、`SVIF.yaml` 和 canonical Agnir decisions/state 为准;本页负责把这些职责映射回仓库中的实际文件位置。** \ No newline at end of file diff --git a/plugin/skills/svif/SKILL.md b/plugin/skills/svif/SKILL.md index 1591e7a..39cbf4f 100644 --- a/plugin/skills/svif/SKILL.md +++ b/plugin/skills/svif/SKILL.md @@ -26,15 +26,17 @@ For the current repository/filesystem founding path, bootstrap the Project in th 5. Run fresh activation from the selected Project root only: `Project root -> AGENTS.md -> README.md / Agnir Project Instructions -> AGNIR.yaml -> declared durable memory`. Validate the Agnir identity/profile/version and then validate that `SVIF.yaml` identifies the same Project and continuity binding. 6. Once bootstrap passes, continue the user's original Project task in the same operation. Do not make the user issue a separate Agnir initialization prompt and do not stop merely because the Project started without Agnir. +The bootstrap above is the immutable onboarding contract of the released `v0.2.0-preview.1` distribution. Migrating the Svif repository Project itself to a newer Agnir compatibility line does not retroactively rewrite that released bootstrap baseline. A future Svif distribution may deliberately move first-use bootstrap to Core/profile `0.2`, but that requires its own versioned distribution change and evidence. + This bootstrap behavior consumes Agnir protocol/profile semantics through Svif's founding Continuity Provider integration; it MUST NOT require the Agnir Skill repository, a previous Agnir installation conversation, GitHub, or another execution surface as a runtime prerequisite. A compatible surface may delegate to an available Agnir installer, but successful first use must remain possible from the Svif Plugin procedure itself. Do not treat partial or contradictory Agnir/Svif artifacts as a clean first-use bootstrap. If any durable surface shows that the Project already intends to use Agnir but activation/discovery is incomplete or broken, enter repair and preserve the applicable Agnir failure class. If `SVIF.yaml` or another durable binding intentionally selects a different Continuity Provider, do not overwrite it with Agnir; use the configured provider when supported or surface a binding/support blocker. If the current execution surface cannot perform the required non-destructive Project writes, report that bootstrap capability blocker rather than pretending that pre-initialization was a user prerequisite. -For an Agent-operable Agnir Project using `repository-filesystem/0.1`, the durable activation route is mandatory before normal Project work: +For an Agent-operable Agnir Project using a supported repository/filesystem profile, the durable activation route is mandatory before normal Project work: `Project root -> AGENTS.md -> README.md / Agnir Project Instructions -> AGNIR.yaml -> declared durable memory` -Agnir Agent activation and Core discovery are distinct layers. When this Skill is operating an Agent-operable Project under `repository-filesystem/0.1`, treat the durable `AGENTS.md -> README.md / Agnir Project Instructions -> AGNIR.yaml` route as part of the Project activation contract, not as an optional convenience. Validate that the route is present, points to the canonical README Agnir section, and contains no unresolved material instruction conflict before treating Agent activation as healthy. The fact that the current Agent can directly open `AGNIR.yaml` MUST NOT be used to bypass a missing, stale, contradictory, or predecessor-private activation route or to claim that a fresh Agent can resume from the Project root. +Agnir Agent activation and Core discovery are distinct layers. When this Skill is operating an Agent-operable Project under a supported repository/filesystem profile, treat the durable `AGENTS.md -> README.md / Agnir Project Instructions -> AGNIR.yaml` route as part of the Project activation contract, not as an optional convenience. Validate that the route is present, points to the canonical README Agnir section, and contains no unresolved material instruction conflict before treating Agent activation as healthy. The fact that the current Agent can directly open `AGNIR.yaml` MUST NOT be used to bypass a missing, stale, contradictory, or predecessor-private activation route or to claim that a fresh Agent can resume from the Project root. Do not validate activation by heading/link presence alone. The canonical README `## Agnir Project Instructions` section itself MUST satisfy the current profile activation contract: it must state that the Project uses Agnir for durable continuity and, before Project work, direct a fresh Agent to treat the Project root as the authorized Project Entry Point, read top-level `AGNIR.yaml`, load Current State and Next Actions, load Decisions and Evidence when relevant, prefer durable Agnir Project truth over chat/private Agent memory unless superseded by newer Principal instruction or directly observed current Project fact, and checkpoint material continuity changes at an intentional save/finish boundary. If any required instruction is missing, materially weakened, or contradicted, activation is not healthy even when `AGENTS.md` reaches the correct heading; repair that earliest activation-contract defect when authorized and rerun activation from the Project root. @@ -53,19 +55,20 @@ When `AGNIR.yaml` is available, read it before substantive work. Before loading 1. validate `agnir.version` against the Agnir Core compatibility supported by the current Project binding; 2. validate `agnir.discovery_profile` against the already selected discovery profile; 3. verify that `project.identity` matches the Project selected by the authorized Project Entry Point or trusted binding context; -4. resolve the required memory locators only after those compatibility and identity checks pass. +4. for Core `0.2`, require a non-empty logical `continuity.lineage`; when a VCS selector is selected by trusted context, require the durable selector binding and verify that it matches that context while remaining distinct from the logical lineage identity; +5. resolve the required memory locators only after those compatibility, identity, lineage, and applicable selector-binding checks pass. -For the `repository-filesystem/0.1` profile, relative memory locators remain scoped to the selected Project root after resolving filesystem indirection. A relative locator that traverses a symlink or other indirection outside that root MUST NOT become an implicitly authorized external Locator Chain merely because the target is readable. Follow external memory only through an explicit durable authorized binding/Locator Chain; otherwise preserve the applicable discovery failure, including `AGNIR_DISCOVERY_UNAUTHORIZED` when the locator is known but authorization is absent or denied. +For supported `repository-filesystem/0.1` and `repository-filesystem/0.2` profiles, relative memory locators remain scoped to the selected Project root after resolving filesystem indirection. A relative locator that traverses a symlink or other indirection outside that root MUST NOT become an implicitly authorized external Locator Chain merely because the target is readable. Follow external memory only through an explicit durable authorized binding/Locator Chain; otherwise preserve the applicable discovery failure, including `AGNIR_DISCOVERY_UNAUTHORIZED` when the locator is known but authorization is absent or denied. A Locator Chain hop may use an environment binding only when that binding is stable and durably associated with the selected Project. A value that exists only in the current process environment, temporary workspace metadata, a prior conversation, private model memory, or a prompt-provided secret MUST NOT become continuity authority merely because it makes the locator resolve in this run. Require the Project Entry Point or another durable Project-owned binding to establish how a fresh Executor can recover the same locator and invoke any required authorization without predecessor-private context. If that durable association cannot be established, surface the applicable discovery failure rather than accepting an ephemeral successful resolution or checkpointing it as resumable continuity. For repository-aware Projects that declare `extensions.agnir/repository.canonical` and `authoritative_ref`, treat those values as durable backend metadata for canonical continuity, not as decorative provenance. Before a state-dependent write or checkpoint, determine whether the selected working copy/revision is actually on the declared canonical repository/ref. A detached commit, pull-request checkout, temporary branch, fork, mirror, or otherwise non-authoritative execution copy MAY be used for observation, implementation, and verification, but MUST NOT silently become the canonical continuity write target merely because it is writable. Reconcile accepted changes back to the declared authoritative ref, or surface the repository/ref mismatch and leave the canonical checkpoint unchanged until the Project policy or trusted Principal explicitly authorizes a different durable binding. Package revision identity and target-Project authoritative-ref identity are separate facts and MUST NOT be conflated. -For the current Svif repository binding, the expected values are Agnir Core `0.1`, profile `repository-filesystem/0.1`, and Project identity `urn:svif:project:svif-core`. Treat these as Project-binding facts, not universal Agnir constants. +For the current Svif repository binding, the expected values are Agnir Core `0.2`, profile `repository-filesystem/0.2`, Project identity `urn:svif:project:svif-core`, one explicit logical Continuity Lineage, and a matching durable VCS selector binding. Treat these as Project-binding facts, not universal Agnir constants. The released `v0.2.0-preview.1` first-use bootstrap remains on its separately versioned Core/profile `0.1` baseline. -Do not load state and then retroactively decide whether it belonged to the selected Project. Unsupported Core/profile compatibility must surface an explicit discovery failure such as `AGNIR_DISCOVERY_UNSUPPORTED_VERSION`; a selected-root identity mismatch must surface `AGNIR_DISCOVERY_PROJECT_MISMATCH`. A known required locator whose authorization is absent or denied must remain `AGNIR_DISCOVERY_UNAUTHORIZED` when that distinction can safely be made; a required locator that cannot resolve to durable state must remain `AGNIR_DISCOVERY_UNRESOLVABLE`. A Locator Chain that loops rather than terminating in required durable state must remain `AGNIR_DISCOVERY_CYCLE`; state known to be superseded or non-authoritative must remain `AGNIR_DISCOVERY_STALE`; and material contradiction between the Discovery Record and resolved memory, or within the resolved memory itself, must remain `AGNIR_DISCOVERY_INCONSISTENT` until safe continuation is re-established. None of these failures grants permission to search sibling repositories, parent/child Projects, home directories, chat history, or retired layouts for substitute state. +Do not load state and then retroactively decide whether it belonged to the selected Project. Unsupported Core/profile compatibility must surface an explicit discovery failure such as `AGNIR_DISCOVERY_UNSUPPORTED_VERSION`; a selected-root identity mismatch must surface `AGNIR_DISCOVERY_PROJECT_MISMATCH`. Core `0.2` without a selected logical lineage must surface `AGNIR_LINEAGE_REQUIRED`. A selected VCS context without a durable selector binding, or with a conflicting selector binding, must preserve the applicable binding failure rather than guessing another lineage. A known required locator whose authorization is absent or denied must remain `AGNIR_DISCOVERY_UNAUTHORIZED` when that distinction can safely be made; a required locator that cannot resolve to durable state must remain `AGNIR_DISCOVERY_UNRESOLVABLE`. A Locator Chain that loops rather than terminating in required durable state must remain `AGNIR_DISCOVERY_CYCLE`; state known to be superseded or non-authoritative must remain `AGNIR_DISCOVERY_STALE`; and material contradiction between the Discovery Record and resolved memory, or within the resolved memory itself, must remain `AGNIR_DISCOVERY_INCONSISTENT` until safe continuation is re-established. None of these failures grants permission to search sibling repositories, parent/child Projects, home directories, chat history, or retired layouts for substitute state. -After validation, treat the Project-managed Agnir state as the durable continuity authority for current state, next actions, decisions, and referenced evidence, but reconcile conflicting truth using Agnir Core `0.1` precedence unless stricter Project policy applies: directly observed current Project or relevant external-system state first; explicit current Principal instruction or policy second; current durable Agnir state third; older checkpoint/evidence fourth; Executor-private context last. Material unresolved uncertainty must be surfaced rather than guessed. A newer observed fact or Principal instruction that supersedes durable state must be reconciled back into the Project-owned checkpoint instead of remaining only in transient execution context. +After validation, treat the Project-managed Agnir state as the durable continuity authority for current state, next actions, decisions, and referenced evidence, but reconcile conflicting truth using the precedence defined by the selected compatible Agnir Core line unless stricter Project policy applies: directly observed current Project or relevant external-system state first; explicit current Principal instruction or policy second; current durable Agnir state third; older checkpoint/evidence fourth; Executor-private context last. Material unresolved uncertainty must be surfaced rather than guessed. A newer observed fact or Principal instruction that supersedes durable state must be reconciled back into the Project-owned checkpoint instead of remaining only in transient execution context. This truth-reconciliation precedence does not grant protected execution authority. Principal approval or policy is usable for protected effects only when it arrives through the applicable trusted integration boundary, and directly observed state does not replace exact-subject verification, required authority, or independent post-effect observation. @@ -81,7 +84,7 @@ If more than one Project is involved, keep each Project's durable state isolated Load current state and next actions first. Then read only decisions and evidence that materially constrain the requested operation. Avoid pulling historical or retired artifacts back into active architecture unless the current Project explicitly declares them authoritative. -Use the Project's canonical repository or substrate when one is declared. For `iorLab/svif`, `main` is the active line and repository-managed Agnir state is canonical. Svif currently consumes Agnir Core compatibility `0.1` through the `repository-filesystem/0.1` profile; Agnir repository release `0.1.1` is a separate SemVer layer and must not be confused with the Core/profile compatibility identifiers. +Use the Project's canonical repository or substrate when one is declared. For `iorLab/svif`, `main` is the active authoritative line and repository-managed Agnir state is canonical. The Svif repository Project now consumes published Agnir repository release `v0.2.0`, Core compatibility `0.2`, and profile `repository-filesystem/0.2`; its logical lineage and VCS selector binding are declared separately in `AGNIR.yaml` / `SVIF.yaml`. The released `v0.2.0-preview.1` distribution still bootstraps new Projects on its published Core/profile `0.1` baseline until a later Svif distribution release deliberately changes that behavior. Repository SemVer and Core/profile compatibility identifiers are separate version layers and must not be conflated. ## 3. Execute through the Svif lifecycle diff --git a/tests/test_agnir_stable_migration.py b/tests/test_agnir_stable_migration.py index 566411a..0d1f7d2 100644 --- a/tests/test_agnir_stable_migration.py +++ b/tests/test_agnir_stable_migration.py @@ -9,6 +9,7 @@ ROOT = Path(__file__).resolve().parents[1] PROJECT = "urn:svif:project:svif-core" +SKILL = ROOT / "plugin" / "skills" / "svif" / "SKILL.md" class PublishedAgnirStableMigrationTests(unittest.TestCase): @@ -44,6 +45,8 @@ def test_repository_self_consumes_published_core_0_2(self) -> None: self.assertIsNotNone(lineage_match) self.assertIsNotNone(selector_match) self.assertNotEqual(lineage_match.group(1), selector_match.group(1)) + self.assertIn(f'lineage: "{lineage_match.group(1)}"', svif) + self.assertIn(f'vcs_selector: "{selector_match.group(1)}"', svif) def test_migration_preserves_project_identity_and_memory_locators(self) -> None: agnir = (ROOT / "AGNIR.yaml").read_text(encoding="utf-8") @@ -56,6 +59,25 @@ def test_migration_preserves_project_identity_and_memory_locators(self) -> None: ): self.assertIn(marker, agnir) + def test_skill_distinguishes_current_self_host_from_released_preview_bootstrap(self) -> None: + text = SKILL.read_text(encoding="utf-8") + + for marker in ( + "For the current Svif repository binding, the expected values are Agnir Core `0.2`", + "profile `repository-filesystem/0.2`", + "one explicit logical Continuity Lineage", + "matching durable VCS selector binding", + "The released `v0.2.0-preview.1` first-use bootstrap remains", + "Core/profile `0.1` baseline", + "published Agnir repository release `v0.2.0`", + ): + self.assertIn(marker, text) + + self.assertIn("Initialize the Agnir `repository-filesystem/0.1` continuity contract", text) + self.assertIn('compatibility `"0.1"`', text) + self.assertIn("Core `0.2`, require a non-empty logical `continuity.lineage`", text) + self.assertIn("AGNIR_LINEAGE_REQUIRED", text) + if __name__ == "__main__": unittest.main() From 267f3d706e4fba67f2fb4a3a7ea33e80b9fb48ef Mon Sep 17 00:00:00 2001 From: mattamior Date: Thu, 3 Sep 2026 14:46:56 +0800 Subject: [PATCH 5/6] checkpoint: record validated Agnir v0.2.0 downstream migration --- ...-agnir-v0.2.0-real-downstream-migration.md | 72 +++++++++++++++---- .agnir/next-actions.md | 41 ++++++----- .agnir/state.md | 53 +++++++------- 3 files changed, 111 insertions(+), 55 deletions(-) diff --git a/.agnir/evidence/2026-09-03-agnir-v0.2.0-real-downstream-migration.md b/.agnir/evidence/2026-09-03-agnir-v0.2.0-real-downstream-migration.md index 51db49f..95b279b 100644 --- a/.agnir/evidence/2026-09-03-agnir-v0.2.0-real-downstream-migration.md +++ b/.agnir/evidence/2026-09-03-agnir-v0.2.0-real-downstream-migration.md @@ -1,6 +1,6 @@ # Published Agnir v0.1.1 -> v0.2.0 real downstream migration — 2026-09-03 -Status: **migration-line evidence; not authoritative-main acceptance until target reconciliation completes.** +Status: **validated migration-source evidence; authoritative-main acceptance still pending target reconciliation.** ## Purpose @@ -35,7 +35,7 @@ Commit `ddaee058efe4c8381f60f5a2ebcae0de9ee9203d` was created from the captured ## Atomic Project-truth migration -The migration commit changes the Project-owned compatibility surfaces coherently: +Migration commit `eac2ab0dd70695d972b99afad084614eae26c77c` changed the Project-owned compatibility surfaces coherently: - `AGNIR.yaml`: Core `0.2`, profile `repository-filesystem/0.2`; - `SVIF.yaml`: continuity compatibility/profile `0.2`; @@ -49,17 +49,63 @@ The migration commit changes the Project-owned compatibility surfaces coherently The migration branch is temporary and cannot silently become canonical continuity merely because it is writable. Its lineage/selector and branch-local State/Next are validation inputs only until a target-main reconciliation is staged and accepted. -## Required validation +## CI sequence and observed friction -Before any main publication, require: +Initial Draft PR #6 run `33723726831`: -1. all active Svif first-use/bootstrap/activation surfaces to converge from Core/profile `0.1` to `0.2`; -2. adapter discovery of Project `urn:svif:project:svif-core` and the logical lineage without predecessor-private context; -3. selector/lineage separation and mismatch failures; -4. checkpoint/resume on Core/profile `0.2`; -5. repository integrity, portable contracts, founding E2E, Plugin package/discovery/first-use tests and full suite; -6. exact migration candidate CI on a non-authoritative validation surface; -7. fresh source/target stale checks before target-main advancement; -8. authoritative-main fresh resume after acceptance. +- portable-contracts: success; +- runtime-kernel: failed only on two tests that still asserted the old current self-host binding plus one Next-Actions distribution-marker assertion; +- repository-integrity: failed only because its current-binding guard still required Core `0.1`. -If the migration exposes an Agnir product defect rather than a Svif binding defect, record it as downstream evidence and repair Agnir rather than weakening the Svif test to hide it. +The adapter's 0.2 discovery, logical lineage, selector binding, checkpoint evidence and the new stable migration self-test all passed in that run. No Agnir 0.2 runtime/semantic defect was exposed. + +Commit `8aaed18dbbbbb857873500505ae941289f0029c4` converged current-binding guards while preserving the immutable Preview.1 bootstrap 0.1 baseline. Run `33724143647` then passed: + +- repository-integrity; +- runtime-kernel; +- portable-contracts. + +Commit `5b2086bdc61cd5dad8397241565fbbda9592fc88` synchronized the active Skill and repository map. The Skill now distinguishes the Svif repository's current Core/profile `0.2` self-host binding from the released `v0.2.0-preview.1` first-use Core/profile `0.1` bootstrap contract, and explicitly requires logical lineage plus applicable selector binding for Core `0.2`. + +Final pre-checkpoint PR-head run `33724576017` passed all three jobs: repository integrity, runtime-kernel full unittest discovery, and portable contracts. + +No Agnir `v0.2.0` product defect has been observed in this real downstream migration. The only initial failures were Svif guards that still encoded the old current binding and one accidental omission of existing distribution markers; both were repaired without weakening 0.1 regression/bootstrap pressure. + +## Exact synthetic integration verification + +Validated pre-checkpoint head: + +- commit: `5b2086bdc61cd5dad8397241565fbbda9592fc88`; +- tree: `142051872a708c9944c737e1ebcee008ac27a381`. + +PR #6 synthetic merge commit: + +- commit: `5d145ce1eb4ec4e6b837194a3e206b77bb71665b`; +- first parent: captured main `dac058789a27f32f4ed1949874c1954f31f12bd8`; +- second parent: validated head `5b2086bdc61cd5dad8397241565fbbda9592fc88`; +- tree: `142051872a708c9944c737e1ebcee008ac27a381`. + +The synthetic integration tree is byte-identical to the migration head tree. GitHub introduced no content transformation. + +## Product-boundary finding + +A materially useful migration finding is the distinction between two versioned facts: + +1. **the Svif repository Project's current self-host binding**, which can migrate to published Agnir Core/profile `0.2`; +2. **the already published `v0.2.0-preview.1` Plugin first-use bootstrap contract**, which remains on its immutable Core/profile `0.1` baseline until a future Svif distribution intentionally changes it. + +Treating those as the same version surface would either leave the repository's current binding stale or silently rewrite an already released onboarding contract. The migration keeps both facts explicit and independently guarded. + +## Remaining publication boundary + +This evidence does not authorize an ordinary PR merge. The migration lineage is source input only. Authoritative main must receive a separately staged target-owned result with: + +- Project identity `urn:svif:project:svif-core`; +- Core/profile `0.2`; +- stable Agnir `v0.2.0` provenance at `fc84095...`; +- logical target lineage `urn:svif:lineage:authoritative`; +- selector `refs/heads/main`; +- matching `SVIF.yaml` continuity binding; +- target-reconciled Current State / Next Actions preserving unrelated Svif obligations. + +The target candidate must be validated while main remains unchanged, followed by fresh source/target stale checks and one coherent main publication. A final migration-line checkpoint records the receipts above and must itself pass CI before it can be used as the source revision for that reconciliation. diff --git a/.agnir/next-actions.md b/.agnir/next-actions.md index 1d36134..5e0a224 100644 --- a/.agnir/next-actions.md +++ b/.agnir/next-actions.md @@ -1,26 +1,31 @@ # Svif Next Actions -Svif is performing a real downstream migration from published Agnir `v0.1.1` / Core-profile `0.1` to published stable Agnir `v0.2.0` / **Agnir Core 0.2** / `repository-filesystem/0.2` on temporary lineage `urn:svif:lineage:agnir-v0.2.0-stable-migration`. Authoritative `main` remains unchanged until the exact migration result is fully validated and reconciled. +The temporary Svif migration lineage has completed real downstream validation against published Agnir `v0.2.0` / Core-profile `0.2`. Authoritative `main` remains at the captured pre-migration Project until a target-owned reconciled candidate is independently validated. -1. **Converge current-Project guards onto Agnir Core/profile `0.2`.** Update repository integrity and current-binding tests to accept the explicit logical lineage + selector binding. Preserve the released Skills-only Preview.1 first-use bootstrap baseline at Core/profile `0.1`; changing that onboarding contract requires a later intentional distribution release, not this Project migration. -2. **Validate the migrated Svif Project as a real published-stable downstream consumer.** Require Project identity `urn:svif:project:svif-core`, logical lineage presence, selector != lineage identity, unchanged durable memory locators, stable Agnir provenance `v0.2.0` / `fc84095...`, Core/profile `0.2` discovery, checkpoint/resume, founding E2E, Plugin package/first-use behavior, repository integrity, contracts and full suite. -3. **Pressure upgrade usability rather than hiding friction.** Any failure attributable to Agnir `v0.2.0` migration semantics, discovery, lineage binding or recovery should be treated as Agnir product evidence and repaired in Agnir `v0.2.x` when appropriate rather than papered over in Svif. -4. **Construct a target-reconciled main candidate only after the migration branch is fully green.** Main must receive the accepted Project/package result while establishing target-owned authoritative logical lineage and `refs/heads/main` binding. Do not copy temporary migration-line State/Next as automatic target truth, and do not advance main before candidate validation and fresh source/target stale checks. -5. **After authoritative-main verification, record this as Agnir v1 downstream-upgrade evidence.** The receipt must distinguish synthetic fixtures and earlier pre-release experiments from this published `v0.1.1` -> published `v0.2.0` real Project upgrade. -6. **Preserve the released Repository Preview and distribution evidence.** Keep `v0.2.0-preview.1` immutable and preserve its **immutable candidate**, real **Codex CLI**, and **ChatGPT desktop/Codex** acceptance evidence. Any Preview fix uses a new tag such as `v0.2.0-preview.2`. -7. **Continue the separate public/personal ChatGPT path when the publisher gate is resolvable.** Submit the supported Skills-only package to the **universal Plugins Directory**, explicitly Publish after approval, then validate a real **individual-user ChatGPT surface**, with **ChatGPT Web** remaining a first-class target. -8. Keep live Cloudflare delivery disabled unless explicitly authorized. -9. **Retire temporary migration/validation refs after accepted reconciliation when a safe delete-ref path is available.** `main` remains the only long-lived Svif branch. +1. **Checkpoint the final migration source and require fresh exact-head CI.** Preserve run `33724576017`, synthetic merge `5d145ce1...`, and exact tree `142051872...`; the checkpoint itself must also pass repository integrity, runtime/unit tests and portable contracts before becoming the source receipt for main reconciliation. +2. **Construct a target-reconciled main candidate without advancing main.** Accept the validated adapter/tests/Skill/repository-map/migration-evidence changes, but rebuild target continuity for Project `urn:svif:project:svif-core` on logical lineage `urn:svif:lineage:authoritative`, separately bound to `refs/heads/main`. `SVIF.yaml` must carry the matching lineage/selector binding. Preserve stable Agnir `v0.2.0` provenance at `fc84095...`. +3. **Do not copy temporary migration-line State/Next as target truth.** Reconcile main Current State and Next Actions against the actual integrated Project result and preserve unrelated Svif release/distribution obligations, including immutable `v0.2.0-preview.1`, Codex/desktop acceptance evidence, the public/personal ChatGPT path, and Cloudflare authority policy. +4. **Validate the exact target candidate while main remains unchanged.** Require repository integrity, runtime-kernel full suite, portable contracts, Core/profile `0.2` fresh self-consumption, lineage/selector agreement, retained Preview.1 bootstrap `0.1` regression, and the published-stable migration evidence guard. +5. **Fresh stale-check source and target immediately before publication.** Captured main must still be `dac058789a27f32f4ed1949874c1954f31f12bd8`; migration source must still equal the final validated checkpoint. Any advance invalidates the candidate. +6. **Advance main exactly once to the verified target-reconciled candidate.** Do not use ordinary PR merge as the publication primitive and do not publish migration-line continuity first then repair it. +7. **Verify authoritative-main fresh resume and CI.** Confirm Core/profile `0.2`, Project identity, target logical lineage `urn:svif:lineage:authoritative`, selector `refs/heads/main`, stable Agnir `v0.2.0` provenance, existing durable locators, and the complete Svif product test surface. +8. **Record a post-integration main checkpoint and feed the result back into Agnir v1 evidence.** Distinguish this published `v0.1.1` -> published `v0.2.0` real Project upgrade from earlier experimental Core 0.2 validation and synthetic fixtures. +9. **Preserve the released Repository Preview and distribution evidence.** Keep `v0.2.0-preview.1` immutable and preserve its **immutable candidate**, real **Codex CLI**, and **ChatGPT desktop/Codex** acceptance evidence. Any Preview fix uses a new tag such as `v0.2.0-preview.2`. +10. **Continue the separate public/personal ChatGPT path when the publisher gate is resolvable.** Submit the supported Skills-only package to the **universal Plugins Directory**, explicitly Publish after approval, then validate a real **individual-user ChatGPT surface**, with **ChatGPT Web** remaining a first-class target. +11. Keep live Cloudflare delivery disabled unless explicitly authorized. +12. Retire temporary migration/validation refs after accepted reconciliation when a safe delete-ref path is available. `main` remains the only long-lived Svif branch. -## Captured migration receipts +## Final migration-line receipts before checkpoint -- authoritative Svif baseline: `main@dac058789a27f32f4ed1949874c1954f31f12bd8`; -- previous Agnir operational release: `v0.1.1` -> `e9712357ab590e5c1e5357b3cf3219d07d789aff`; -- target Agnir stable release: `v0.2.0` -> `fc84095ed5d500be9e1b43a4af0e93356571bbd4`; -- preparatory dual-line adapter/tests commit: `ddaee058efe4c8381f60f5a2ebcae0de9ee9203d`; -- migration Project-truth commit: `eac2ab0dd70695d972b99afad084614eae26c77c`; -- initial Draft PR #6 run: `33723726831`; portable contracts success, failures limited to old current-binding/repository guards and preserved distribution-marker assertions; -- migration branch selector: `refs/heads/migration/agnir-v0.2.0-stable`; +- captured source main: `dac058789a27f32f4ed1949874c1954f31f12bd8`; +- Agnir source release: `v0.1.1` -> `e9712357ab590e5c1e5357b3cf3219d07d789aff`; +- Agnir target stable: `v0.2.0` -> `fc84095ed5d500be9e1b43a4af0e93356571bbd4`; +- migration head before this checkpoint: `5b2086bdc61cd5dad8397241565fbbda9592fc88`; +- migration head tree: `142051872a708c9944c737e1ebcee008ac27a381`; +- final PR-head run: `33724576017` success, all three jobs green; +- synthetic merge: `5d145ce1eb4ec4e6b837194a3e206b77bb71665b`; +- synthetic merge tree: `142051872a708c9944c737e1ebcee008ac27a381`, exact tree match; +- migration selector: `refs/heads/migration/agnir-v0.2.0-stable`; - migration logical lineage: `urn:svif:lineage:agnir-v0.2.0-stable-migration`. ## Invariants diff --git a/.agnir/state.md b/.agnir/state.md index be89ded..00ea1ad 100644 --- a/.agnir/state.md +++ b/.agnir/state.md @@ -1,10 +1,10 @@ # Svif Current State -Svif is the authoritative active **Project orchestration product** in `iorLab/svif`. The canonical long-lived ref remains `main`; this temporary migration lineage exists only to validate a real downstream upgrade of Svif's founding Agnir Continuity Provider from published stable `v0.1.1` / Core `0.1` to published stable `v0.2.0` / Core `0.2`. The former `iorLab/svif-cloudflare-reference` project is retired. +Svif is the authoritative active **Project orchestration product** in `iorLab/svif`. The canonical long-lived ref remains `main`; this temporary migration lineage validates a real downstream upgrade of Svif's founding Agnir Continuity Provider from published stable `v0.1.1` / Core `0.1` to published stable `v0.2.0` / Core `0.2`. The former `iorLab/svif-cloudflare-reference` project is retired. ## Product architecture -Svif continues to coordinate the same four first-class components: Orchestrator (`src/svif/runtime.py`), Continuity Provider (`src/svif/continuity/agnir.py`), Execution Surface (`src/svif/execution/chatgpt.py`), and Capability Provider (`src/svif/capabilities/cloudflare.py`). The Project persists; Executors and execution environments may change. No execution surface becomes canonical Project truth merely because execution occurred there. +Svif continues to coordinate the same four first-class components: Orchestrator (`src/svif/runtime.py`), Continuity Provider (`src/svif/continuity/agnir.py`), Execution Surface (`src/svif/execution/chatgpt.py`), and Capability Provider (`src/svif/capabilities/cloudflare.py`). The Project persists; Executors and execution environments may change. ## Released Svif product state @@ -19,39 +19,44 @@ Svif continues to coordinate the same four first-class components: Orchestrator - Live Cloudflare delivery remains disabled unless explicitly authorized. - `README.md` and `README.zh-CN.md` remain the synchronized user/Agent entry points. -## Real Agnir stable migration under validation — 2026-09-03 +## Published Agnir v0.2.0 migration-line validation completed — 2026-09-03 -Captured authoritative Svif baseline before migration: `main@dac058789a27f32f4ed1949874c1954f31f12bd8`. +Captured authoritative Svif source before migration: `main@dac058789a27f32f4ed1949874c1954f31f12bd8`. -Before migration, the Svif Project itself consumed Agnir Core `0.1`, `repository-filesystem/0.1`, and published Agnir repository release `v0.1.1` at `e9712357ab590e5c1e5357b3cf3219d07d789aff`. +The temporary migration line now self-hosts: -This migration lineage now declares: - -- Agnir Core compatibility `0.2`; -- discovery profile `repository-filesystem/0.2`; +- Agnir Core `0.2`; +- `repository-filesystem/0.2`; - unchanged Project identity `urn:svif:project:svif-core`; -- logical Continuity Lineage `urn:svif:lineage:agnir-v0.2.0-stable-migration`; -- VCS selector binding `refs/heads/migration/agnir-v0.2.0-stable` as backend selection metadata, not lineage identity; -- unchanged memory locators `.agnir/state.md`, `.agnir/next-actions.md`, `.agnir/decisions.md`, `.agnir/evidence/`; -- published Agnir stable package `v0.2.0` at immutable revision `fc84095ed5d500be9e1b43a4af0e93356571bbd4` as operational provenance; -- `SVIF.yaml` continuity binding updated to compatibility `0.2` / profile `repository-filesystem/0.2` and explicitly carries the same lineage/selector binding while keeping `project-binding/0.2` and the same Project identity. +- logical lineage `urn:svif:lineage:agnir-v0.2.0-stable-migration`; +- separate VCS selector `refs/heads/migration/agnir-v0.2.0-stable`; +- unchanged State / Next Actions / Decisions / Evidence locators; +- published Agnir stable `v0.2.0` at immutable revision `fc84095ed5d500be9e1b43a4af0e93356571bbd4`. + +Implementation/validation sequence: -The preparatory commit `ddaee058efe4c8381f60f5a2ebcae0de9ee9203d` brought the previously real-consumer-validated dual-line Agnir adapter and lineage tests onto the current Svif baseline without changing Project compatibility. Migration commit `eac2ab0dd70695d972b99afad084614eae26c77c` then changed branch-local Project truth to the published stable Core/profile `0.2` line. Initial Draft PR #6 CI proved portable contracts green and localized the remaining failures to guards that still asserted the old current binding; no adapter/Core 0.2 runtime defect was observed. +1. `ddaee058efe4c8381f60f5a2ebcae0de9ee9203d` added the already real-consumer-validated dual-line 0.1/0.2 adapter and tests without changing Project compatibility. +2. `eac2ab0dd70695d972b99afad084614eae26c77c` atomically migrated branch-local Project truth to Core/profile `0.2`. +3. Initial Draft PR #6 run `33723726831` passed portable contracts and exposed only old current-binding/repository assertions plus distribution markers omitted from the first migration Next Actions; no Core 0.2 adapter/runtime defect appeared. +4. `8aaed18dbbbbb857873500505ae941289f0029c4` converged repository/current-binding guards to 0.2 while explicitly retaining Preview.1 first-use bootstrap at 0.1. Run `33724143647` passed repository integrity, runtime/unit tests and portable contracts. +5. `5b2086bdc61cd5dad8397241565fbbda9592fc88` synchronized the active Skill and repository tree. The Skill now distinguishes current Svif self-host Core/profile `0.2` from the immutable Preview.1 bootstrap baseline `0.1`, and requires Core 0.2 lineage/selector validation. +6. Final PR-head run `33724576017` passed all three jobs: repository integrity, runtime-kernel full unittest discovery, and portable contracts. +7. PR #6 synthetic merge commit `5d145ce1eb4ec4e6b837194a3e206b77bb71665b` has tree `142051872a708c9944c737e1ebcee008ac27a381`, exactly equal to source head `5b2086...` tree. The captured main is an ancestor and no synthetic merge tree transformation occurred. -This is an explicit incompatible Core migration, not the earlier compatible operational upgrade from Agnir repository `0.1.0` to `0.1.1`. Existing Decisions and historical Evidence remain durable history; unrelated Svif product/distribution obligations remain active. +No Agnir `v0.2.0` product defect has been observed in this real downstream migration so far. The only initial failures were Svif guards that still encoded the old current self-host compatibility and one accidental omission of existing distribution markers; both were repaired without weakening 0.1 regression/bootstrap pressure. -## Migration acceptance boundary +## Acceptance boundary -This temporary branch is not authoritative main. It must not silently checkpoint branch-local State back onto `main`. Before acceptance, current-project guards must converge to Core/profile `0.2` while released Preview.1 bootstrap guards remain explicitly `0.1`. The exact migration candidate must pass repository integrity, portable contracts, runtime/unit tests, fresh discovery/resume, founding E2E and Plugin regression pressure. Only then may the accepted Project/package result be reconciled to main using target-owned main continuity and one coherent target publication. +This migration line remains non-authoritative. Its lineage/selector and branch-local State/Next are not main truth. The next publication boundary is a separately staged target-main candidate that accepts the validated product/package changes but reconstructs `AGNIR.yaml`, `SVIF.yaml`, State and Next Actions for authoritative main using target-owned lineage `urn:svif:lineage:authoritative` and selector `refs/heads/main`. + +Before main can advance, the final migration source and main target must be re-read for staleness, the target-reconciled candidate must pass the same complete CI while main remains unchanged, and only then may main advance once to that exact candidate. ## Existing product obligations preserved - `plugin/skills/svif/SKILL.md` remains the single-sourced orchestration workflow. -- A genuinely uninitialized Project using the released Preview.1 contract must still be bootstrapped by Svif without requiring a separate Agnir initialization prompt. -- Existing Project content and instructions must be preserved; partial/broken continuity is repair, not clean bootstrap; another intentionally selected Continuity Provider must not be overwritten. -- Repository Preview `v0.2.0-preview.1` remains immutable; any Preview fix uses a new tag. -- Personal ChatGPT Web remains a first-class target. -- Repository CI/package validation is not personal ChatGPT installation evidence. +- `v0.2.0-preview.1` stays immutable; any Preview fix uses a new tag. +- Personal ChatGPT Web remains a first-class target; repository CI/package validation is not personal ChatGPT installation evidence. +- Live Cloudflare delivery remains disabled unless explicitly authorized. - `main` remains the only long-lived branch. -`.agnir/next-actions.md` is the canonical ordered resume plan for completing and validating this migration. +`.agnir/next-actions.md` is the canonical ordered resume plan. From eba1b8538c4692a08bf69452525b735d23564599 Mon Sep 17 00:00:00 2001 From: mattamior Date: Thu, 3 Sep 2026 15:24:31 +0800 Subject: [PATCH 6/6] checkpoint: record Agnir v0.2.0 main acceptance --- ...2026-09-03-agnir-v0.2.0-main-acceptance.md | 59 +++++++++++++++++++ .agnir/next-actions.md | 43 +++++++------- .agnir/state.md | 33 +++++------ REPOSITORY_TREE.md | 1 + 4 files changed, 97 insertions(+), 39 deletions(-) create mode 100644 .agnir/evidence/2026-09-03-agnir-v0.2.0-main-acceptance.md diff --git a/.agnir/evidence/2026-09-03-agnir-v0.2.0-main-acceptance.md b/.agnir/evidence/2026-09-03-agnir-v0.2.0-main-acceptance.md new file mode 100644 index 0000000..cae6108 --- /dev/null +++ b/.agnir/evidence/2026-09-03-agnir-v0.2.0-main-acceptance.md @@ -0,0 +1,59 @@ +# Published Agnir v0.2.0 authoritative-main acceptance — 2026-09-03 + +Status: **accepted on authoritative Svif main; post-publication verification complete.** + +## Purpose + +Close the real downstream migration loop for Svif across the published Agnir compatibility boundary from `v0.1.1` / Core-profile `0.1` to `v0.2.0` / Core-profile `0.2`, and record the exact target-reconciliation/publication receipts. + +## Source and migration receipts + +- captured authoritative Svif main before migration: `dac058789a27f32f4ed1949874c1954f31f12bd8`; +- published source Agnir package: `v0.1.1` -> `e9712357ab590e5c1e5357b3cf3219d07d789aff`; +- published target Agnir package: `v0.2.0` -> `fc84095ed5d500be9e1b43a4af0e93356571bbd4`; +- validated migration source: `267f3d706e4fba67f2fb4a3a7ea33e80b9fb48ef`; +- migration source tree: `d6ffec2fddc48ec0052dd0531ca0088fb13b37b2`; +- final migration-source CI: run `33724859300`, repository-integrity/runtime-kernel/portable-contracts all success. + +The migration preserved Project identity `urn:svif:project:svif-core` and the existing State/Next/Decisions/Evidence locators while establishing an explicit Core 0.2 logical lineage distinct from its VCS selector. + +## Target reconciliation + +The validated source result was reconciled into target-owned main continuity before publication. + +- target-reconciled candidate: `2b5b92ab234d4c1b0d6596bbb0b8439eb6e05cfa`; +- target candidate tree: `191db90c0b959254025cb061159044c1b0ddf3d6`; +- first parent: captured main `dac058789a27f32f4ed1949874c1954f31f12bd8`; +- second parent: validated migration source `267f3d706e4fba67f2fb4a3a7ea33e80b9fb48ef`; +- target logical lineage: `urn:svif:lineage:authoritative`; +- target VCS selector: `refs/heads/main`; +- target Agnir operational provenance: `v0.2.0@fc84095ed5d500be9e1b43a4af0e93356571bbd4`. + +Candidate CI run `33725164044` passed repository-integrity, runtime-kernel and portable-contracts. PR #7's synthetic merge commit `1db24d60c7b4d60bde243c20fac1ab6ea1968798` produced exactly the candidate tree `191db90c0b959254025cb061159044c1b0ddf3d6`. + +Fresh stale checks immediately before publication confirmed: + +- main still `dac058789a27f32f4ed1949874c1954f31f12bd8`; +- migration source still `267f3d706e4fba67f2fb4a3a7ea33e80b9fb48ef`; +- integration ref still candidate `2b5b92ab234d4c1b0d6596bbb0b8439eb6e05cfa`. + +## Publication and observation + +Authoritative `main` advanced non-force exactly once from `dac058789a27...` directly to `2b5b92ab234...`. Ordinary PR merge was not used. No migration-line continuity was published first and repaired afterward. + +Post-publication push CI run `33725240001` completed successfully with all three jobs green: + +- `repository-integrity` — success; +- `runtime-kernel` — success; +- `portable-contracts` — success. + +Fresh authoritative-main reads after publication confirmed: + +- `AGNIR.yaml`: Core `0.2`, `repository-filesystem/0.2`, Project `urn:svif:project:svif-core`, lineage `urn:svif:lineage:authoritative`, selector `refs/heads/main`, unchanged durable locators, Agnir `v0.2.0@fc84095...`; +- `SVIF.yaml`: matching Project identity, compatibility/profile `0.2`, matching logical lineage and VCS selector. + +## Product evidence + +This real migration did not expose an Agnir `v0.2.0` semantic defect. The failures encountered during convergence were stale Svif guards that still encoded the previous current binding. Those guards were corrected while preserving Core/profile `0.1` regression coverage and the immutable `v0.2.0-preview.1` first-use bootstrap baseline. + +This receipt is materially stronger than both synthetic migration fixtures and the earlier pre-release Core 0.2 real-consumer validation because both endpoints are published Agnir releases and the accepted result is now authoritative Svif main truth. diff --git a/.agnir/next-actions.md b/.agnir/next-actions.md index 01dc81b..3c24d74 100644 --- a/.agnir/next-actions.md +++ b/.agnir/next-actions.md @@ -1,31 +1,31 @@ # Svif Next Actions -The validated published-Agnir `v0.2.0` migration is being staged for authoritative-main acceptance through target-owned reconciliation. The candidate preserves Project `urn:svif:project:svif-core`, uses logical lineage `urn:svif:lineage:authoritative`, and binds it separately to `refs/heads/main`. +The real Svif Project migration from published Agnir `v0.1.1` / Core-profile `0.1` to published stable Agnir `v0.2.0` / Core-profile `0.2` has been accepted on authoritative `main` through target-owned reconciliation. Main now uses logical lineage `urn:svif:lineage:authoritative` bound separately to `refs/heads/main`, and authoritative push CI is green. -1. **Validate the exact target-reconciled candidate while main remains unchanged.** Require repository-integrity, runtime-kernel full unittest discovery, portable contracts, Core/profile `0.2` self-consumption, matching `AGNIR.yaml` / `SVIF.yaml` lineage+selector binding, stable Agnir `v0.2.0` provenance, and retained Preview.1 bootstrap Core/profile `0.1` regression. -2. **Verify the candidate integration tree is exactly the intended target tree.** Any synthetic validation surface must produce the same tree as the staged candidate; do not allow a server-side merge result to choose continuity conflict sides. -3. **Fresh stale-check source and target immediately before publication.** Main must still be `dac058789a27f32f4ed1949874c1954f31f12bd8`; validated migration source must still be `267f3d706e4fba67f2fb4a3a7ea33e80b9fb48ef`. Any change invalidates the candidate. -4. **Advance main exactly once to the verified target-reconciled candidate.** Do not ordinary-merge PR #6 and do not publish migration-line `AGNIR.yaml`/State/Next first then repair them. -5. **Verify authoritative-main fresh resume and push CI.** Confirm Core/profile `0.2`, Project identity `urn:svif:project:svif-core`, lineage `urn:svif:lineage:authoritative`, selector `refs/heads/main`, unchanged durable locators, stable Agnir `v0.2.0` / `fc84095...` provenance, repository integrity, runtime/unit tests and portable contracts. -6. **Record a post-integration main checkpoint.** Capture exact accepted main revision/run and close the migration acceptance loop without rewriting the immutable Svif Preview tag or Agnir stable tag. -7. **Feed this real downstream upgrade into Agnir v1 evidence.** Distinguish it from synthetic migration fixtures and earlier pre-release Core 0.2 consumer validation; this is the first published `v0.1.1` -> published `v0.2.0` real Project upgrade boundary. -8. **Preserve the released Repository Preview and distribution evidence.** Keep `v0.2.0-preview.1` immutable and preserve its **immutable candidate**, real **Codex CLI**, and **ChatGPT desktop/Codex** acceptance evidence. Any Preview fix uses a new tag such as `v0.2.0-preview.2`. -9. **Continue the separate public/personal ChatGPT path when the publisher gate is resolvable.** Submit the supported Skills-only package to the **universal Plugins Directory**, explicitly Publish after approval, then validate a real **individual-user ChatGPT surface**, with **ChatGPT Web** remaining a first-class target. -10. Keep live Cloudflare delivery disabled unless explicitly authorized. -11. Retire temporary migration/validation refs after accepted reconciliation when a safe delete-ref path is available. `main` remains the only long-lived Svif branch. +1. **Feed the completed real downstream upgrade into Agnir v1 evidence.** Record the exact Svif pre-migration main, migration source, target-reconciled candidate, candidate tree, source/candidate/main CI runs, synthetic-tree receipts, fresh stale checks, main publication boundary, Project identity/locator preservation, and the distinction between current self-host Core/profile `0.2` and immutable Preview.1 bootstrap Core/profile `0.1`. +2. **Preserve the released Repository Preview and distribution evidence.** Keep `v0.2.0-preview.1` immutable and preserve its **immutable candidate**, real **Codex CLI**, and **ChatGPT desktop/Codex** acceptance evidence. Any Preview fix uses a new tag such as `v0.2.0-preview.2`. +3. **Continue the separate public/personal ChatGPT path when the publisher gate is resolvable.** Submit the supported Skills-only package to the **universal Plugins Directory**, explicitly Publish after approval, then validate a real **individual-user ChatGPT surface**, with **ChatGPT Web** remaining a first-class target. +4. **Treat future Agnir updates according to compatibility semantics.** Core `0.2` compatible repository updates may preserve the current lineage; any future incompatible Core boundary must use explicit migration rather than silent upgrade. +5. Keep live Cloudflare delivery disabled unless explicitly authorized. +6. Retire temporary migration/validation refs after their evidence has been safely captured and when a safe delete-ref path is available. `main` remains the only long-lived Svif branch. -## Accepted source receipts for target reconciliation +## Completed published-Agnir migration receipts -- captured target main: `dac058789a27f32f4ed1949874c1954f31f12bd8`; +- captured pre-migration main: `dac058789a27f32f4ed1949874c1954f31f12bd8`; - published Agnir source: `v0.1.1` -> `e9712357ab590e5c1e5357b3cf3219d07d789aff`; - published Agnir target: `v0.2.0` -> `fc84095ed5d500be9e1b43a4af0e93356571bbd4`; -- validated migration source: `267f3d706e4fba67f2fb4a3a7ea33e80b9fb48ef`; -- migration source tree: `d6ffec2fddc48ec0052dd0531ca0088fb13b37b2`; -- migration checkpoint run: `33724859300` success, all three jobs green; -- prior exact product/Skill run: `33724576017` success; -- prior synthetic merge `5d145ce1eb4ec4e6b837194a3e206b77bb71665b` had exact source tree `142051872a708c9944c737e1ebcee008ac27a381` before the receipt-only checkpoint; -- target logical lineage: `urn:svif:lineage:authoritative`; -- target selector: `refs/heads/main`. +- validated migration source: `267f3d706e4fba67f2fb4a3a7ea33e80b9fb48ef`, tree `d6ffec2fddc48ec0052dd0531ca0088fb13b37b2`; +- migration source CI: `33724859300` success, all three jobs green; +- target-reconciled candidate/main publication revision: `2b5b92ab234d4c1b0d6596bbb0b8439eb6e05cfa`; +- target candidate tree: `191db90c0b959254025cb061159044c1b0ddf3d6`; +- candidate CI: `33725164044` success, all three jobs green; +- PR #7 synthetic merge: `1db24d60c7b4d60bde243c20fac1ab6ea1968798`, exact tree `191db90c...`; +- authoritative-main push CI: `33725240001` success, all three jobs green; +- Project identity: `urn:svif:project:svif-core`; +- authoritative logical lineage: `urn:svif:lineage:authoritative`; +- authoritative selector: `refs/heads/main`; +- durable State/Next/Decisions/Evidence locators: unchanged; +- main `AGNIR.yaml` and `SVIF.yaml`: Core/profile `0.2`, matching lineage/selector binding, Agnir operational `v0.2.0@fc84095...`. ## Invariants @@ -34,5 +34,6 @@ The validated published-Agnir `v0.2.0` migration is being staged for authoritati - logical lineage identity != VCS selector != commit/checkpoint receipt. - Core `0.1` -> `0.2` is explicit migration, not compatible upgrade. - Source/migration continuity is reconciliation input, not automatic target truth. +- Main publication exposed Project result + reconciled target continuity in one coherent ref advancement. - The released Preview.1 bootstrap baseline and the Svif repository's current self-host binding are separate versioned facts. - Svif product architecture remains Orchestrator + Continuity Provider + Execution Surface + Capability Provider. diff --git a/.agnir/state.md b/.agnir/state.md index 4aff876..726b323 100644 --- a/.agnir/state.md +++ b/.agnir/state.md @@ -1,6 +1,6 @@ # Svif Current State -Svif is the authoritative active **Project orchestration product** in `iorLab/svif`. This target state accepts the validated published-Agnir migration result into authoritative `main` while preserving target-owned continuity. The former `iorLab/svif-cloudflare-reference` project is retired. +Svif is the authoritative active **Project orchestration product** in `iorLab/svif`. Authoritative `main` has now accepted the validated published-Agnir migration result through target-owned continuity reconciliation. The former `iorLab/svif-cloudflare-reference` project is retired. ## Product architecture @@ -20,11 +20,11 @@ Svif continues to coordinate the same four first-class components: Orchestrator - Live Cloudflare delivery remains disabled unless explicitly authorized. - `README.md` and `README.zh-CN.md` remain synchronized user/Agent entry points. -## Authoritative Agnir compatibility target — 2026-09-03 +## Authoritative Agnir compatibility — accepted 2026-09-03 -This target state upgrades the Svif Project itself from published Agnir `v0.1.1` / Core-profile `0.1` to published stable Agnir `v0.2.0` / Core-profile `0.2`. +The Svif Project itself has completed an explicit migration from published Agnir `v0.1.1` / Core-profile `0.1` to published stable Agnir `v0.2.0` / Core-profile `0.2`. -Target-owned continuity is: +Authoritative continuity is now: - Project identity: `urn:svif:project:svif-core` — unchanged; - Agnir Core: `0.2`; @@ -37,29 +37,26 @@ Target-owned continuity is: `SVIF.yaml` declares the same Project identity, Core/profile compatibility, logical lineage and VCS selector binding. Logical lineage identity is not derived from the branch ref or commit receipt. -## Real downstream migration evidence accepted as integration input +## Accepted migration and publication receipts Captured pre-migration authoritative main: `dac058789a27f32f4ed1949874c1954f31f12bd8`. -Validated migration source: `267f3d706e4fba67f2fb4a3a7ea33e80b9fb48ef`, tree `d6ffec2fddc48ec0052dd0531ca0088fb13b37b2`. +Validated migration source: `267f3d706e4fba67f2fb4a3a7ea33e80b9fb48ef`, tree `d6ffec2fddc48ec0052dd0531ca0088fb13b37b2`; exact source CI run `33724859300` passed repository-integrity, runtime-kernel and portable-contracts. -The migration line demonstrated: +Target-reconciled candidate: `2b5b92ab234d4c1b0d6596bbb0b8439eb6e05cfa`, tree `191db90c0b959254025cb061159044c1b0ddf3d6`, with first parent `dac058789a27...` and second parent `267f3d706...`. -- explicit published `v0.1.1` / Core `0.1` -> published `v0.2.0` / Core `0.2` migration; -- Project identity and memory-locator preservation; -- separate logical lineage and VCS selector binding; -- dual-line 0.1/0.2 adapter support and retained 0.1 regression pressure; -- current Svif self-host binding `0.2` kept distinct from immutable Preview.1 first-use bootstrap `0.1`; -- repository integrity, runtime/unit tests, founding E2E/Plugin regression surface and portable contracts all green. +Candidate validation run `33725164044` passed all three jobs. PR #7 synthetic merge `1db24d60c7b4d60bde243c20fac1ab6ea1968798` produced exactly the same tree `191db90c...` as the staged candidate. Fresh stale checks immediately before publication confirmed main, migration source and candidate refs had not advanced. -Final migration-source checkpoint run `33724859300` passed repository-integrity, runtime-kernel and portable-contracts. Earlier exact PR-head run `33724576017` also passed all three jobs. Before that checkpoint, PR #6 synthetic merge tree matched the source head tree exactly; no server-side content rewrite occurred. +Authoritative `main` then advanced exactly once, non-force, directly from `dac058789a27...` to `2b5b92ab234...`; ordinary PR merge was not used. There was no interval in which migration-line `AGNIR.yaml`, State or Next Actions were published as main truth and repaired afterward. -The migration source lineage `urn:svif:lineage:agnir-v0.2.0-stable-migration`, its branch selector, and its branch-local State/Next are reconciliation input only and are not authoritative-main truth. +Post-publication main push CI run `33725240001` passed repository-integrity, runtime-kernel full unittest discovery and portable-contracts. Fresh reads of `main` confirm `AGNIR.yaml` and `SVIF.yaml` both resolve Core/profile `0.2`, Project `urn:svif:project:svif-core`, lineage `urn:svif:lineage:authoritative`, selector `refs/heads/main`, preserved durable locators and Agnir `v0.2.0@fc84095...` provenance. -## Target publication boundary +This is the first recorded real Svif Project upgrade across the **published** Agnir `v0.1.1` -> **published** `v0.2.0` compatibility boundary. It is distinct from synthetic migration fixtures and the earlier pre-release Core 0.2 real-consumer experiment. -This content is designed for a staged target-reconciled candidate while main remains at captured revision `dac058789a27...`. The candidate must be independently validated on its exact tree. Immediately before publication, both captured main and migration source must be re-read; any change invalidates the candidate. Only after those checks may main advance once directly to the verified target candidate. +## Evidence consequence -After main advances, fresh main CI and cold-start discovery must verify Core/profile `0.2`, Project identity, target lineage `urn:svif:lineage:authoritative`, selector `refs/heads/main`, preserved durable locators and stable Agnir `v0.2.0` provenance. A later checkpoint records the exact accepted main revision/run and feeds the real downstream upgrade receipt into Agnir's v1 evidence. +The migration has not exposed an Agnir `v0.2.0` product defect. The only convergence failures encountered were stale Svif guards that still described the old current binding; they were repaired without weakening the retained Core/profile `0.1` regression and immutable Preview.1 onboarding baseline. + +The next material action is to record these exact downstream-upgrade receipts in `iorLab/agnir` as v1 evidence, then continue the separate Svif distribution obligations. `.agnir/next-actions.md` is the canonical ordered resume plan. diff --git a/REPOSITORY_TREE.md b/REPOSITORY_TREE.md index 7cd6e13..ea73835 100644 --- a/REPOSITORY_TREE.md +++ b/REPOSITORY_TREE.md @@ -38,6 +38,7 @@ svif/ # Svif 产品主仓库 │ ├── 2026-09-02-svif-v0.2.0-preview.1-candidate.md # Preview 候选、CI、Codex CLI 与桌面端真实安装验收证据 │ ├── 2026-09-02-svif-v0.2.0-preview.1-release.md # tag、GitHub Prerelease、main CI 与 tag-based 安装烟测证据 │ ├── 2026-09-03-agnir-v0.2.0-real-downstream-migration.md # Svif 从发布版 Agnir v0.1.1/Core 0.1 迁移到 v0.2.0/Core 0.2 的真实下游证据 +│ ├── 2026-09-03-agnir-v0.2.0-main-acceptance.md # target reconciliation、authoritative main publication 与 post-publication CI/cold-start 验证证据 │ └── checkpoint-2026-08-28-validation-2.md # Validation 2 的持久 checkpoint 记录 │ ├── .github/ # GitHub 托管侧自动化配置