Skip to content

Commit a13bffd

Browse files
Close registry validator review gaps
1 parent 82e6cae commit a13bffd

16 files changed

Lines changed: 343 additions & 16 deletions

File tree

‎.github/workflows/validate.yml‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ jobs:
2424
- run: python tests/test_generated_views.py
2525
- run: python tests/test_markdown_links.py
2626
- run: python tests/test_bridge_config.py
27+
- run: python tests/test_tag_promotion.py
2728
- name: Enforce tag-promotion separation
2829
if: github.event_name == 'pull_request'
2930
run: python scripts/check_tag_promotion.py "${{ github.event.pull_request.base.sha }}" "${{ github.sha }}"
31+
- name: Detect tag-promotion separation violation on push
32+
if: github.event_name == 'push'
33+
env:
34+
BEFORE: ${{ github.event.before }}
35+
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
36+
run: |
37+
if [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then
38+
base="$(git merge-base "origin/$DEFAULT_BRANCH" "$GITHUB_SHA")"
39+
else
40+
base="$BEFORE"
41+
fi
42+
python scripts/check_tag_promotion.py "$base" "$GITHUB_SHA"

‎docs/localization/ZH_CN_VALIDATION.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ The validation process surfaced several pre-existing canonical design questions
114114
- corpus-import wording contains a sequencing question between manifest-first fallback and the missing-full-import-approval stop condition;
115115
- some Markdown and JSON artifacts use different canonical field names, including `source_session` / `target_session` versus `source_ai` / `target_ai`, and `session_family` / `human_relay_needed` versus `visitor_family` / `relay_needed`;
116116
- the public/reference runtime boundary could be stated more explicitly in a future canonical protocol clarification.
117+
- Registry Contract v1 now treats CSV files and `registry/INDEX.md` as generated compatibility views; `locales/zh-CN/registry/README.md` retains the earlier legacy/optional-rollup wording and is queued for Wave 4 correction.
117118

118119
Any repair to those items should be made as an explicit English-source protocol/schema decision and then propagated to localized surfaces.
119120

‎docs/registry/REGISTRY_CONTRACT_V1.md‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Every canonical JSON record must contain:
2525
- its type-specific immutable ID;
2626
- `created_at` as an RFC 3339 timestamp;
2727
- a repository-relative artifact `path` where that record type has an artifact;
28-
- a type-specific `status`.
28+
- a type-specific `status` where that record type has a lifecycle.
2929

3030
The validator will check IDs, locations, timestamps, path existence, references, tags, and lifecycle/path agreement. It will not claim that a Git author proves a visitor identity or human approval.
3131

@@ -51,7 +51,7 @@ A response record indexes a response artifact. It carries response identity, res
5151

5252
### Message
5353

54-
A message record carries message identity, sender, individual and/or group destination, state, path, tags, and the distinct optional relations `reply_to`, `reply_expected`, `response_message_id`, `needs_human_relay`, `related_packet`, `related_response`, and `summary`.
54+
A message record carries message identity, sender, individual and/or group destination, state, path, tags, and the distinct optional relations `reply_to`, `reply_expected`, `response_message_id`, `needs_human_relay`, `related_packet`, `related_response`, and `summary`. When both `to_visitor_id` and `to_group` are populated, the message is addressed to both; neither destination overrides or suppresses the other.
5555

5656
### Notification
5757

@@ -63,11 +63,11 @@ A notification record carries notification identity, `from_visitor_id`, `to_visi
6363

6464
A visit is an append-only signoff record and has no lifecycle status. It uses `session_family` and `relay_needed`.
6565

66-
Visitor registration is canonical JSON-per-record under `registry/visitors/<visitor_id>.json`; the visitor CSV is a derived compatibility view. Visitor lifecycle is separate from visit records.
66+
Visitor registration is canonical JSON-per-record under `registry/visitors/<visitor_id>.json`; the visitor CSV is a derived compatibility view. Visitor lifecycle is separate from visit records. A routing record may name an unregistered visitor handle; registration is not a prerequisite for message, notification, or visit routing.
6767

6868
### Tag
6969

70-
A tag record is the canonical vocabulary record. Tags are `proposed`, `accepted`, or `deprecated`, with directories that match their status. Tag records retain creator, creation timestamp, and—when accepted—`accepted_by`, `accepted_at`, and `acceptance_basis`.
70+
A tag record is the canonical vocabulary record. Tags are `proposed`, `accepted`, or `deprecated`, with directories that match their status. Tag records retain creator, creation timestamp, and—when accepted—`accepted_by`, `accepted_at`, and `acceptance_basis`. Those fields are an attestation recorded by the repository: schema validation checks their presence and form, but does not authenticate operator identity or prove approval. Enforced approval requires repository review rules outside this schema.
7171

7272
## Lifecycle and storage buckets
7373

@@ -120,7 +120,7 @@ Packets and responses carry `created_by`, `deposited_by`, `content_origin`, `sou
120120

121121
Third-party, web, and mixed material requires source references. Derivative/summarising material requires upstream references and coverage. Routine routing summaries require record-level provenance; evidence or decision synthesis requires claim-level support where practical and must preserve uncertainty.
122122

123-
AI/session-created tags begin proposed and cannot become accepted in the same change set. Operator-supplied tags may be accepted directly with `acceptance_basis: operator_supplied`. Promotion of a proposal uses `acceptance_basis: operator_approved_promotion`.
123+
AI/session-created tags begin proposed and cannot become accepted in the same change set. Operator-supplied tags may be accepted directly with `acceptance_basis: operator_supplied`. Promotion of a proposal uses `acceptance_basis: operator_approved_promotion`. Pull-request validation rejects a same-change-set promotion; push validation detects the equivalent direct-write violation for remediation, but cannot undo a commit already accepted by the remote.
124124

125125
## Compatibility
126126

‎docs/registry/SCHEMA_AND_VALIDATOR_DESIGN_V1.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ tests/fixtures/invalid/
2323
.github/workflows/validate.yml
2424
```
2525

26-
Schemas use JSON Schema draft 2020-12. Every record declares `record_type` and integer `schema_version`; the validator verifies both the declared type and the repository path. It does not choose a schema from path alone.
26+
Schemas use JSON Schema draft 2020-12. Every record declares `record_type` and integer `schema_version`; the validator allowlists the declared type, chooses its schema from that type, and verifies its agreement with the canonical directory. It does not choose a schema from path alone.
2727

2828
## Shared rules
2929

@@ -37,7 +37,7 @@ IDs are validated as complete date-led values, then checked against filename and
3737

3838
Packets and responses require `created_by`, `deposited_by`, `content_origin`, `source_refs`, `derivative_of`, and `provenance_coverage` according to the contract.
3939

40-
For `third_party`, `web`, and `mixed`, `source_refs` must be non-empty. For a derivative/summarising artifact, `derivative_of` and coverage must be non-empty. The schema checks field presence/shape; the validator checks referenced local artifact IDs.
40+
For `third_party`, `web`, and `mixed`, `source_refs` must be non-empty. For a derivative/summarising artifact, `derivative_of` and coverage must be non-empty. The schema checks field presence/shape; the validator resolves `derivative_of` entries to packet or response records in the validation set. Visitor identifiers remain routing handles and do not require a corresponding visitor-registration record.
4141

4242
Messages may carry provenance when relaying source-bearing content. Notifications inherit through their required `message_id`. Visits do not require provenance.
4343

@@ -46,12 +46,12 @@ Messages may carry provenance when relaying source-bearing content. Notification
4646
1. Load every canonical registry JSON record.
4747
2. Validate JSON Schema.
4848
3. Assert ID equals filename stem.
49-
4. Assert timestamp/date-prefix/year-directory agreement.
49+
4. Assert identifier-date/`created_at` agreement and, where a canonical registry record is filed under a four-digit year directory, year-directory agreement.
5050
5. Assert path safety, existence and artifact/registry bucket agreement.
5151
6. Assert uniqueness of IDs by namespace.
52-
7. Resolve packet, response, message, notification, tag and derivative references.
52+
7. Resolve packet, response, message, notification, tag and derivative references; visitor handles remain intentionally unregistered-capable.
5353
8. Check tag status/path agreement and proposed/accepted/deprecated lifecycle rules.
54-
9. Compare a PR base/head when available to reject illegal state transitions, including AI-proposed tags accepted in the same change set.
54+
9. Compare a PR base/head when available to reject illegal state transitions, including AI-proposed tags accepted in the same change set. Push validation detects the same condition after a direct write; repository review rules are required if prevention is required before acceptance.
5555
10. Validate examples as isolated fixtures.
5656
11. Check canonical Markdown links.
5757
12. Later: regenerate CSV/index views in a temporary directory and fail on diff.
@@ -60,7 +60,7 @@ The validator reports record, field, invariant and repair direction. It never ex
6060

6161
## Fixtures
6262

63-
Each valid fixture contains a complete fictional public-safe artifact/record pair. Invalid fixtures each violate one named invariant: schema type, missing path, filename mismatch, status bucket mismatch, unresolved reference, invalid tag, illegal transition, or missing required provenance.
63+
Each valid fixture contains a complete fictional public-safe artifact/record pair. Invalid fixtures each violate one named invariant: schema type, unselectable shared schema, mismatched canonical location, missing path, filename mismatch, identifier-date mismatch, duplicate ID, status bucket mismatch, unresolved reference, invalid tag, illegal transition, or missing required provenance.
6464

6565
Top-level examples will be migrated into these fixtures before the validator is required on main.
6666

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","additionalProperties":false,"required":["record_type","schema_version","visitor_id","created_at","session_family","session_type","display_name","status","last_seen","profile_path","notes"],"properties":{"record_type":{"const":"visitor"},"schema_version":{"$ref":"common.schema.json#/$defs/schema_version"},"visitor_id":{"type":"string","pattern":"^[a-z][a-z0-9-]*-[0-9]{8}-[0-9]{4}-[a-z0-9][a-z0-9-]*-[0-9]{2}$"},"created_at":{"$ref":"common.schema.json#/$defs/timestamp"},"session_family":{"type":"string","minLength":1},"session_type":{"type":"string","minLength":1},"display_name":{"type":"string","minLength":1},"status":{"enum":["registered","active","dormant","retired","superseded"]},"last_seen":{"type":["string","null"],"format":"date-time"},"profile_path":{"type":["string","null"],"pattern":"^(?!/)(?!.*(?:^|/)\\\\.\\\\.(?:/|$))[A-Za-z0-9._/-]+$"},"notes":{"type":"string"}}}
1+
{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","additionalProperties":false,"required":["record_type","schema_version","visitor_id","created_at","session_family","session_type","display_name","status","last_seen","profile_path","notes"],"properties":{"record_type":{"const":"visitor"},"schema_version":{"$ref":"common.schema.json#/$defs/schema_version"},"visitor_id":{"type":"string","pattern":"^[a-z][a-z0-9-]*-[0-9]{8}-[0-9]{4}-[a-z0-9][a-z0-9-]*-[0-9]{2}$"},"created_at":{"$ref":"common.schema.json#/$defs/timestamp"},"session_family":{"type":"string","minLength":1},"session_type":{"type":"string","minLength":1},"display_name":{"type":"string","minLength":1},"status":{"enum":["registered","active","dormant","retired","superseded"]},"last_seen":{"type":["string","null"],"format":"date-time"},"profile_path":{"anyOf":[{"$ref":"common.schema.json#/$defs/safe_path"},{"type":"null"}]},"notes":{"type":"string"}}}

‎registry/visitors/.gitkeep‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

‎requirements-dev.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
jsonschema>=4.23,<5
2+
referencing>=0.35,<1

‎scripts/check_tag_promotion.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
def changed_files(base: str, head: str) -> list[str]:
1111
result = subprocess.run(
12-
["git", "diff", "--name-only", f"{base}...{head}"],
12+
["git", "diff", "--no-renames", "--name-only", f"{base}...{head}"],
1313
text=True,
1414
capture_output=True,
1515
check=False,

0 commit comments

Comments
 (0)