- experiments/postfix-probe: Postfix 3.7 -> pipe -> mas-gateway -> ticket service in Docker; 14 address forms x 5 configurations. 13/14 arrive byte-identical everywhere; quoted "ticket=PR42" is unquoted in transit. Legacy bang/percent rewrites never fire for MAS addresses. - docs/experiment-review.md: Email infrastructure section with results - MAS-WP-0003: provider-side live test on a real subdomain (needs DNS/MX) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 623586@bnt-lap001 Assistant-Session: 10b30928-5b3b-4c11-9934-5ecca07367d8
13 KiB
| id | type | title | domain | repo | status | owner | topic_slug | created | updated | state_hub_workstream_id |
|---|---|---|---|---|---|---|---|---|---|---|
| MAS-WP-0002 | workplan | MAS v0.1 reference experiment | communication | mail-address-semantics | done | claude | coulomb-social | 2026-10-08 | 2026-10-08 | 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:
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
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:
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:
mas_core.model
mas_core.parser
mas_core.discovery
mas_core.policy
mas_core.statute
mas_core.lint
Define an explicit version:
MAS 0.1
implementation 0.1.0-dev
Deliverable
A repository where:
pytest
runs the existing fixture corpus.
Milestone 1 — Core parser and model
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:
MasAddress
Target
OpaqueTarget
ResourceTarget
Facet
Interaction
RequiredProfile
Example:
ticket=PR42+comments!callback
should parse to approximately:
MasAddress
target:
ResourceTarget
type = ticket
id = PR42
facet = comments
interaction = callback
required_profile = P2
Critically:
ticket!callback
must parse successfully in the maximal parser and report:
required_profile = P2
The parser must not require knowledge of whether a particular namespace enables P2.
Parser APIs
Recommended:
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:
63 octets
64 octets
65 octets
The transport limit applies after encoding.
Additional =
Validate:
ticket==PR42
as:
resource_type = ticket
resource_id = =PR42
because only the first = is structural.
Reserved operators
Ensure:
$
^
always fail canonical v0.1 parsing.
Registry independence
Ensure a registry key such as:
support
is legal while:
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
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:
acme.coulomb.social
must declare:
namespace = acme.coulomb.social
or fail with:
NamespaceMismatch
P2 consistency
Reject or lint:
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:
+
!
$
^
Inheritance cycles
Detect:
A → B → A
and longer cycles.
Child precedence
Where inheritance is supported:
child overrides inherited defaults
Explicit address registry entries must not inherit.
CLI
Implement:
mas lint-statute FILE
Output should distinguish:
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
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:
discover(host) -> DiscoveryResult
with successful result:
ValidatedStatute
and typed failures such as:
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:
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
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:
resolve(
parsed_address,
statute,
local_registry=None,
) -> AddressSemantics
Policy must remain pure.
Resolution examples
With no statute:
bernd.worsch@example.org
resolves only to syntactic information:
target = bernd.worsch
kind = opaque
It must not become:
kind = person
from punctuation.
With statute registry:
addresses:
bernd.worsch:
kind: person
the resolver may produce:
kind = person
For:
ticket=PR42
and a statute declaring ticket:
kind = resource
resource_type = ticket
resource_id = PR42
Profile validation
For:
ticket!callback
if statute says:
p2 = false
policy should return a typed semantic failure such as:
ProfileNotEnabled
The syntax remains valid.
Exit criteria
The complete policy test suite runs with network disabled.
Milestone 5 — mas CLI
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
mas parse ADDRESS
Shows syntax only.
Discover
mas discover HOST
Shows validated statute or typed failure.
Explain
mas explain ADDRESS
Shows three distinct layers:
Syntax
Namespace discovery
Semantic resolution
Example failure:
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
mas lint-statute FILE
Run conformance suite
mas test
Exit criteria
A human can understand exactly why an address received its interpretation.
Milestone 6 — Static statute deployment
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:
/.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:
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
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:
SMTP
or:
provider inbound webhook
Choose whichever produces the smallest reliable prototype.
Gateway behavior
For each inbound recipient:
- parse address;
- discover statute;
- resolve policy;
- produce normalized event;
- POST event to configured HTTP application endpoint.
Example event:
{
"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
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:
ticket!callback@great-service.example
The service:
- receives a normalized MAS event;
- verifies P2 is enabled;
- creates ticket;
- records callback as requested interaction mode;
- generates resource address:
ticket=GS-41872@great-service.example
- replies using that resource address.
The subject does not require a ticket prefix.
Flow B — correlate
Customer later sends or replies to:
ticket=GS-41872@great-service.example
The service:
- resolves resource type and id;
- locates GS-41872;
- 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:
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:
archive message
+
return "ticket closed" response
It must not silently mutate or reopen the ticket.
Flow D — unknown resource
Mail arrives at:
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.
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:
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.
Initial execution order
The shortest path is:
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.