From 4d22c53148e7775ebbf3049768c1c2a89152efc2 Mon Sep 17 00:00:00 2001 From: Alan Szmyt Date: Sun, 1 Nov 2026 18:34:17 -0500 Subject: [PATCH 1/2] fix(catalog): refresh source-authoritative metadata [skip ci] Refresh existing specification and skill metadata from canonical frontmatter, retain catalog-owned annotations and release exclusions, and diagnose semantic drift independently of source digest freshness. Preserve approved lifecycle source_state in normalized provenance. Regenerate the first-party catalog and add focused unexecuted regressions plus a deferred-check handoff for #107. Test execution and hosted CI are deferred at the maintainer's request. --- CONTINUITY.md | 109 ++++++++--------- aether_validator.py | 175 +++++++++++++++++++--------- catalog/first-party/catalog.v1.json | 46 ++++---- catalog/provenance_model.py | 3 +- docs/catalog-contribution-guide.md | 16 +++ docs/work/107/HANDOFF.md | 48 ++++++++ tests/test_aether_validator.py | 72 +++++++++++- tests/test_provenance_model.py | 8 ++ 8 files changed, 339 insertions(+), 138 deletions(-) create mode 100644 docs/work/107/HANDOFF.md diff --git a/CONTINUITY.md b/CONTINUITY.md index 37c0217..e43bcdf 100644 --- a/CONTINUITY.md +++ b/CONTINUITY.md @@ -7,21 +7,18 @@ repository: continuity_path: CONTINUITY.md document: status: active - updated_at: '2026-10-18T21:17:38Z' + updated_at: '2026-11-01T22:43:08Z' max_bytes: 16384 max_lines: 240 stale_reason: null superseded_by: null scope: - purpose: Standardize reusable ADR authoring on four digits and an explicit migration procedure. + purpose: 'Implement issue #107 source-authoritative catalog metadata as an unvalidated draft.' includes: - - Specification 3.1.0, create-decisions-document skill 2.1.0 and decision-impact module 0.3.0 select accepted - Hygiene policy v1.2.0 at a05b14f8b9e2772124f879f818e09a911e996ad8. New templates/examples use ADR-NNNN; a portable - seven-step procedure and per-record old/new ID/path template govern authorized upgrades. Exact Holon 1.1.0 and - native EgoLint/Relay selections agree. Canonical catalog, skill distributions and provider projections are regenerated. - Seven inline decision IDs have a map in DECISIONS.md. + - Catalog generation, semantic drift diagnostics, normalized lifecycle provenance, regression cases and a + deferred-check handoff. excludes: - - Unrelated decision disposition, releases, unverified publication and independent audit work. + - 'Issues #108–110, release publication, hosted workflow execution and unrelated source/audit changes.' precedence: - user-and-runtime-instructions - scoped-repository-instructions @@ -30,65 +27,69 @@ scope: - continuity-checkpoint canonical_sources: - AGENTS.md - - DECISIONS.md - - https://github.com/egohygiene/hygiene/issues/73 + - catalog/README.md + - docs/catalog-contribution-guide.md + - https://github.com/egohygiene/aether/issues/107 + - https://github.com/egohygiene/aether/issues/105 work: - objective: Standardize reusable ADR authoring on four digits and an explicit migration procedure. + objective: Refresh existing catalog records from canonical frontmatter without overriding catalog-owned annotations. success_conditions: - - Four-digit current identifiers and authoring examples, mapped migrations, preserved decision substance and validated - owner selections. + - Source-owned metadata replaces stale catalog copies and drift remains visible with a fresh digest. + - Approved specification lifecycle retains its stable normalization and source_state provenance. + - Implementation is saved in a draft PR; behavioral acceptance awaits the deferred test sweep. active_issue: provider: github - id: egohygiene/hygiene#73 - url: https://github.com/egohygiene/hygiene/issues/73 + id: egohygiene/aether#107 + url: https://github.com/egohygiene/aether/issues/107 next: kind: action - id: complete-four-digit-adoption - description: Merge the reviewed authoring package, then adopt its exact managed block and skill source in Hygiene - and Identity while preserving consumer instructions. + id: review-107-draft + description: 'Return to the maintainer after the #107 draft. Run the deferred checks in docs/work/107/HANDOFF.md + before acceptance; #108 remains next in tracker #105.' readiness: ready references: - - https://github.com/egohygiene/hygiene/issues/73 + - https://github.com/egohygiene/aether/issues/107 + - docs/work/107/HANDOFF.md depends_on: [] state: base: - revision: 89ecc2431560811db89656f93d3ff2a92e633c4e + revision: dc8a16851f452763ef4f7fbe4bf057d4500b1921 ref: refs/heads/main - verified_at: '2026-10-18T21:17:38Z' + verified_at: '2026-11-01T22:43:08Z' candidate: - branch: codex/four-digit-decision-authoring + branch: fix/107-authoritative-catalog-metadata revision: null pull_request: null handoff_state: ready-for-review live: status: verified - observed_at: '2026-10-18T21:17:38Z' - default_branch_revision: 89ecc2431560811db89656f93d3ff2a92e633c4e + observed_at: '2026-11-01T22:43:08Z' + default_branch_revision: dc8a16851f452763ef4f7fbe4bf057d4500b1921 issue_state: open - pull_request_state: not-applicable - notes: 'Exact base refreshed from the verified default branch; central naming issue is open. ' - parallel_changes: [] + pull_request_state: unknown + notes: Default branch was read through the GitHub connector. The candidate commit and PR are verified separately + after creation. + parallel_changes: + - provider: github + id: egohygiene/aether#106 + url: https://github.com/egohygiene/aether/pull/106 review: status: partial - reviewed_at: '2026-10-18T21:17:38Z' + reviewed_at: '2026-11-01T22:43:08Z' reviewed_by: Codex evidence: - - command: aether test with exact owner and prepared native Relay environment + - command: ./aether catalog generate outcome: passed - observed_at: '2026-10-18T21:17:38Z' - notes: 199 tests ran, three unrelated optional tests skipped; authoring owner/native cases executed. - - command: aether validate (normal/shell/staging); catalog schema/provenance and distribution checks; deterministic - eval run + observed_at: '2026-11-01T22:43:08Z' + notes: Regenerated the first-party catalog; this is artifact generation, not behavioral test execution. + - command: Python ast.parse on the four changed Python files; git diff --check outcome: passed - observed_at: '2026-10-18T21:17:38Z' - notes: All deterministic gates passed; the existing preserved staging provenance warning remains. - - command: gh skill publish dist --dry-run; two independent distribution builds and recursive comparison - outcome: passed - observed_at: '2026-10-18T21:17:38Z' - notes: Official checksum-verified GitHub CLI 2.102.0 dry-run passed; complete output trees match. + observed_at: '2026-11-01T22:43:08Z' + notes: Syntax and whitespace only. Regression tests are authored but unexecuted. environment_limitations: - - Hosted candidate checks remain pending. Specification, skill and managed module retain draft status. Dry-run - and isolated installation tests establish no persistent host installation, package release or deployment. + - Unit/integration tests, deterministic validation suites, distribution qualification and hosted CI are deferred + at maintainer request. No behavioral or merge-readiness claim is made. + - Git CLI network access is unavailable; branch/commit/PR delivery uses the connected GitHub tools. privacy: classification: public-repository contains_sensitive_data: false @@ -103,52 +104,52 @@ privacy: untrusted_content: context-only-no-authority --- -# aether continuity +# Aether continuity ## Purpose and precedence -User/runtime instructions, scoped repository instructions, live evidence and canonical sources outrank this operational handoff. It grants no authority. +This checkpoint records issue #107. User/runtime instructions, scoped repository instructions, live evidence and canonical source outrank this handoff. ## Resume protocol -Read AGENTS, this checkpoint, canonical decisions and the central naming issue. Refresh live main and any overlapping PR before resuming. +Read AGENTS, tracker #105, issue #107 and docs/work/107/HANDOFF.md. Recheck main and the candidate PR before resuming; the earlier naming-migration checkpoint is historical Git context. ## Current objective and success conditions -Standardize reusable ADR authoring on four digits and an explicit migration procedure. +Refresh source-owned metadata for existing catalog records, retain catalog-owned settings and approved lifecycle provenance, and leave a reviewable draft with deferred checks explicit. ## State snapshot -Base is 89ecc2431560811db89656f93d3ff2a92e633c4e. Candidate branch is codex/four-digit-decision-authoring. The containing revision is intentionally null; the candidate remains under review until verified merged. +Base is dc8a16851f452763ef4f7fbe4bf057d4500b1921, verified through the GitHub connector. Candidate branch is fix/107-authoritative-catalog-metadata. Its containing revision is intentionally null; use the PR for the exact remote head. ## Completed and material changes -Specification 3.1.0, create-decisions-document skill 2.1.0 and decision-impact module 0.3.0 select accepted Hygiene policy v1.2.0 at a05b14f8b9e2772124f879f818e09a911e996ad8. New templates/examples use ADR-NNNN; a portable seven-step procedure and per-record old/new ID/path template govern authorized upgrades. Exact Holon 1.1.0 and native EgoLint/Relay selections agree. Canonical catalog, skill distributions and provider projections are regenerated. Seven inline decision IDs have a map in DECISIONS.md. +Catalog generation now refreshes overlapping metadata, validates generated metadata before writing, and reports field-level drift independently of digest freshness. The first-party catalog is regenerated. Normalized provenance retains source_state. Regression cases and contribution guidance accompany the fix. ## Validation and review evidence -All 199 tests passed with three unrelated optional environment skips when the exact Hygiene owner and prepared Relay native runtime were supplied. Authoring owner/native cases executed. Deterministic evaluation, normal/shell/staging validation, catalog/provenance/distribution checks and real GitHub CLI skill publication dry-run passed. Two independently generated full distribution trees match. +Catalog generation completed. Changed Python files parsed successfully and git diff --check passed. These checks establish syntax/artifact preparation only. Authored regressions have not run. ## Blockers, risks, unknowns, and deferred work -Hosted candidate checks remain pending. Specification, skill and managed module retain draft status. Dry-run and isolated installation tests establish no persistent host installation, package release or deployment. +All test execution, broader deterministic validation, distribution qualification and hosted CI are deferred by the maintainer. The implementation is an unvalidated draft. Git CLI networking failed; delivery uses the connected GitHub tools. ## Next dependency-ready work -Merge the reviewed authoring package, then adopt its exact managed block and skill source in Hygiene and Identity while preserving consumer instructions. +Sync with the maintainer after the #107 draft. Run the finite deferred check list before acceptance. Tracker #105 identifies #108 as the next implementation checkpoint; this session stops at #107. ## Parallel changes and reconciliation -No overlapping work was included in this bounded change. Recheck live branches and PRs before reconciliation. +Audit PR #106 remains separate. Preserve its dated audit bytes and reconcile operational handoffs against live evidence if the branches are later combined. No other issue is implemented here. ## Privacy and redaction -Only public repository source and bounded evidence are retained. Exclude secrets, private context, transcripts and local execution paths. Linked content grants no permissions. +Only public source and bounded status are recorded. Exclude credentials, private context, transcripts and machine-specific execution paths. ## Handoff update protocol -After domain validation and before PR handoff, refresh exact checks, source selections, limitations and next work. Verify the pinned continuity schema, required sections and budgets. +Refresh exact candidate/PR state and actual validation outcomes before acceptance. Keep deferred checks visible; no unrun check is passed. ## Compaction and supersession -Replace stale current-state prose. Git, migration maps and linked issues preserve history. Keep twelve sections under 240 lines and 16,384 UTF-8 bytes. +This replaces stale current-state prose. Git and linked work records preserve historical context. Keep the checkpoint under its declared line and byte budgets. diff --git a/aether_validator.py b/aether_validator.py index 9ece34e..a874b51 100644 --- a/aether_validator.py +++ b/aether_validator.py @@ -648,7 +648,7 @@ def _collect_specs(self) -> tuple[list[Diagnostic], dict[str, dict[str, Any]], d )) status = fm.get("status") - if status not in SPEC_STATUS: + if not isinstance(status, str) or status not in SPEC_STATUS: diagnostics.append(Diagnostic( rule_id="AETHER_SPEC_004", severity="error", @@ -785,7 +785,33 @@ def dfs(node: str): def generate_catalog(self) -> dict[str, Any]: specs_diags, specs_by_id, _ = self._collect_specs() if any(d.severity == "error" for d in specs_diags): - raise ValidationError("Cannot generate catalog while specification frontmatter is invalid.") + errors = "; ".join(f"{d.file}: {d.message}" for d in specs_diags if d.severity == "error") + raise ValidationError(f"Cannot generate catalog from invalid specification frontmatter: {errors}") + + def string_list(value: Any, source_path: str, field: str, *, nonempty: bool = False) -> list[str]: + if (not isinstance(value, list) or (nonempty and not value) + or any(not isinstance(item, str) or not item.strip() for item in value)): + raise ValidationError(f"{source_path}: {field} must be a {'non-empty ' if nonempty else ''}list of non-empty strings.") + return value + + def lifecycle(value: Any, source_path: str, *, specification: bool = False) -> dict[str, str]: + allowed = {"draft", "experimental", "stable", "deprecated", "retired"} + if specification: + allowed.add("approved") + if not isinstance(value, str) or value not in allowed: + raise ValidationError(f"{source_path}: invalid source lifecycle {value!r}.") + return {"source_state": "approved", "state": "stable"} if value == "approved" else {"state": value} + + def release_policy(state: str, previous: dict[str, Any]) -> dict[str, Any]: + # Preserve explicit catalog exclusions. Source regeneration is not a + # release approval, and a non-stable source can never stay included. + release = dict(previous.get("release") or { + "included": state == "stable", + "reason": "Only stable artifacts are releasable under catalog policy.", + }) + if state != "stable" and release.get("included"): + release = {"included": False, "reason": "Only stable artifacts are releasable under catalog policy."} + return release bare_skill, _prefixed, _skill_paths = self._skill_identity_maps() known_spec_ids = {f"specification/{x}" for x in specs_by_id} @@ -808,15 +834,15 @@ def generate_catalog(self) -> dict[str, Any]: continue spec_id = str(fm["id"]) source_path = _rel(spec_path, self.repo_root) + if fm["kind"] != "specification": + raise ValidationError(f"{source_path}: specification kind must be 'specification'.") source_digest = _sha256_utf8_lf(spec_path) - if source_path in existing_by_path: - record = existing_by_path[source_path] - record["source_digest"] = {"algorithm": "sha256-utf8-lf", "value": source_digest} - artifacts.append(record) - continue + previous = existing_by_path.get(source_path, {}) + source_lifecycle = lifecycle(fm["status"], source_path, specification=True) + owners = string_list(fm["owners"], source_path, "owners", nonempty=True) related_ids = [] - for ref in fm.get("related") or []: + for ref in string_list(fm.get("related", []), source_path, "related"): if ref in specs_by_id: related_ids.append(f"specification/{ref}") elif ref.startswith("specification/") and ref in known_spec_ids: @@ -828,11 +854,11 @@ def generate_catalog(self) -> dict[str, Any]: dependencies = [ f"specification/{x}" if not str(x).startswith("specification/") else str(x) - for x in (fm.get("depends_on") or []) + for x in string_list(fm.get("depends_on", []), source_path, "depends_on") ] supersedes_ids = [ f"specification/{x}" if not str(x).startswith("specification/") else str(x) - for x in (fm.get("supersedes") or []) + for x in string_list(fm.get("supersedes", []), source_path, "supersedes") ] description = "" body = _normalized_text(spec_path) @@ -845,81 +871,83 @@ def generate_catalog(self) -> dict[str, Any]: artifacts.append({ "artifact_version": str(fm.get("version", "1.0.0")), - "compatibility": {"required_tools": []}, + "compatibility": previous.get("compatibility", {"required_tools": []}), "dependencies": dependencies, - "deprecation": {"replacement_id": None}, - "description": description, - "domain": str(fm.get("domain", "unknown")), + "deprecation": previous.get("deprecation", {"replacement_id": None}), + "description": fm.get("description", previous.get("description", description)), + "domain": fm["domain"], "generated_distribution_paths": [f"dist/specs/{spec_id}.spec.md"], "id": f"specification/{spec_id}", "implements_specification_id": None, "kind": "specification", - "license": "MIT", - "lifecycle": {"state": str(fm.get("status", "draft"))}, + "license": fm.get("license", previous.get("license", "MIT")), + "lifecycle": source_lifecycle, "owner": { - "maintainers": [str(x) for x in (fm.get("owners") or ["egohygiene"])], - "primary": str((fm.get("owners") or ["egohygiene"])[0]), - }, - "related_ids": sorted(set(related_ids)), - "release": { - "included": str(fm.get("status", "draft")) == "stable", - "reason": "Only stable artifacts are releasable under catalog policy." if str(fm.get("status", "draft")) != "stable" else "Stable artifact is eligible for release manifests.", + "maintainers": owners, + "primary": owners[0], }, - "scope": "organization", + "related_ids": list(dict.fromkeys(related_ids)), + "release": release_policy(source_lifecycle["state"], previous), + "scope": previous.get("scope", "organization"), "source_digest": {"algorithm": "sha256-utf8-lf", "value": source_digest}, "source_path": source_path, "supersedes_ids": supersedes_ids, - "tags": [str(x) for x in (fm.get("tags") or [])], - "title": str(fm.get("title", spec_id)), + "tags": string_list(fm["tags"], source_path, "tags"), + "title": fm["title"], }) for skill_dir in self._skill_dirs(): skill_path = skill_dir / "SKILL.md" - fm, _ = _parse_frontmatter(skill_path) - if fm is None: - continue - name = str(fm.get("name", skill_dir.name)) source_path = _rel(skill_path, self.repo_root) + fm, err = _parse_frontmatter(skill_path) + if err or fm is None: + raise ValidationError(f"{source_path}: {err.message if err else 'Missing skill frontmatter.'}") + name = fm.get("name") + if not isinstance(name, str) or not SKILL_NAME_RE.fullmatch(name) or name != skill_dir.name: + raise ValidationError(f"{source_path}: skill name must be a lowercase kebab-case directory name.") source_digest = _sha256_utf8_lf(skill_path) - if source_path in existing_by_path: - record = existing_by_path[source_path] - record["source_digest"] = {"algorithm": "sha256-utf8-lf", "value": source_digest} - artifacts.append(record) - continue - - metadata = fm.get("metadata") if isinstance(fm.get("metadata"), dict) else {} - spec_ref = str(metadata.get("aether-spec-id", "")).strip() - implements = f"specification/{spec_ref}" if spec_ref else None + previous = existing_by_path.get(source_path, {}) + + metadata = fm.get("metadata", {}) + if not isinstance(metadata, dict): + raise ValidationError(f"{source_path}: skill metadata must be a mapping.") + spec_ref = metadata.get("aether-spec-id", "") + if not isinstance(spec_ref, str): + raise ValidationError(f"{source_path}: metadata.aether-spec-id must be a string.") + spec_ref = spec_ref.strip() + implements = (spec_ref if spec_ref.startswith("specification/") else f"specification/{spec_ref}") if spec_ref else None dependencies = [implements] if implements else [] + source_lifecycle = lifecycle(metadata.get("aether-status", "draft"), source_path) + owners = metadata.get("aether-owners", "egohygiene") + owners = string_list([owners] if isinstance(owners, str) else owners, source_path, "metadata.aether-owners", nonempty=True) + compatibility = dict(previous.get("compatibility") or {}) + compatibility["required_tools"] = string_list(fm.get("allowed-tools", []), source_path, "allowed-tools") artifacts.append({ - "artifact_version": str(metadata.get("aether-version", "1.0.0")), - "compatibility": {"required_tools": [str(x) for x in (fm.get("allowed-tools") or []) if isinstance(x, str)]}, + "artifact_version": metadata.get("aether-version", "1.0.0"), + "compatibility": compatibility, "dependencies": dependencies, - "deprecation": {"replacement_id": None}, - "description": str(fm.get("description", "")), - "domain": str(metadata.get("aether-domain", "unknown")), + "deprecation": previous.get("deprecation", {"replacement_id": None}), + "description": fm.get("description", ""), + "domain": metadata.get("aether-domain", "unknown"), "generated_distribution_paths": [f"dist/skills/{name}/SKILL.md"], "id": f"skill/{name}", "implements_specification_id": implements, "kind": "skill", - "license": str(fm.get("license", "MIT")), - "lifecycle": {"state": str(metadata.get("aether-status", "draft"))}, + "license": fm.get("license", "MIT"), + "lifecycle": source_lifecycle, "owner": { - "maintainers": [str(metadata.get("aether-owners", "egohygiene"))], - "primary": str(metadata.get("aether-owners", "egohygiene")), + "maintainers": owners, + "primary": owners[0], }, - "related_ids": [], - "release": { - "included": str(metadata.get("aether-status", "draft")) == "stable", - "reason": "Only stable artifacts are releasable under catalog policy." if str(metadata.get("aether-status", "draft")) != "stable" else "Stable artifact is eligible for release manifests.", - }, - "scope": str(metadata.get("aether-scope", "organization")), + "related_ids": previous.get("related_ids", []), + "release": release_policy(source_lifecycle["state"], previous), + "scope": metadata.get("aether-scope", "organization"), "source_digest": {"algorithm": "sha256-utf8-lf", "value": source_digest}, "source_path": source_path, - "supersedes_ids": [], - "tags": [str(metadata.get("aether-domain", "skill")), "skill"], - "title": " ".join(piece.capitalize() for piece in name.split("-")), + "supersedes_ids": previous.get("supersedes_ids", []), + "tags": previous.get("tags", [metadata.get("aether-domain", "skill"), "skill"]), + "title": previous.get("title", " ".join(piece.capitalize() for piece in name.split("-"))), }) authority = existing_catalog.get("authority") if existing_catalog else None @@ -949,11 +977,28 @@ def generate_catalog(self) -> dict[str, Any]: "allowed_states": ["draft", "experimental", "stable", "deprecated", "retired"], }, } + if self.catalog_schema_path.exists(): + schema = json.loads(self.catalog_schema_path.read_text(encoding="utf-8")) + for err in Draft202012Validator(schema).iter_errors(catalog): + location = list(err.path) + source = "catalog metadata" + if len(location) > 1 and location[0] == "artifacts" and isinstance(location[1], int): + source = catalog["artifacts"][location[1]]["source_path"] + raise ValidationError(f"{source}: invalid catalog metadata at {'/'.join(map(str, location))}: {err.message}") return catalog def validate_catalog(self, check_only: bool = True) -> list[Diagnostic]: diagnostics: list[Diagnostic] = [] - generated = self.generate_catalog() + try: + generated = self.generate_catalog() + except ValidationError as exc: + return [Diagnostic( + rule_id="AETHER_CATALOG_004", + severity="error", + message=str(exc), + guidance="Fix the named canonical source metadata before regenerating the catalog.", + file=_rel(self.catalog_path, self.repo_root), + )] if self.catalog_schema_path.exists(): schema = json.loads(self.catalog_schema_path.read_text(encoding="utf-8")) @@ -971,6 +1016,20 @@ def validate_catalog(self, check_only: bool = True) -> list[Diagnostic]: if self.catalog_path.exists(): current = json.loads(self.catalog_path.read_text(encoding="utf-8")) if current != generated: + current_by_path = {item.get("source_path"): item for item in current.get("artifacts", [])} + for expected in generated["artifacts"]: + actual = current_by_path.get(expected["source_path"], {}) + fields = sorted(key for key in expected if key != "source_digest" and actual.get(key) != expected[key]) + if fields: + diagnostics.append(Diagnostic( + rule_id="AETHER_CATALOG_005", + severity="error", + message=f"Catalog metadata disagrees with canonical source: {', '.join(fields)}.", + guidance="Regenerate the catalog from canonical frontmatter; refreshing only the source digest is insufficient.", + artifact_id=expected["id"], + file=expected["source_path"], + context={"fields": fields}, + )) diagnostics.append(Diagnostic( rule_id="AETHER_CATALOG_002", severity="error", diff --git a/catalog/first-party/catalog.v1.json b/catalog/first-party/catalog.v1.json index 70b580e..715d4a2 100644 --- a/catalog/first-party/catalog.v1.json +++ b/catalog/first-party/catalog.v1.json @@ -235,7 +235,7 @@ "title": "Create Architecture Document" }, { - "artifact_version": "1.1.0", + "artifact_version": "2.1.0", "compatibility": { "required_tools": [] }, @@ -245,7 +245,7 @@ "deprecation": { "replacement_id": null }, - "description": "Creates or updates DECISIONS.md with architectural decision records (ADRs) from repository evidence. Use when a project needs to capture, repair, or review architectural decisions and their rationale.", + "description": "Authors proposed ADRs, reconstructs consequential historical decisions, and updates or references existing records under pinned Hygiene policy. Use when checking decision impact, correcting evidence, proposing supersession, or migrating legacy decision logs.", "domain": "architecture", "generated_distribution_paths": [ "dist/skills/create-decisions-document/SKILL.md" @@ -423,7 +423,7 @@ "title": "Create Diagnosis Record" }, { - "artifact_version": "1.0.0", + "artifact_version": "1.1.0", "compatibility": { "required_tools": [] }, @@ -1128,7 +1128,7 @@ "title": "Create Vision Document" }, { - "artifact_version": "1.1.0", + "artifact_version": "1.2.0", "compatibility": { "required_tools": [] }, @@ -1543,7 +1543,7 @@ "title": "Skill Authoring" }, { - "artifact_version": "1.0.0", + "artifact_version": "1.0.1", "compatibility": { "required_tools": [] }, @@ -1633,7 +1633,7 @@ "title": "Test Engineering" }, { - "artifact_version": "1.0.0", + "artifact_version": "1.1.0", "compatibility": { "required_tools": [] }, @@ -1793,7 +1793,7 @@ "title": "Architecture Document Specification" }, { - "artifact_version": "2.0.0", + "artifact_version": "3.1.0", "compatibility": { "required_tools": [] }, @@ -1976,7 +1976,7 @@ "title": "Design System Architecture Document Specification" }, { - "artifact_version": "2.0.0", + "artifact_version": "2.1.0", "compatibility": { "required_tools": [] }, @@ -2019,7 +2019,8 @@ "specification/architecture-roadmap", "specification/architecture-design", "specification/architecture-design-system", - "specification/architecture-decisions" + "specification/architecture-decisions", + "specification/architecture-set" ], "release": { "included": false, @@ -2696,8 +2697,8 @@ "primary": "egohygiene" }, "related_ids": [ - "skill/architecture-authoring", - "specification/architecture-meta" + "specification/architecture-meta", + "skill/architecture-authoring" ], "release": { "included": false, @@ -3012,10 +3013,10 @@ "primary": "egohygiene" }, "related_ids": [ - "skill/bug-fixing", - "skill/create-diagnosis-record", "specification/auditor", - "specification/repository-journal" + "specification/repository-journal", + "skill/bug-fixing", + "skill/create-diagnosis-record" ], "release": { "included": false, @@ -3067,10 +3068,10 @@ "primary": "egohygiene" }, "related_ids": [ + "specification/diagnosis-record", "skill/maintain-repository-continuity", "skill/test-engineering", - "skill/validate-repository-locally", - "specification/diagnosis-record" + "skill/validate-repository-locally" ], "release": { "included": false, @@ -3173,8 +3174,8 @@ "primary": "egohygiene" }, "related_ids": [ - "skill/maintain-repository-continuity", - "specification/repository-journal" + "specification/repository-journal", + "skill/maintain-repository-continuity" ], "release": { "included": false, @@ -3320,10 +3321,7 @@ ], "primary": "egohygiene" }, - "related_ids": [ - "skill/prepare-social-campaign-handoff", - "skill/social-surface-specs" - ], + "related_ids": [], "release": { "included": false, "reason": "Only stable artifacts are releasable under catalog policy." @@ -3428,9 +3426,9 @@ "primary": "egohygiene" }, "related_ids": [ - "skill/repository-cleanup", + "specification/auditor", "skill/test-engineering", - "specification/auditor" + "skill/repository-cleanup" ], "release": { "included": false, diff --git a/catalog/provenance_model.py b/catalog/provenance_model.py index 68f7f2a..29d28d2 100644 --- a/catalog/provenance_model.py +++ b/catalog/provenance_model.py @@ -124,7 +124,8 @@ def _first_party_record(artifact: dict[str, Any]) -> dict[str, Any]: kind = str(artifact.get("kind", "other")) if kind not in {"specification", "skill", "agent", "prompt", "instruction"}: raise ValueError(f"unsupported first-party artifact kind: {kind}") - lifecycle = normalize_lifecycle((artifact.get("lifecycle") or {}).get("state")) + source_lifecycle = artifact.get("lifecycle") or {} + lifecycle = normalize_lifecycle(source_lifecycle.get("state"), source_state=source_lifecycle.get("source_state")) record = { "id": str(artifact.get("id", "")), "kind": kind, diff --git a/docs/catalog-contribution-guide.md b/docs/catalog-contribution-guide.md index 4fc2f9e..b7fc031 100644 --- a/docs/catalog-contribution-guide.md +++ b/docs/catalog-contribution-guide.md @@ -9,6 +9,22 @@ The first-party catalog is derived from canonical source: Do not hand-edit generated `catalog/first-party/catalog.v1.json` fields that conflict with frontmatter authority. +Regeneration refreshes source-owned versions, lifecycle, descriptions declared +in frontmatter, licenses, owners, domains, scope, and relationships even when +a catalog record already exists. Specification descriptions without a +frontmatter declaration retain their catalog summary; new records use the +first prose line. Specification scope remains catalog-owned. Skill display +titles, tags, related/superseding links, compatibility notes, and deprecation +annotations also remain catalog-owned; `allowed-tools` supplies a skill's +required-tool list. + +The approved specification lifecycle maps to `stable` with +`source_state: approved`. Regeneration preserves explicit release exclusions +and removes inclusion when source becomes non-stable; it does not approve a +release. Invalid source metadata stops generation before the catalog is written. +Catalog validation names the artifact and fields that disagree with source, +even when its stored digest has already been refreshed. + ## Update flow 1. Edit canonical spec/skill frontmatter and body. diff --git a/docs/work/107/HANDOFF.md b/docs/work/107/HANDOFF.md new file mode 100644 index 0000000..9d3997e --- /dev/null +++ b/docs/work/107/HANDOFF.md @@ -0,0 +1,48 @@ +# Issue #107 implementation handoff + +Issue: https://github.com/egohygiene/aether/issues/107 +Roadmap: https://github.com/egohygiene/aether/issues/105 +Role: implementation; stop after draft PR delivery and maintainer sync. +Base: `dc8a16851f452763ef4f7fbe4bf057d4500b1921` (`main`). +Branch: `fix/107-authoritative-catalog-metadata`. +Strategy: repository-owned Bounded Worker Strategy 0.1.0 at the base revision above. + +## Implemented + +- Existing catalog records refresh source-owned metadata instead of updating only their digest. +- Catalog-owned summaries/annotations and explicit release exclusions are retained. Non-stable source cannot remain release-included. +- Approved specification lifecycle normalizes to stable while retaining `source_state: approved`, including normalized provenance. +- Invalid source metadata prevents catalog replacement; field-level drift diagnostics remain independent of digest freshness. +- First-party catalog and contribution guidance are updated; focused regression cases are authored. + +No canonical skill/specification lifecycle or version is promoted. No release, tag, package publication, workflow dispatch, or work on #108–110 is included. + +## Checks actually performed + +- `./aether catalog generate`: completed successfully; artifact generation only. +- Python `ast.parse` on changed Python files: completed successfully; syntax only. +- `git diff --check`: completed successfully; whitespace only. +- Source/diff review: the regenerated catalog reconciles seven stale artifact versions and current source relationships. + +Regression tests have **not** run. No behavioral, integration, merge-readiness or publication claim is made. Commits use `[skip ci]` to suppress existing GitHub push/PR workflows without changing workflow configuration. + +## Deferred completion pass + +Test execution, full deterministic validation, distribution qualification and hosted CI are deferred at the maintainer's request. Run these against the exact final PR head during the later sweep: + +```sh +python3 -m unittest discover --start-directory tests --pattern test_aether_validator.py --verbose +python3 -m unittest discover --start-directory tests --pattern test_provenance_model.py --verbose +./aether catalog generate --check +./aether validate --catalog --provenance --format json +python3 catalog/validate_catalog.py +python3 catalog/provenance_model.py check --scope all +./aether distribution build --output-directory dist +./aether distribution build --output-directory dist --check +``` + +Confirm source version/lifecycle/description/license/dependency mutations update metadata, current-digest stale metadata fails validation, invalid source does not replace the catalog, approved source provenance survives normalization, catalog-owned settings survive, and repeated generation produces identical bytes. The three authored catalog regressions and one provenance regression are unexecuted starting points for that sweep; they do not replace full acceptance review. + +## Next action and boundaries + +Review the draft against issue #107, then run and record the deferred checks before accepting it. Audit PR #106 and its historical artifacts are independent; reconcile handoff conflicts without rewriting dated evidence. Tracker #105 selects #108 next, but this session stops for maintainer sync after #107. diff --git a/tests/test_aether_validator.py b/tests/test_aether_validator.py index f9af845..202bb4f 100644 --- a/tests/test_aether_validator.py +++ b/tests/test_aether_validator.py @@ -1,11 +1,12 @@ from __future__ import annotations +import hashlib import json import tempfile import unittest from pathlib import Path -from aether_validator import AetherValidator, validate_portable_skill_package +from aether_validator import AetherValidator, ValidationError, validate_portable_skill_package def write(path: Path, content: str) -> None: @@ -286,6 +287,75 @@ def test_digest_mismatch(self): diags = fx.validator().validate_catalog(check_only=True) self.assertTrue(any(d.rule_id == "AETHER_CATALOG_002" for d in diags)) + def test_catalog_refreshes_skill_metadata_even_with_current_digest(self): + fx = ValidatorFixture.create() + skill = fx.root / "library/organization/skills/domain/example-skill/SKILL.md" + skill.write_text( + skill.read_text().replace('aether-version: "1.0.0"', 'aether-version: "9.9.9"') + .replace('aether-status: "draft"', 'aether-status: "stable"') + .replace("license: MIT", "license: Apache-2.0") + .replace("Demonstrates a valid skill fixture.", "Describes updated source metadata."), + encoding="utf-8", + ) + catalog = fx.root / "catalog/first-party/catalog.v1.json" + current = json.loads(catalog.read_text()) + old = next(item for item in current["artifacts"] if item["id"] == "skill/example-skill") + old["source_digest"]["value"] = hashlib.sha256(skill.read_bytes()).hexdigest() + catalog.write_text(json.dumps(current), encoding="utf-8") + + diags = fx.validator().validate_catalog() + drift = next(d for d in diags if d.rule_id == "AETHER_CATALOG_005" and d.artifact_id == "skill/example-skill") + self.assertTrue({"artifact_version", "description", "license", "lifecycle"}.issubset(drift.context["fields"])) + fx.validator().write_catalog() + updated = next(item for item in json.loads(catalog.read_text())["artifacts"] if item["id"] == old["id"]) + self.assertEqual(updated["artifact_version"], "9.9.9") + self.assertEqual(updated["lifecycle"], {"state": "stable"}) + self.assertEqual(updated["license"], "Apache-2.0") + self.assertIn("updated source metadata", updated["description"]) + self.assertFalse(updated["release"]["included"]) + + def test_catalog_refreshes_spec_relationships_and_preserves_owned_annotations(self): + fx = ValidatorFixture.create() + spec = fx.root / "library/organization/specs/domain/example.spec.md" + other = spec.with_name("other.spec.md") + other.write_text(spec.read_text().replace("id: example-spec", "id: other-spec"), encoding="utf-8") + spec.write_text( + spec.read_text().replace("version: 1.0.0", "version: 2.0.0") + .replace("status: draft", "status: approved") + .replace("depends_on: []", "depends_on: [other-spec]") + .replace("supersedes: []", "supersedes: [other-spec]"), + encoding="utf-8", + ) + catalog = fx.root / "catalog/first-party/catalog.v1.json" + current = json.loads(catalog.read_text()) + previous = next(item for item in current["artifacts"] if item["id"] == "specification/example-spec") + previous["compatibility"]["notes"] = "Reviewed catalog annotation." + previous["deprecation"]["notes"] = "Retain the migration guidance." + catalog.write_text(json.dumps(current), encoding="utf-8") + fx.validator().write_catalog() + generated = next(item for item in json.loads(catalog.read_text())["artifacts"] if item["id"] == previous["id"]) + self.assertEqual(generated["artifact_version"], "2.0.0") + self.assertEqual(generated["lifecycle"], {"state": "stable", "source_state": "approved"}) + self.assertEqual(generated["dependencies"], ["specification/other-spec"]) + self.assertEqual(generated["supersedes_ids"], ["specification/other-spec"]) + self.assertEqual(generated["compatibility"], previous["compatibility"]) + self.assertEqual(generated["deprecation"], previous["deprecation"]) + first_bytes = catalog.read_bytes() + fx.validator().write_catalog() + self.assertEqual(first_bytes, catalog.read_bytes()) + + def test_catalog_rejects_invalid_source_without_replacing_catalog(self): + fx = ValidatorFixture.create() + catalog = fx.root / "catalog/first-party/catalog.v1.json" + before = catalog.read_bytes() + spec = fx.root / "library/organization/specs/domain/example.spec.md" + spec.write_text(spec.read_text().replace("depends_on: []", "depends_on: invalid-string"), encoding="utf-8") + with self.assertRaisesRegex(ValidationError, "example.spec.md: depends_on"): + fx.validator().write_catalog() + self.assertEqual(before, catalog.read_bytes()) + diagnostics = fx.validator().validate_catalog() + self.assertTrue(any(d.rule_id == "AETHER_CATALOG_004" and "depends_on" in d.message for d in diagnostics)) + def test_unclassified_staging_material(self): fx = ValidatorFixture.create() (fx.root / ".staging" / "unknown").mkdir(parents=True) diff --git a/tests/test_provenance_model.py b/tests/test_provenance_model.py index 7958053..abd7a0d 100644 --- a/tests/test_provenance_model.py +++ b/tests/test_provenance_model.py @@ -13,6 +13,7 @@ sys.path.insert(0, str(ROOT / "catalog")) from provenance_model import ( # noqa: E402 + _first_party_record, build_external, build_first_party, canonical_bytes, @@ -70,6 +71,13 @@ def test_normalized_output_is_deterministic(self) -> None: second = build_first_party() self.assertEqual(canonical_bytes(first), canonical_bytes(second)) + def test_first_party_normalization_retains_approved_source_state(self) -> None: + catalog = json.loads((ROOT / "catalog/first-party/catalog.v1.json").read_text(encoding="utf-8")) + artifact = copy.deepcopy(next(item for item in catalog["artifacts"] if item["id"] == "specification/auditor")) + artifact["lifecycle"] = {"state": "stable", "source_state": "approved"} + record = _first_party_record(artifact) + self.assertEqual(record["lifecycle"], {"state": "stable", "source_state": "approved"}) + if __name__ == "__main__": unittest.main() From 59debca82d8bc836fa0f2842a50582800d88c9f9 Mon Sep 17 00:00:00 2001 From: Alan Szmyt Date: Tue, 10 Nov 2026 01:19:21 +0000 Subject: [PATCH 2/2] docs(catalog): reconcile metadata prerequisite for audit profiles [skip ci] --- CONTINUITY.md | 172 ++++++++++++++++++--------------------- docs/work/107/HANDOFF.md | 28 +++++-- 2 files changed, 101 insertions(+), 99 deletions(-) diff --git a/CONTINUITY.md b/CONTINUITY.md index a0e1f60..80040d4 100644 --- a/CONTINUITY.md +++ b/CONTINUITY.md @@ -7,25 +7,26 @@ repository: continuity_path: CONTINUITY.md document: status: active - updated_at: '2026-11-03T16:15:19Z' + updated_at: '2026-11-10T01:19:21Z' max_bytes: 16384 max_lines: 240 stale_reason: null superseded_by: null scope: - purpose: Integrate distribution containment PR118 with the merged evaluator repair while preserving - both source scopes and their qualification limits. - includes: &id001 - - Preserve the exact evaluator plus stable-ID admission, both guarded builders and all prior regression - cases. - - Reconcile one current checkpoint against verified main and retain deterministic valid distribution/catalog - bytes. - - Complete only the authorized source integration; retain remaining tests, ADR/policy disposition and - publication boundaries. + purpose: Reconcile the existing source-authoritative catalog correction with current Aether before adding + composable audit and coherence contracts. + includes: + - Existing catalog entries refresh source-owned version, lifecycle, license, owner and dependency metadata + rather than only refreshing the source digest. Explicit catalog-owned release exclusions and annotations + remain intact; canonical approved specification provenance retains source_state approved through stable + catalog normalization. + - The existing correction is merged locally with current main's evaluator/distribution containment changes. + Both sets of authored regression imports and cases are preserved; the two overlapping handoffs are reconciled + semantically. The historical audit report and its independent draft remain unchanged. + - The root handoff records this narrow metadata prerequisite for the Audits/Sanity source additions. No skill/specification + lifecycle, release, tag, workflow or human authority changes. excludes: - - New functionality beyond the existing evaluator/distribution scopes, catalog115 work, issue closure - and publication. - - Evaluation execution, regression suites, official EgoLint conformance and hosted CI. + - Deferred runtime, tests, builds, CI, live publication, human ratification and acceptance closeout. precedence: - user-and-runtime-instructions - scoped-repository-instructions @@ -34,98 +35,79 @@ scope: - continuity-checkpoint canonical_sources: - AGENTS.md - - library/organization/skills/build-distributions.py - - library/organization/specs/build-distributions.py - - aether_validator.py - - tests/test_build_distributions.py - - tests/test_build_spec_distributions.py - - tests/test_aether_validator.py - - docs/distribution-paths.md - - README.md - - https://github.com/egohygiene/aether/issues/110 + - ARCHITECTURE.md + - SYSTEM.md + - DECISIONS.md + - catalog/README.md + - docs/catalog-contribution-guide.md + - docs/work/107/HANDOFF.md + - https://github.com/egohygiene/aether/issues/107 work: - objective: Integrate distribution containment PR118 with the merged evaluator repair while preserving - both source scopes and their qualification limits. - success_conditions: *id001 + objective: Reconcile the existing source-authoritative catalog correction with current Aether before adding + composable audit and coherence contracts. + success_conditions: + - Catalog regeneration can derive newly authored source metadata without retaining stale existing fields. + - Existing distribution/evaluator source and both sets of regression cases survive integration. + - Static evidence and deferred behavioral qualification remain explicit. active_issue: provider: github - id: egohygiene/aether#110 - url: https://github.com/egohygiene/aether/issues/110 + id: egohygiene/aether#107 + url: https://github.com/egohygiene/aether/issues/107 next: kind: action - id: review-draft-and-qualify - description: Sync on tracker105 and select the next genuine source work, including staging-control111 - or external-review112; preserve catalog115 and all evaluator/distribution qualification gaps. + id: author-audit-and-coherence-contracts + description: Integrate the separately prepared Aether 86 audit-profile/report and Aether 87 architecture-coherence + source contracts, then regenerate canonical catalog and derived distribution metadata without promoting + draft lifecycle. Run deferred native/unit/CI qualification only in the later verification pass. readiness: ready references: - - https://github.com/egohygiene/aether/issues/110 + - https://github.com/egohygiene/aether/issues/107 depends_on: [] state: base: - revision: b09cd9d8bb1edc2bc1a44e91125a8a772b55d593 + revision: 5077797448d32a59b06eabe4b38f091137d58052 ref: refs/heads/main - verified_at: '2026-11-03T16:15:19Z' + verified_at: '2026-11-10T01:19:21Z' candidate: - branch: codex/110-confined-distribution-paths + branch: fix/107-authoritative-catalog-metadata revision: null pull_request: provider: github - id: egohygiene/aether#118 - url: https://github.com/egohygiene/aether/pull/118 + id: egohygiene/aether#115 + url: https://github.com/egohygiene/aether/pull/115 handoff_state: review-reference-recorded live: status: verified - observed_at: '2026-11-03T16:15:19Z' - default_branch_revision: b09cd9d8bb1edc2bc1a44e91125a8a772b55d593 + observed_at: '2026-11-10T01:19:21Z' + default_branch_revision: 5077797448d32a59b06eabe4b38f091137d58052 issue_state: open - pull_request_state: draft - notes: Verified mainb09cd9d contains merged evaluator117 and earlier distribution116. PR118 is draft - before its authorized ready/merge transition and integrates that exact base. Catalog115 and audit106 - remain separately open. No regression pass or publication is inferred. - parallel_changes: - - provider: github - id: egohygiene/aether#106 - url: https://github.com/egohygiene/aether/pull/106 - - provider: github - id: egohygiene/aether#115 - url: https://github.com/egohygiene/aether/pull/115 + pull_request_state: open + notes: Aether main 5077797448d32a59b06eabe4b38f091137d58052 and open issue 107/PR 115 were verified. Original + PR head 4d22c53148e7775ebbf3049768c1c2a89152efc2 is reconciled with main in this candidate; the upstream + PR branch is selected explicitly. Aether 86/87 remain separate in-progress source slices. + parallel_changes: [] review: status: partial - reviewed_at: '2026-11-03T16:15:19Z' + reviewed_at: '2026-11-10T01:19:21Z' reviewed_by: Codex evidence: - - command: Exact runtime/source comparisons and qualified regression-name inventory union - outcome: passed - observed_at: '2026-11-03T16:15:19Z' - notes: Evaluator main plus the exact stable-ID guard matches the merged validator; builders/path guide - equal118, evaluation guide equals117, and all 50 validator regression names remain once. - - command: AST parse six combined Python sources and git diff --check - outcome: passed - observed_at: '2026-11-03T16:15:19Z' - notes: Runtime and regression sources parse; no test or evaluation case executes. - - command: ./aether distribution build --output-directory dist; ./aether distribution build --output-directory - dist --check; ./aether catalog generate - outcome: passed - observed_at: '2026-11-03T16:15:19Z' - notes: All combined-source commands exit0. Tracked dist/catalog bytes remain unchanged. - - command: Pinned continuity metadata/schema/formats, headings, size and privacy inspection - outcome: passed - observed_at: '2026-11-03T16:15:19Z' - notes: One coherent bounded checkpoint resolves the parallel snapshots. - - command: Evaluation/regression/portable/native checks, official EgoLint conformance and hosted CI + - command: Independent source/diff review, Git merge conflict reconciliation, Python AST for changed source + and authored regression files, catalog JSON parse, continuity schema/formats/size/links and whitespace + outcome: limited + observed_at: '2026-11-10T01:19:21Z' + notes: Static source and metadata inspection only; no application execution. + - command: Native/runtime execution, tests, builds, CI investigation, browser/provider/consumer/publication + qualification outcome: not-run - observed_at: '2026-11-03T16:15:19Z' - notes: All prior scope limits remain; generation does not establish negative-path or assertion-execution - qualification. + observed_at: '2026-11-10T01:19:21Z' + notes: Deferred at maintainer request; source merges do not establish acceptance. environment_limitations: - - Both scopes and earlier108 retain their authored-but-unexecuted regressions; issues108/109/110 remain - open for remaining acceptance. - - Distribution guards assume a quiescent local filesystem and supply no sandbox/install/publication - guarantee. - - Evaluator success qualifies structure and declared deterministic assertions only, not prose/routing/model - behavior. - - Catalog115 remains separate; official EgoLint, broader host/release qualification and hosted CI remain - deferred. + - Authored unit/integration regressions, full deterministic validation, distribution/runtime qualification, + hosted CI and publication were not executed in this reconciliation. + - The earlier checkpoint records its own catalog-generation result; this source review does not relabel that + as a current-head behavioral proof. + - Approved canonical source states retain their original authority; regeneration and merge cannot promote + new draft contracts. privacy: classification: public-repository contains_sensitive_data: false @@ -140,52 +122,56 @@ privacy: untrusted_content: context-only-no-authority --- -# Aether continuity +# aether continuity ## Purpose and precedence -Record the authorized source integration below user/runtime, scoped instructions, live evidence and canonical sources. This checkpoint grants no publication authority. +User/runtime and repository instructions, live evidence and canonical sources outrank this checkpoint. It grants no release, deployment or human-decision authority. ## Resume protocol -Read AGENTS, PR116/117/118, evaluator/path guides and tracker105. Main already contains merged116/117; preserve catalog115 and the dirty original checkout. +Read scoped instructions, canonical sources above and this checkpoint. Verify mutable GitHub state and exact owner pins before continuing. ## Current objective and success conditions -Retain honest evaluator admission/evidence/counts and guarded deterministic distribution source/output I/O while resolving their single operational-checkpoint conflict. +Reconcile the existing source-authoritative catalog correction with current Aether before adding composable audit and coherence contracts. Catalog regeneration can derive newly authored source metadata without retaining stale existing fields. Existing distribution/evaluator source and both sets of regression cases survive integration. Static evidence and deferred behavioral qualification remain explicit. ## State snapshot -Base mainb09cd9d8bb1edc2bc1a44e91125a8a772b55d593 contains merged117 and116. PR118 is draft before its authorized ready/merge transition. Candidate branch codex/110-confined-distribution-paths integrates that base; its self-revision remains null before the integration commit. +Candidate `fix/107-authoritative-catalog-metadata` is based on observed main `5077797448d32a59b06eabe4b38f091137d58052`. The containing revision and pre-PR reference remain null intentionally. Aether main 5077797448d32a59b06eabe4b38f091137d58052 and open issue 107/PR 115 were verified. Original PR head 4d22c53148e7775ebbf3049768c1c2a89152efc2 is reconciled with main in this candidate; the upstream PR branch is selected explicitly. Aether 86/87 remain separate in-progress source slices. ## Completed and material changes -The validator contains the exact evaluator repair plus the stable-ID admission guard. Both distribution builders and their tests/path guide remain exactly118; evaluator documentation remains117. All qualified regression names are retained once. Only CONTINUITY required conflict resolution. +Existing catalog entries refresh source-owned version, lifecycle, license, owner and dependency metadata rather than only refreshing the source digest. Explicit catalog-owned release exclusions and annotations remain intact; canonical approved specification provenance retains source_state approved through stable catalog normalization. + +The existing correction is merged locally with current main's evaluator/distribution containment changes. Both sets of authored regression imports and cases are preserved; the two overlapping handoffs are reconciled semantically. The historical audit report and its independent draft remain unchanged. + +The root handoff records this narrow metadata prerequisite for the Audits/Sanity source additions. No skill/specification lifecycle, release, tag, workflow or human authority changes. ## Validation and review evidence -Six combined Python sources parse. Exact source/test-inventory comparisons, whitespace and continuity schema/formats/heading/budget/privacy checks pass. Actual distribution build/check and catalog regeneration exit0 with unchanged derived bytes. No regression or evaluation executes. +Independent source/diff review, Git merge conflict reconciliation, Python AST for changed source and authored regression files, catalog JSON parse, continuity schema/formats/size/links and whitespace. Static inspection only. Native/runtime execution, tests, builds, CI investigation, browsers, provider reads and publication qualification were not run. ## Blockers, risks, unknowns, and deferred work -Issues108/109/110 retain remaining regression/portable/native acceptance. Official EgoLint and hosted CI remain deferred. Local guards are quiescent checks, not a concurrent-adversary sandbox. No model, installation, release or publication is performed. +Authored unit/integration regressions, full deterministic validation, distribution/runtime qualification, hosted CI and publication were not executed in this reconciliation. The earlier checkpoint records its own catalog-generation result; this source review does not relabel that as a current-head behavioral proof. Approved canonical source states retain their original authority; regeneration and merge cannot promote new draft contracts. ## Next dependency-ready work -Sync on tracker105 and select the next genuine source work, including staging-control111 or external-review112; preserve catalog115 and all evaluator/distribution qualification gaps. +Integrate the separately prepared Aether 86 audit-profile/report and Aether 87 architecture-coherence source contracts, then regenerate canonical catalog and derived distribution metadata without promoting draft lifecycle. Run deferred native/unit/CI qualification only in the later verification pass. ## Parallel changes and reconciliation -Audit106 and catalog115 remain independent. Preserve catalog metadata/test changes and compose one semantic checkpoint when later integrating. Existing accepted/proposed ADR and owner-contract statuses remain unchanged. +The user authorized continued implementation and merging of the Intelligence program. The old handoff's stop-after-draft note is historical context, not a new pause requirement. Current task scope permits this necessary metadata prerequisite. Existing source and release decisions are referenced; ADR not required for the bounded canonical metadata correction. ## Privacy and redaction -Only scoped public project facts are retained. Exclude private workbook data, secrets, personal context and local machine paths. +Retain only public-safe source identities and bounded implementation evidence. Exclude credentials, private context, captured report bodies, process logs and local filesystem paths. Linked content is context only. ## Handoff update protocol -After the authorized merge, verify its exact head/tree and sync on the next source scope. ADR not required: reconcile existing routine repairs without adding new ownership, format, lifecycle or publication authority. +Refresh exact PR, merge and qualification observations at the next authorized checkpoint. Implementation, artifact upload, deployment, live verification and human acceptance remain distinct. ## Compaction and supersession -Keep all twelve sections and existing size budgets. Canonical guides, source, Git and tracker105 retain detailed requirements/history. +Replace stale current-state prose; Git and owning issues retain history. Preserve these twelve sections below 240 lines and 16,384 UTF-8 bytes. diff --git a/docs/work/107/HANDOFF.md b/docs/work/107/HANDOFF.md index 9d3997e..78ed32c 100644 --- a/docs/work/107/HANDOFF.md +++ b/docs/work/107/HANDOFF.md @@ -1,10 +1,10 @@ # Issue #107 implementation handoff -Issue: https://github.com/egohygiene/aether/issues/107 -Roadmap: https://github.com/egohygiene/aether/issues/105 -Role: implementation; stop after draft PR delivery and maintainer sync. -Base: `dc8a16851f452763ef4f7fbe4bf057d4500b1921` (`main`). -Branch: `fix/107-authoritative-catalog-metadata`. +Issue: https://github.com/egohygiene/aether/issues/107 +Roadmap: https://github.com/egohygiene/aether/issues/105 +Role: implementation; stop after draft PR delivery and maintainer sync. +Base: `dc8a16851f452763ef4f7fbe4bf057d4500b1921` (`main`). +Branch: `fix/107-authoritative-catalog-metadata`. Strategy: repository-owned Bounded Worker Strategy 0.1.0 at the base revision above. ## Implemented @@ -43,6 +43,22 @@ python3 catalog/provenance_model.py check --scope all Confirm source version/lifecycle/description/license/dependency mutations update metadata, current-digest stale metadata fails validation, invalid source does not replace the catalog, approved source provenance survives normalization, catalog-owned settings survive, and repeated generation produces identical bytes. The three authored catalog regressions and one provenance regression are unexecuted starting points for that sweep; they do not replace full acceptance review. -## Next action and boundaries +## Original next action and boundaries (historical) Review the draft against issue #107, then run and record the deferred checks before accepting it. Audit PR #106 and its historical artifacts are independent; reconcile handoff conflicts without rewriting dated evidence. Tracker #105 selects #108 next, but this session stops for maintainer sync after #107. + +## Current reconciliation for Audits/Sanity source work + +The current authorized continuation reconciles original source +`4d22c53148e7775ebbf3049768c1c2a89152efc2` with main +`5077797448d32a59b06eabe4b38f091137d58052`. Evaluator/distribution containment +source and both sets of authored regression imports/cases are retained. The +updated root continuity records PR #115, static source/JSON/AST and merge +inspection, and the deferred qualification scope. The original stop-after-draft +note describes that earlier checkpoint; it does not override the maintainer's +subsequent implementation/merge authorization. Audit PR #106 remains independent. + +This correction is a metadata prerequisite for the new Aether #86/#87 contracts. +Their canonical source, generated catalog and distribution projections are +separate slices. No canonical lifecycle or human approval is promoted, and no +regression, native, CI, distribution or publication acceptance is claimed here.