--- id: MAS-WP-0002 type: workplan title: "MAS v0.1 reference experiment" domain: communication repo: mail-address-semantics status: done owner: claude topic_slug: coulomb-social created: "2026-10-08" updated: "2026-10-18" state_hub_workstream_id: "17d1228a-bfd1-5fed-ac90-a314b64f6f5b" --- # MAS v0.1 reference experiment ### Objective Build one runnable reference experiment for MAS v0.1. The experiment should validate the full path: ```text address → parse → discovery → policy → mail gateway → ticket resource → subsequent resource-addressed mail ``` The plan deliberately avoids parallel language implementations and infrastructure-specific integrations until the model has survived one complete implementation. --- ## Milestone 0 — Establish the implementation baseline ```task id: MAS-WP-0002-T01 status: done priority: high state_hub_task_id: "4cd7534b-0175-5213-851b-1416cabc4366" ``` ### Goal Turn the specification bundle into executable project inputs. ### Work Import or vendor: ```text SPECIFICATION.md grammar/mas-v0.1.abnf schema/mas-statute-v0.1.schema.json conformance/addresses.json ``` Establish the repository structure. Define Python package boundaries: ```text mas_core.model mas_core.parser mas_core.discovery mas_core.policy mas_core.statute mas_core.lint ``` Define an explicit version: ```text MAS 0.1 implementation 0.1.0-dev ``` ### Deliverable A repository where: ```text pytest ``` runs the existing fixture corpus. --- ## Milestone 1 — Core parser and model ```task id: MAS-WP-0002-T02 status: done priority: high state_hub_task_id: "099c957c-1598-5888-b1f5-74e8e90d38fc" ``` ### Goal Implement the specification without network or namespace policy. ### Core model Define immutable objects such as: ```text MasAddress Target OpaqueTarget ResourceTarget Facet Interaction RequiredProfile ``` Example: ```text ticket=PR42+comments!callback ``` should parse to approximately: ```text MasAddress target: ResourceTarget type = ticket id = PR42 facet = comments interaction = callback required_profile = P2 ``` Critically: ```text ticket!callback ``` must parse successfully in the maximal parser and report: ```text required_profile = P2 ``` The parser must not require knowledge of whether a particular namespace enables P2. ### Parser APIs Recommended: ```python parse_local_part(value) -> ParsedLocalPart parse_address(value) -> ParsedAddress validate_profile(parsed, profile) -> ValidationResult ``` Avoid coupling syntax parsing to statute policy. ### Property cases to add Extend beyond the existing fixtures. #### Length boundaries Test: ```text 63 octets 64 octets 65 octets ``` The transport limit applies after encoding. #### Additional `=` Validate: ```text ticket==PR42 ``` as: ```text resource_type = ticket resource_id = =PR42 ``` because only the first `=` is structural. #### Reserved operators Ensure: ```text $ ^ ``` always fail canonical v0.1 parsing. #### Registry independence Ensure a registry key such as: ```text support ``` is legal while: ```text support+foo ``` is not interpreted as a registry key lookup for the full faceted address. Registry lookup should operate on the base target. ### Exit criteria - all existing fixtures pass; - new property cases pass; - parser performs zero I/O; - parser does not infer person/actor/function kind. --- ## Milestone 2 — Statute model and linting ```task id: MAS-WP-0002-T03 status: done priority: high state_hub_task_id: "90f803d5-9956-5a67-9100-629c5c1a1376" ``` ### Goal Make namespace semantics explicit and verifiable. ### Work Implement statute loading and JSON Schema validation. Add semantic validation beyond the schema. Checks should include: #### Namespace consistency A statute fetched for: ```text acme.coulomb.social ``` must declare: ```text namespace = acme.coulomb.social ``` or fail with: ```text NamespaceMismatch ``` #### P2 consistency Reject or lint: ```text profiles.p2 = false ``` together with enabled interaction modes. #### Resource type validity Resource type names must themselves satisfy MAS resource-type syntax. #### Registry key validity Explicit address registry keys must refer to base opaque targets only. For v0.1, registry keys should not contain: ```text + ! $ ^ ``` #### Inheritance cycles Detect: ```text A → B → A ``` and longer cycles. #### Child precedence Where inheritance is supported: ```text child overrides inherited defaults ``` Explicit address registry entries must not inherit. ### CLI Implement: ```text mas lint-statute FILE ``` Output should distinguish: ```text schema error semantic error warning ``` ### Exit criteria A malformed but schema-valid statute can still be rejected by semantic linting. --- ## Milestone 3 — Exact-host discovery ```task id: MAS-WP-0002-T04 status: done priority: high state_hub_task_id: "c220fde2-3f6f-5d32-9dd8-dfdadf2f636a" ``` ### Goal Resolve namespace metadata without contaminating parsing or policy. ### API Recommended: ```python discover(host) -> DiscoveryResult ``` with successful result: ```text ValidatedStatute ``` and typed failures such as: ```text NotFound TransportFailure InvalidContentType SchemaInvalid NamespaceMismatch CrossHostRedirect InheritanceCycle UnsupportedVersion ``` ### Redirect rule Before adding caching, define redirect semantics precisely. Initial rule: > **Follow same-host HTTPS redirects only. Reject cross-host redirects.** A redirect must not transfer MAS authority to another hostname. Scheme downgrade to HTTP must be rejected. ### Parent domains Never perform implicit lookup such as: ```text acme.coulomb.social → coulomb.social ``` Inheritance occurs only through an explicit statute declaration. ### Caching Add only minimal caching after behavior is correct. Cache successful statutes separately from failures. Do not let cache behavior hide typed discovery failures. ### Exit criteria Discovery is deterministic enough to test against a local HTTP fixture server. --- ## Milestone 4 — Policy resolver ```task id: MAS-WP-0002-T05 status: done priority: high state_hub_task_id: "940a1636-a0de-5697-8e8e-cb4181cf9b76" ``` ### Goal Combine syntax and validated metadata without network access. ### API Conceptually: ```python resolve( parsed_address, statute, local_registry=None, ) -> AddressSemantics ``` Policy must remain pure. ### Resolution examples With no statute: ```text bernd.worsch@example.org ``` resolves only to syntactic information: ```text target = bernd.worsch kind = opaque ``` It must not become: ```text kind = person ``` from punctuation. With statute registry: ```text addresses: bernd.worsch: kind: person ``` the resolver may produce: ```text kind = person ``` For: ```text ticket=PR42 ``` and a statute declaring `ticket`: ```text kind = resource resource_type = ticket resource_id = PR42 ``` ### Profile validation For: ```text ticket!callback ``` if statute says: ```text p2 = false ``` policy should return a typed semantic failure such as: ```text ProfileNotEnabled ``` The syntax remains valid. ### Exit criteria The complete policy test suite runs with network disabled. --- ## Milestone 5 — `mas` CLI ```task id: MAS-WP-0002-T06 status: done priority: high state_hub_task_id: "6aedba08-4220-599e-8ad3-01fa99cf3c16" ``` ### Goal Provide the main inspection surface for developers and operators. ### Commands #### Parse ```text mas parse ADDRESS ``` Shows syntax only. #### Discover ```text mas discover HOST ``` Shows validated statute or typed failure. #### Explain ```text mas explain ADDRESS ``` Shows three distinct layers: ```text Syntax Namespace discovery Semantic resolution ``` Example failure: ```text Address: bernd.worsch@example.org Syntax: target: bernd.worsch required profile: P1 Discovery: no valid MAS statute found Semantics: kind: opaque ``` This is preferable to displaying a guessed “person”. #### Lint statute ```text mas lint-statute FILE ``` #### Run conformance suite ```text mas test ``` ### Exit criteria A human can understand exactly why an address received its interpretation. --- ## Milestone 6 — Static statute deployment ```task id: MAS-WP-0002-T07 status: done priority: medium state_hub_task_id: "bb1e8e08-83a8-5122-a556-26b3d7ac6017" ``` ### Goal Test real exact-host discovery. Serve one validated static statute at: ```text /.well-known/mas ``` No framework-specific statute server should be built yet. The deployment should advertise only the features needed by the reference service. Example: ```text P1 = enabled P2 = enabled resource_types: ticket interaction_modes: callback ``` ### Exit criteria `mas discover` retrieves the live/local test statute and validates namespace consistency. --- ## Milestone 7 — Generic mail-to-HTTP gateway ```task id: MAS-WP-0002-T08 status: done priority: medium state_hub_task_id: "896df692-3b8d-50ea-bdee-262e4e4854b7" ``` ### Goal Bridge ordinary inbound mail into an application-neutral MAS event. ### Input Either: ```text SMTP ``` or: ```text provider inbound webhook ``` Choose whichever produces the smallest reliable prototype. ### Gateway behavior For each inbound recipient: 1. parse address; 2. discover statute; 3. resolve policy; 4. produce normalized event; 5. POST event to configured HTTP application endpoint. Example event: ```json { "recipient": "ticket=GS-41872@example.org", "mas": { "profile": "P1", "kind": "resource", "resource_type": "ticket", "resource_id": "GS-41872" }, "message": { "from": "customer@example.net", "subject": "The problem happened again" } } ``` The gateway must not contain ticket-specific logic. ### Exit criteria The same gateway could theoretically route another MAS resource type without code changes. --- ## Milestone 8 — Reference ticket service ```task id: MAS-WP-0002-T09 status: done priority: medium state_hub_task_id: "35069786-a79f-5256-a8c9-a0637bcebd51" ``` ### Goal Prove the canonical lifecycle. ### Flow A — create Customer sends: ```text ticket!callback@great-service.example ``` The service: 1. receives a normalized MAS event; 2. verifies P2 is enabled; 3. creates ticket; 4. records callback as requested interaction mode; 5. generates resource address: ```text ticket=GS-41872@great-service.example ``` 6. replies using that resource address. The subject does not require a ticket prefix. --- ### Flow B — correlate Customer later sends or replies to: ```text ticket=GS-41872@great-service.example ``` The service: 1. resolves resource type and id; 2. locates GS-41872; 3. associates the new message with that resource. No subject-line parsing is required. --- ### Flow C — closed resource Ticket GS-41872 is closed. Mail later arrives at: ```text ticket=GS-41872@great-service.example ``` The address remains valid. The service must apply explicit lifecycle policy. For the prototype, choose one behavior such as: ```text archive message + return "ticket closed" response ``` It must not silently mutate or reopen the ticket. --- ### Flow D — unknown resource Mail arrives at: ```text ticket=DOES-NOT-EXIST@great-service.example ``` The service must handle this deliberately and without generating abusive backscatter. --- ## Milestone 9 — Experiment review > Findings: `docs/experiment-review.md`. MTA half tested against real Postfix (`experiments/postfix-probe/`); provider half continues in MAS-WP-0003. ```task id: MAS-WP-0002-T10 status: done priority: medium state_hub_task_id: "31bbfb56-5541-5ace-aff7-94ed6f1df57a" ``` ### Goal Decide whether MAS v0.1 survives contact with implementation. Review: #### Syntax Did the parser remain simple? #### Discovery Was exact-host metadata sufficient? #### Policy Could all inference remain explicit? #### Email infrastructure Which characters or address forms were rewritten or rejected? #### Resource addressing Did resource addresses actually improve correlation? #### Subject lines Could application identifiers genuinely disappear from subjects? #### Lifecycle Did closed resources remain understandable? #### Interaction Was `!callback` useful or did it require more negotiation semantics? #### Security Did any address accidentally start behaving like a capability? --- ## Decision gate Only after the reference experiment should the project decide whether to build: ```text TypeScript implementation Go implementation Postfix/Exim integration mail-client inspector address composer signed statutes P3 economic profile ``` Each addition should answer a demonstrated need from the experiment. **Decided 2026-10-18 (founder)** on the evidence of this workplan and MAS-WP-0003 (`docs/experiment-review.md`, `docs/spec-change-proposals.md`): - **Build:** the Postfix integration profile (MAS-WP-0004); spec v0.1.1 errata plus v0.2 drafts for SCP-6/SCP-7 (MAS-WP-0005); and the address composer (MAS-WP-0006), pulled forward because provider forms refuse `=` and lower-case addresses. - **Defer:** the TypeScript and Go implementations, the mail-client inspector, signed statutes, and the P3 economic profile. None of them had a demonstrated need. --- ## Initial execution order The shortest path is: ```text 1. mas-core parser/model 2. expanded conformance corpus 3. statute validator/linter 4. exact-host discovery 5. pure policy resolver 6. mas CLI 7. static statute 8. generic inbound gateway 9. reference ticket service 10. experiment review ``` The governing rule for this phase is: > **One implementation, one lifecycle, one real email path.**