MAS v0.2 final (MAS-WP-0009 finished); residual MAS-WP-0014
Founder decision after the second implementation: close the two open gaps, then declare v0.2 final. Section 13: parsers MUST reject over 64 octets (fixtures p1-050/051); section 8: equivalence is between targets, key format not normative. Draft markers removed, CHANGELOG dated 2026-10-19, MAS_VERSION 0.2, mas_conformance vendors the v0.2 addresses.json. Mode contract v0.1 stays draft: MAS-WP-0014 (independent email side). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 5787@bnt-lap001 Assistant-Session: 5db26b82-138b-40cc-9856-4d5eb0f752fd
This commit is contained in:
parent
a841ceec73
commit
f07576451a
16 changed files with 178 additions and 40 deletions
43
SCOPE.md
43
SCOPE.md
|
|
@ -7,8 +7,10 @@ Reference implementation and spec workbench for Mail Address Semantics
|
||||||
framework. Contents: parser, statute discovery, pure policy, generator,
|
framework. Contents: parser, statute discovery, pure policy, generator,
|
||||||
CLI, mail gateway, reference ticket service, a tested Postfix integration
|
CLI, mail gateway, reference ticket service, a tested Postfix integration
|
||||||
profile, and the mode contract with an email side that works as master or
|
profile, and the mode contract with an email side that works as master or
|
||||||
slave. It implements address spec v0.1.1, the v0.2 draft and mode contract
|
slave. It implements address spec v0.1.1 and v0.2 (final), and mode
|
||||||
v0.1 (draft).
|
contract v0.1 (draft). A second, independent implementation in
|
||||||
|
JavaScript (`impl/js/`) validates v0.2 and the master side of the mode
|
||||||
|
contract.
|
||||||
|
|
||||||
## Core Idea
|
## Core Idea
|
||||||
|
|
||||||
|
|
@ -23,9 +25,13 @@ delivery stays external.
|
||||||
## In Scope
|
## In Scope
|
||||||
|
|
||||||
- **Normative bundles.** `mas-v0.1-spec/` (v0.1.1 errata) and
|
- **Normative bundles.** `mas-v0.1-spec/` (v0.1.1 errata) and
|
||||||
`mas-v0.2-spec/` (draft: stand-ins, destination transforms,
|
`mas-v0.2-spec/` (final 2026-10-19: stand-ins, destination transforms,
|
||||||
`resource_id_case`). Spec changes go through
|
`resource_id_case`). Spec changes go through
|
||||||
`docs/spec-change-proposals.md`.
|
`docs/spec-change-proposals.md`.
|
||||||
|
- **`impl/js/`.** The second implementation: parser, statute-aware rules,
|
||||||
|
signing, outside master. Zero dependencies; `tests/test_js_impl.py` and
|
||||||
|
the differential test `tests/test_differential.py` run it when Node is
|
||||||
|
available.
|
||||||
- **`mas_core`.** Parser, model, statute validation and lint, exact-host
|
- **`mas_core`.** Parser, model, statute validation and lint, exact-host
|
||||||
discovery with explicit `inherits`, policy, and the generator with v0.2
|
discovery with explicit `inherits`, policy, and the generator with v0.2
|
||||||
destination transforms.
|
destination transforms.
|
||||||
|
|
@ -62,9 +68,8 @@ delivery stays external.
|
||||||
- Real non-email modes (telephony, chat, SMS, video), connectors to real
|
- Real non-email modes (telephony, chat, SMS, video), connectors to real
|
||||||
CMS or ticketing products, and the multimodal framework itself. Other
|
CMS or ticketing products, and the multimodal framework itself. Other
|
||||||
modes exist here only as contract-speaking test stubs.
|
modes exist here only as contract-speaking test stubs.
|
||||||
- Implementations in other languages (TypeScript, Go), until a second
|
- Further language implementations beyond `impl/js/`, and `impl/js/` as
|
||||||
consumer exists. That is also the gate for declaring v0.2 final
|
a published package.
|
||||||
(MAS-WP-0009).
|
|
||||||
- Postfix policy daemon, Exim profile (until there is demand), mail-client
|
- Postfix policy daemon, Exim profile (until there is demand), mail-client
|
||||||
plugins and inspectors, browser extensions, a web composer.
|
plugins and inspectors, browser extensions, a web composer.
|
||||||
- Central MAS registry, signed statutes, implicit parent-domain
|
- Central MAS registry, signed statutes, implicit parent-domain
|
||||||
|
|
@ -81,26 +86,28 @@ delivery stays external.
|
||||||
- Status: active. The reference experiment (MAS-WP-0002) passed its
|
- Status: active. The reference experiment (MAS-WP-0002) passed its
|
||||||
decision gate on 2026-10-18; the findings are in
|
decision gate on 2026-10-18; the findings are in
|
||||||
`docs/experiment-review.md`.
|
`docs/experiment-review.md`.
|
||||||
- Finished: MAS-WP-0001 to 0006, 0008, 0010 and 0011 (peer
|
- Finished: MAS-WP-0001 to 0006 and 0008 to 0013. That covers v0.2,
|
||||||
authentication in the mode contract). Postfix profile, v0.1.1
|
which is final; the mode contract with authentication, one outbound
|
||||||
|
path and harness-independent flows; and the second implementation. Postfix profile, v0.1.1
|
||||||
errata, v0.2 draft, address composer, v0.2 conformance cases, and the
|
errata, v0.2 draft, address composer, v0.2 conformance cases, and the
|
||||||
mode contract with its reference implementation.
|
mode contract with its reference implementation.
|
||||||
- Open: MAS-WP-0007 (retire the live MX; backlog, kept by decision),
|
- Open: MAS-WP-0007 (retire the live MX; backlog, kept by decision) and
|
||||||
MAS-WP-0009 (declare v0.2 final; waits for a second implementation),
|
MAS-WP-0014 (finalize mode contract v0.1; waits for an independent email
|
||||||
and MAS-WP-0012 and 0013 (proposed).
|
side).
|
||||||
- v0.2 stays a draft (founder, 2026-10-19).
|
- v0.2 is final (founder, 2026-10-19, after MAS-WP-0009). Mode contract
|
||||||
|
v0.1 stays a draft.
|
||||||
- P2 is decided: `!MODE` requests a handoff (founder, 2026-10-19; SCP-8,
|
- P2 is decided: `!MODE` requests a handoff (founder, 2026-10-19; SCP-8,
|
||||||
v0.2 §7.1). The mode contract v0.1 draft and the reference
|
v0.2 §7.1). The mode contract v0.1 draft and the reference
|
||||||
implementation are done (MAS-WP-0010); `mas test` runs the contract
|
implementation are done (MAS-WP-0010); `mas test` runs the contract
|
||||||
corpus.
|
corpus.
|
||||||
- The live MX runs image `mas-live:v0.2-a95f848` (redeployed 2026-10-19)
|
- The live MX runs image `mas-live:v0.2-a95f848` (redeployed 2026-10-19)
|
||||||
and is kept up by founder decision (MAS-WP-0007 stays in backlog).
|
and is kept up by founder decision (MAS-WP-0007 stays in backlog).
|
||||||
Open follow-ups: MAS-WP-0012 (one outbound path and cap), 0013
|
The deployment with MAS-WP-0012 (one outbound path) is recorded in
|
||||||
(harness-independent flows), 0009 (second implementation, now covering
|
MAS-WP-0007.
|
||||||
both drafts).
|
- Tests: `.venv/bin/pytest` runs 274 tests, offline apart from loopback
|
||||||
- Tests: `.venv/bin/pytest` runs 249 tests, offline apart from loopback
|
fixtures, the Docker probe and Node for `impl/js/`. `mas test` runs
|
||||||
fixtures and the Docker probe. `mas test` runs four corpora: parser
|
four corpora: parser 64, statute 16, v0.2 statute-aware 33, mode
|
||||||
62, statute 16, v0.2 statute-aware 29, mode contract 194.
|
contract 195. `cd impl/js && node --test` runs 85 more.
|
||||||
- Assessments: `history/` (latest: `2026-10-19T090725Z-scope-vs-intent.md`).
|
- Assessments: `history/` (latest: `2026-10-19T090725Z-scope-vs-intent.md`).
|
||||||
|
|
||||||
## Getting Oriented
|
## Getting Oriented
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ export class MasSyntaxError extends Error {
|
||||||
*/
|
*/
|
||||||
export function parseLocalPart(local, profile = "p2") {
|
export function parseLocalPart(local, profile = "p2") {
|
||||||
if (typeof local !== "string" || local.length === 0) throw new MasSyntaxError("empty");
|
if (typeof local !== "string" || local.length === 0) throw new MasSyntaxError("empty");
|
||||||
|
if (octets(local) > MAX_OCTETS) throw new MasSyntaxError("too-long", "over 64 octets (section 13)");
|
||||||
if (/[^\x21-\x7e]/.test(local)) throw new MasSyntaxError("non-ascii", "outside the ASCII grammar");
|
if (/[^\x21-\x7e]/.test(local)) throw new MasSyntaxError("non-ascii", "outside the ASCII grammar");
|
||||||
if (/[$^]/.test(local)) throw new MasSyntaxError("reserved-operator");
|
if (/[$^]/.test(local)) throw new MasSyntaxError("reserved-operator");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,16 @@
|
||||||
# MAS specification changelog
|
# MAS specification changelog
|
||||||
|
|
||||||
## v0.2 (draft)
|
## v0.2 (2026-10-19)
|
||||||
|
|
||||||
|
Declared final by the founder after the second implementation
|
||||||
|
(MAS-WP-0009): `impl/js`, written from the texts alone, passes every
|
||||||
|
fixture unchanged and agrees with `mas_core` on 1500 generated
|
||||||
|
local-parts. Closed before release:
|
||||||
|
|
||||||
|
- **Length, §13.** A parser MUST reject a local-part over 64 octets.
|
||||||
|
Fixtures p1-050 (64 octets, valid) and p1-051 (65 octets, invalid).
|
||||||
|
- **Equivalence, §8.** Equivalence is between targets (facets and
|
||||||
|
interactions take no part). The key format is not normative.
|
||||||
|
|
||||||
New features, opt-in per statute with `"mas_version": "0.2"`
|
New features, opt-in per statute with `"mas_version": "0.2"`
|
||||||
(mail-address-semantics MAS-WP-0005; proposals SCP-6 and SCP-7 in
|
(mail-address-semantics MAS-WP-0005; proposals SCP-6 and SCP-7 in
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# MAS v0.2 Conformance (draft)
|
# MAS v0.2 Conformance
|
||||||
|
|
||||||
## 1. Test surfaces
|
## 1. Test surfaces
|
||||||
|
|
||||||
|
|
@ -102,4 +102,4 @@ python reference/mas_parser.py --self-test
|
||||||
```
|
```
|
||||||
|
|
||||||
The reference parser is non-normative; the grammar, specification, schema,
|
The reference parser is non-normative; the grammar, specification, schema,
|
||||||
and fixtures are normative for this draft.
|
and fixtures are normative for v0.2.
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
# Mail Address Semantics (MAS) v0.2 (draft) — Specification Bundle
|
# Mail Address Semantics (MAS) v0.2 — Specification Bundle
|
||||||
|
|
||||||
This bundle is the **draft** of MAS v0.2. It extends v0.1.1 (see `CHANGELOG.md`).
|
This bundle is MAS v0.2, final as of 2026-10-19. It extends v0.1.1 (see
|
||||||
|
`CHANGELOG.md`). Two independent implementations pass its fixtures
|
||||||
|
unchanged: `mas_core` (Python) and `impl/js` (JavaScript).
|
||||||
|
|
||||||
## Contents
|
## Contents
|
||||||
|
|
||||||
- `SPECIFICATION.md` — normative protocol/convention text (v0.2 draft)
|
- `SPECIFICATION.md` — normative protocol/convention text (v0.2)
|
||||||
- `CHANGELOG.md` — errata applied since v0.1
|
- `CHANGELOG.md` — errata applied since v0.1
|
||||||
- `grammar/mas-v0.2.abnf` — canonical ASCII local-part grammar (unchanged from v0.1.1)
|
- `grammar/mas-v0.2.abnf` — canonical ASCII local-part grammar (unchanged from v0.1.1)
|
||||||
- `schema/mas-statute-v0.2.schema.json` — statute discovery document schema
|
- `schema/mas-statute-v0.2.schema.json` — statute discovery document schema
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# Mail Address Semantics (MAS) v0.2 (draft)
|
# Mail Address Semantics (MAS) v0.2
|
||||||
|
|
||||||
## 1. Status and normative language
|
## 1. Status and normative language
|
||||||
|
|
||||||
This document is a **draft** of MAS v0.2. It includes v0.1.1 and adds
|
This document is MAS v0.2 (final, 2026-10-19). It includes v0.1.1 and adds
|
||||||
statute-enabled stand-ins for `=` and `!`, destination transforms for
|
statute-enabled stand-ins for `=` and `!`, destination transforms for
|
||||||
generators, and a statute-declared resource-identifier case rule (see
|
generators, and a statute-declared resource-identifier case rule (see
|
||||||
`CHANGELOG.md`). The canonical grammar is unchanged from v0.1.1.
|
`CHANGELOG.md`). The canonical grammar is unchanged from v0.1.1.
|
||||||
|
|
@ -344,6 +344,13 @@ A statute MAY declare:
|
||||||
|
|
||||||
Dot folding MUST NOT be assumed by default.
|
Dot folding MUST NOT be assumed by default.
|
||||||
|
|
||||||
|
Equivalence is a property of **targets**. Two addresses are equivalent when
|
||||||
|
their targets are, after any stand-ins are read (§6.3) and under the rules
|
||||||
|
above. Facets and interactions take no part: `ticket=PR42+comments` and
|
||||||
|
`ticket=PR42` name the same target (§4.2). How an implementation compares
|
||||||
|
targets, for example through a normalized key, is not normative. Only the
|
||||||
|
resulting equivalence is.
|
||||||
|
|
||||||
A v0.2 statute MAY declare `equivalence.resource_id_case`:
|
A v0.2 statute MAY declare `equivalence.resource_id_case`:
|
||||||
|
|
||||||
- `exact` (default): resource identifiers compare exactly, subject to
|
- `exact` (default): resource identifiers compare exactly, subject to
|
||||||
|
|
@ -527,7 +534,8 @@ members in lower case, so it needs only `lowercase`.
|
||||||
|
|
||||||
## 13. Length
|
## 13. Length
|
||||||
|
|
||||||
An SMTP local-part is limited to 64 octets.
|
An SMTP local-part is limited to 64 octets. A parser MUST reject a longer
|
||||||
|
local-part as non-canonical: it cannot be transported over SMTP.
|
||||||
|
|
||||||
Generators MUST enforce the underlying transport limit after encoding and
|
Generators MUST enforce the underlying transport limit after encoding and
|
||||||
after destination transforms. `--` adds one octet and `---` adds two.
|
after destination transforms. `--` adds one octet and `---` adds two.
|
||||||
|
|
|
||||||
|
|
@ -308,7 +308,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "p1-041",
|
"id": "p1-041",
|
||||||
"local_part": "tïcket=PR42",
|
"local_part": "t\u00efcket=PR42",
|
||||||
"profile": "p1",
|
"profile": "p1",
|
||||||
"valid": false,
|
"valid": false,
|
||||||
"error": "non-ASCII outside canonical MAS v0.1"
|
"error": "non-ASCII outside canonical MAS v0.1"
|
||||||
|
|
@ -350,5 +350,28 @@
|
||||||
"facet": null,
|
"facet": null,
|
||||||
"interaction": null
|
"interaction": null
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "p1-050",
|
||||||
|
"local_part": "ticket=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||||
|
"profile": "p1",
|
||||||
|
"valid": true,
|
||||||
|
"parsed": {
|
||||||
|
"target": "ticket=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||||
|
"target_kind": "resource",
|
||||||
|
"resource_type": "ticket",
|
||||||
|
"resource_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||||
|
"facet": null,
|
||||||
|
"interaction": null
|
||||||
|
},
|
||||||
|
"note": "64 octets: the SMTP limit (section 13)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "p1-051",
|
||||||
|
"local_part": "ticket=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||||
|
"profile": "p1",
|
||||||
|
"valid": false,
|
||||||
|
"error": "longer than 64 octets",
|
||||||
|
"note": "v0.2: parsers MUST reject over 64 octets (section 13)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
; Mail Address Semantics (MAS) v0.2 (draft)
|
; Mail Address Semantics (MAS) v0.2
|
||||||
; The canonical grammar is unchanged from v0.1.1. The v0.2 stand-ins
|
; The canonical grammar is unchanged from v0.1.1. The v0.2 stand-ins
|
||||||
; ("--" for "=", "---" for "!") are not canonical syntax: they parse as
|
; ("--" for "=", "---" for "!") are not canonical syntax: they parse as
|
||||||
; opaque targets and are interpreted only by statute-aware
|
; opaque targets and are interpreted only by statute-aware
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Non-normative MAS v0.1 canonical ASCII reference parser."""
|
"""Non-normative MAS canonical ASCII reference parser (grammar unchanged since v0.1.1)."""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
|
@ -21,7 +21,7 @@ def parse(local_part, profile="p1"):
|
||||||
try:
|
try:
|
||||||
local_part.encode("ascii")
|
local_part.encode("ascii")
|
||||||
except UnicodeEncodeError:
|
except UnicodeEncodeError:
|
||||||
raise ValueError("non-ASCII outside canonical MAS v0.1")
|
raise ValueError("non-ASCII outside canonical MAS")
|
||||||
|
|
||||||
if '"' in local_part or "\\" in local_part or "@" in local_part:
|
if '"' in local_part or "\\" in local_part or "@" in local_part:
|
||||||
raise ValueError("quoted/escaped/full address outside canonical local-part grammar")
|
raise ValueError("quoted/escaped/full address outside canonical local-part grammar")
|
||||||
|
|
@ -114,7 +114,7 @@ def self_test():
|
||||||
if failures:
|
if failures:
|
||||||
print(json.dumps(failures, indent=2))
|
print(json.dumps(failures, indent=2))
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
print(f"PASS: {len(fixtures)} MAS v0.1 fixtures")
|
print(f"PASS: {len(fixtures)} MAS v0.2 fixtures")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
ap = argparse.ArgumentParser()
|
ap = argparse.ArgumentParser()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
"""MAS v0.1 conformance corpus.
|
"""MAS v0.1 conformance corpus.
|
||||||
|
|
||||||
`addresses.json` is vendored unchanged from the spec bundle
|
`addresses.json` is vendored unchanged from the spec bundle
|
||||||
(`mas-v0.1-spec/conformance/addresses.json`) and is normative.
|
(`mas-v0.2-spec/conformance/addresses.json`, a superset of v0.1.1's) and is
|
||||||
|
normative.
|
||||||
`properties.json` adds implementation-independent property and boundary
|
`properties.json` adds implementation-independent property and boundary
|
||||||
cases. `statutes/` holds statute validation cases. `statute_aware.json` is
|
cases. `statutes/` holds statute validation cases. `statute_aware.json` is
|
||||||
vendored unchanged from the v0.2 draft bundle
|
vendored unchanged from the v0.2 draft bundle
|
||||||
|
|
|
||||||
|
|
@ -308,7 +308,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "p1-041",
|
"id": "p1-041",
|
||||||
"local_part": "tïcket=PR42",
|
"local_part": "t\u00efcket=PR42",
|
||||||
"profile": "p1",
|
"profile": "p1",
|
||||||
"valid": false,
|
"valid": false,
|
||||||
"error": "non-ASCII outside canonical MAS v0.1"
|
"error": "non-ASCII outside canonical MAS v0.1"
|
||||||
|
|
@ -350,5 +350,28 @@
|
||||||
"facet": null,
|
"facet": null,
|
||||||
"interaction": null
|
"interaction": null
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "p1-050",
|
||||||
|
"local_part": "ticket=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||||
|
"profile": "p1",
|
||||||
|
"valid": true,
|
||||||
|
"parsed": {
|
||||||
|
"target": "ticket=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||||
|
"target_kind": "resource",
|
||||||
|
"resource_type": "ticket",
|
||||||
|
"resource_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||||
|
"facet": null,
|
||||||
|
"interaction": null
|
||||||
|
},
|
||||||
|
"note": "64 octets: the SMTP limit (section 13)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "p1-051",
|
||||||
|
"local_part": "ticket=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||||
|
"profile": "p1",
|
||||||
|
"valid": false,
|
||||||
|
"error": "longer than 64 octets",
|
||||||
|
"note": "v0.2: parsers MUST reject over 64 octets (section 13)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
"""Mail Address Semantics (MAS) v0.1 reference implementation.
|
"""Mail Address Semantics (MAS) reference implementation: address spec v0.2, mode contract v0.1.
|
||||||
|
|
||||||
Parsing is local and deterministic. Discovery is networked. Policy is pure.
|
Parsing is local and deterministic. Discovery is networked. Policy is pure.
|
||||||
Delivery remains external.
|
Delivery remains external.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
MAS_VERSION = "0.1"
|
MAS_VERSION = "0.2"
|
||||||
__version__ = "0.1.0.dev0"
|
__version__ = "0.1.0.dev0"
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
||||||
[project]
|
[project]
|
||||||
name = "mas-core"
|
name = "mas-core"
|
||||||
version = "0.1.0.dev0"
|
version = "0.1.0.dev0"
|
||||||
description = "Mail Address Semantics (MAS) v0.1 reference implementation"
|
description = "Mail Address Semantics (MAS) reference implementation (address spec v0.2, mode contract v0.1)"
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
dependencies = ["jsonschema>=4.18"]
|
dependencies = ["jsonschema>=4.18"]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ FIXTURES = address_fixtures() + property_fixtures()
|
||||||
|
|
||||||
|
|
||||||
def test_vendored_corpus_matches_spec_bundle():
|
def test_vendored_corpus_matches_spec_bundle():
|
||||||
spec = json.loads((ROOT / "mas-v0.1-spec/conformance/addresses.json").read_text())
|
spec = json.loads((ROOT / "mas-v0.2-spec/conformance/addresses.json").read_text())
|
||||||
assert spec == address_fixtures()
|
assert spec == address_fixtures()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ type: workplan
|
||||||
title: "Declare MAS v0.2 final"
|
title: "Declare MAS v0.2 final"
|
||||||
domain: communication
|
domain: communication
|
||||||
repo: mail-address-semantics
|
repo: mail-address-semantics
|
||||||
status: active
|
status: finished
|
||||||
owner: claude
|
owner: claude
|
||||||
topic_slug: coulomb-social
|
topic_slug: coulomb-social
|
||||||
created: "2026-10-19"
|
created: "2026-10-19"
|
||||||
|
|
@ -43,7 +43,7 @@ read in a way `mas_core` does not.
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: MAS-WP-0009-T02
|
id: MAS-WP-0009-T02
|
||||||
status: progress
|
status: done
|
||||||
priority: low
|
priority: low
|
||||||
state_hub_task_id: "a30c1b69-4b41-5636-865a-82341175668b"
|
state_hub_task_id: "a30c1b69-4b41-5636-865a-82341175668b"
|
||||||
```
|
```
|
||||||
|
|
@ -80,3 +80,19 @@ in the changelog.
|
||||||
and the JS parser does not. No fixture covers it.
|
and the JS parser does not. No fixture covers it.
|
||||||
- **Not independently validated:** the email side of the mode contract.
|
- **Not independently validated:** the email side of the mode contract.
|
||||||
It exists only in Python; the JS work covers the master side.
|
It exists only in Python; the JS work covers the master side.
|
||||||
|
|
||||||
|
## Outcome of T02 (2026-10-19)
|
||||||
|
|
||||||
|
- **Founder decision: close the open gaps, then declare v0.2 final.**
|
||||||
|
- Length (§13): parsers MUST reject over 64 octets. Fixtures p1-050 and
|
||||||
|
p1-051. The JS parser was aligned; `mas_core` already did this.
|
||||||
|
- Equivalence (§8): it is between targets (facets and interactions take
|
||||||
|
no part), and the key format is not normative.
|
||||||
|
- **v0.2 is final** (2026-10-19). "Draft" is removed from the bundle and
|
||||||
|
`MAS_VERSION` is 0.2. `mas_conformance/addresses.json` now vendors the
|
||||||
|
v0.2 bundle (a superset of v0.1.1's).
|
||||||
|
- **Results.** Both implementations pass every fixture: `mas test` parser
|
||||||
|
64/64 and statute-aware 33/33; JS `node --test` 85/85. They agree on
|
||||||
|
every generated case in the differential test.
|
||||||
|
- **Mode contract v0.1 stays a draft** (founder). Residual: MAS-WP-0014
|
||||||
|
(an independent email side).
|
||||||
|
|
|
||||||
47
workplans/MAS-WP-0014-finalize-mode-contract.md
Normal file
47
workplans/MAS-WP-0014-finalize-mode-contract.md
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
---
|
||||||
|
id: MAS-WP-0014
|
||||||
|
type: workplan
|
||||||
|
title: "Finalize mode contract v0.1: an independent email side"
|
||||||
|
domain: communication
|
||||||
|
repo: mail-address-semantics
|
||||||
|
status: backlog
|
||||||
|
owner: claude
|
||||||
|
topic_slug: coulomb-social
|
||||||
|
created: "2026-10-19"
|
||||||
|
updated: "2026-10-19"
|
||||||
|
origin: residual
|
||||||
|
origin_ref: MAS-WP-0009
|
||||||
|
---
|
||||||
|
|
||||||
|
# Finalize mode contract v0.1
|
||||||
|
|
||||||
|
Residual of MAS-WP-0009. The founder decided on 2026-10-19 that mode
|
||||||
|
contract v0.1 stays a draft. Its master side is validated independently
|
||||||
|
(`impl/js/src/master.mjs` passes the slave-setup flows), but the email side
|
||||||
|
exists only in Python (`mas_modes.email_mode`).
|
||||||
|
|
||||||
|
## An independent email side, or a real integration
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: MAS-WP-0014-T01
|
||||||
|
status: wait
|
||||||
|
priority: low
|
||||||
|
```
|
||||||
|
|
||||||
|
Do one of these. Either build an email side independent of
|
||||||
|
`mas_modes.email_mode`, in another language or by another team, that
|
||||||
|
passes `mas-modes-v0.1-spec/conformance/flows.json` (both roles) from the
|
||||||
|
bundle alone. Or run a real outside master or mode integration (a CMS,
|
||||||
|
ticketing, telephony) against the contract and record the findings. Fold
|
||||||
|
the findings into CONTRACT.md as proposals, as SCP-9 did for v0.2.
|
||||||
|
|
||||||
|
## Declare final
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: MAS-WP-0014-T02
|
||||||
|
status: wait
|
||||||
|
priority: low
|
||||||
|
```
|
||||||
|
|
||||||
|
With the findings folded in, ask the founder to declare mode contract v0.1
|
||||||
|
final. Then remove "draft" from the bundle and v0.2 §7.1.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue