5.15 Open Decisions
The interface proposal raised questions that the sources do not answer, or answer in two different ways. None of them is resolved silently in the contracts: where a choice had to be made to write a contract, the contract follows the option recommended here and says so. Each decision names the sources, the options, a recommendation and what changes in the contracts with each answer. Priority 1 blocks implementation, 2 should be answered before the first release, 3 can wait. Every decision was settled between 2026-10-30 and 2026-10-31: D1 to D12 with the owner, D13 to D23 along the recommendations at the owner's request; each keeps its reasoning here. D24 is open (raised on 2026-11-10 with the predefined roles).
5.15.1 Priority 1¶
D1. Where the parsers run: ingestion and farm (decided)¶
Decided on 2026-10-30: option A, merge. ingestion is no longer a system: Sources, Files and Sync are domains of farm, with the same paths and contracts on the host farm.moonitoring.com, and the parsers are a component of farm, so the one path that broke the rule "a system never reads another system's data" no longer exists. The platform has nine systems.
The question was raised by the architecture page on ingestion, which had the ingestion API and the parser workers write into the farm database while the system overview forbids it; the data model binds the two sides tightly (WriteCommit references the parse job and the sync run, DataIssue the run that detected it, FeedCoverage the run that computed it). The alternatives were a split with the parsers in farm and ParseJob moved there, and a split with the parsers writing through a gRPC streaming API of farm.
Applied to the refined requirements on 2026-10-30: the seven ingestion.* capabilities were renamed farm.sources.manage, farm.ingestion.read, farm.sync_clients.read, farm.sync_clients.manage, farm.source_origins.manage, farm.ingestion.read_all and farm.sync_clients.manage_all, and the note of req 1002 no longer names ingestion as a prefix. The service accounts of the farm project (sa-ingestion, sa-parser) stay as component identities.
D2. entitlements: a system, or a domain of access (decided)¶
Decided on 2026-10-30: option C, a domain of farm. Entitlement and FeatureUsage are owned by farm; the Entitlements API keeps its contract at farm.moonitoring.com/entitlements/v1, its events travel on the farm topic, the gRPC package is moonitoring.farm.entitlements.v1, and the staff capability is farm.entitlements.manage (req 1006.24). The platform has eight systems; the Commerce domain is gone. D6 stands: a feature is still named by the system that checks it (moochat.chat, grant.checks, blog.newsletter); only the storage and the staff capability live in farm.
What it costs, written down so it is not a surprise: farm is now a dependency of every product. moochat, grant, releases, news and ai-gateway read their entitlements from farm, through its events and its EntitlementsService, and the AI gateway reserves and settles usage in farm on every model call. A farm outage therefore reaches the second authorization gate of every product once their caches go cold, and future commerce work (plans, billing) lands in the farm data system. The recommendation was a system of its own, deployed next to access; the alternative was a domain of access.
D3. The identity of the farm sync client (decided)¶
Decided on 2026-10-30: option A. The sync client is a ServiceAccount of the farm's Organization (req 1..2). A user with farm.sync_clients.manage, or staff with farm.sync_clients.manage_all, issues an enrolment code; the client redeems it with a key it generated locally; farm asks access to create the ServiceAccount with that public key and the global role Sync Client, which holds farm.sync_runs.write (req 1002.25) and farm.sync_clients.read; from then on the client signs in at /tokens/v1/tokens with private_key_jwt, as the infrastructure draft proposes. Three settings were fixed with it: the token lasts fifteen minutes, the owner of the ServiceAccount is the person who issued the code, and the Sync Client role is never delegated*. Retiring the client disables the account; a reinstallation disables the previous credential.
The alternatives were a token audience of its own minted from the SyncClient row, which would have put a second authorization path next to memberships, and reusing the installation token of releases for uploads, which the infrastructure draft rejects because updates and data have different purposes and lifetimes.
Applied to the refined requirements on 2026-10-30: farm.sync_runs.write is req 1002.25, with the role Sync Client described there and the identity rule under req 67. The role list of the RBAC draft in the design folder still has to gain the role.
D4. Audit events: same transaction, one place (decided)¶
Decided on 2026-10-30: option A. The system that makes a privileged change writes the PlatformAuditEvent in its own database, in the same transaction, and publishes it as platform_audit_event.recorded.v1 through its outbox, on its own topic <system>.audit.v1 since 2026-11-11 (the shared platform.audit.v1 could not prove which system published an event, since Pub/Sub carries no publisher identity); access consumes, consolidates and serves the read-only Audit API. Each system keeps its own rows, so a lost message loses no audit; a message that reaches the dead-letter topic alerts the platform engineers, because a lost audit event is an incident; a redelivered event is matched by request_id. Req 70 gained a sentence on the mechanism the same day. Audit stays a domain of access, also decided on 2026-10-30: a synchronous audit service would add a failure dependency to every admin write without a guarantee the outbox lacks, and the locked log bucket is the tamper-proof copy; the domain can become a system of its own at the cost of a host name when security auditors become its owner.
The alternatives were one audit table in access written through a synchronous call, which cannot be in the transaction and leaves a false record after a rollback, and an audit built from the logs alone, which keeps thirty days and has no before and after images.
D5. Capabilities the contracts need and the catalog lacks (decided)¶
Decided on 2026-10-30: all twelve enter the catalog. Req 1002 gained access.webhooks.manage, farm.farm_configurations.manage, grant.checks.run, grant.documents.manage and moochat.connectors.manage (1002.26 to 1002.30); req 1006 gained ai-gateway.usage.read, farm.parse_jobs.manage, farm.reconciliation_rules.manage, farm.erasure_requests.manage, grant.calls.manage and grant.sources.manage (1006.31 to 1006.36; blog.posts.manage was 1006.37 and left again with D12). No operation is marked proposed any more, and the traceability matrix shows no capability outside the catalog. The recommendation had been to add six now and defer the grant, news and connector ones with their requirements (D12); adding them all means those names are fixed before their systems have requirements.
D6. Entitlement feature naming (decided)¶
Decided on 2026-10-30: option A. A feature is named <system>.<feature>, prefixed by the system that checks it, the one whose catalog publishes it: moochat.chat for the MooChat agent, farm.models.<model> for the models farm gates, grant.checks, blog.newsletter. Capabilities and features follow one rule, come from the same CatalogService of each system, and farm validates a feature the way access validates a capability. Req 68.3, 69.3, 68.7 and 120.5 were updated the same day; farm.moochat no longer appears.
The alternatives were a product-line prefix, which would have given capabilities and features two different rules and needed a product registry nobody owns, and a flat namespace owned by entitlements, which no system would publish or validate.
Still to touch outside the documentation: the label farm.moochat on page 11 of the architecture diagram.
5.15.2 Priority 2¶
D7. Where predictions are requested and recorded (decided)¶
Decided on 2026-10-30: option A, through farm. The Organization Dashboard asks farm (listModels, requestPredictions); farm checks the two gates, farm.models.use and the model's feature, calls ModelsService.Predict of ai-model-platform, and stores the outputs as computed observations and events with the model and its version as origin (req 19.*.4) and a derivation naming the input revisions (req 57), so a prediction is canonical data: readable at a write number, exportable, submittable and visible to an agent next to the values it came from. Batch scorings run in ai-model-platform and write back through ComputedRecordsService. The data model gained Model, ModelVersion and ScoringBatch (req 74 to 76) the same day, so the Models API of ai-model-platform is no longer a divergence.
The alternatives were calling ai-model-platform directly, which would have kept predictions outside the canonical data and given ai-model-platform an Organization-facing authorization it has nowhere else, and a farm front door with storage in ai-model-platform, which would have put two systems in every read.
D8. Onboarding of Organizations and cooperative links (decided)¶
Decided on 2026-10-30: self-service with a staff approval, and the cooperative accepts the link. Any signed-in person creates a farm or a cooperative, since 2026-11-11 also an insurer or a professional (D11), and becomes its owner (createOrganization); the Organization is pending until a staff member with the new capability access.organizations.approve (req 1006.38) activates it (setOrganizationStatus), and organization.created.v1 is published at that moment; the same capability suspends and reactivates. A farm joins a cooperative by a link request the cooperative accepts (requestCooperativeLink, acceptCooperativeLink, rejectCooperativeLink), and either side can end the link (leaveCooperative); the farm never sets its cooperative alone. Req 2 gained status (2.7) and the rule under 2.*.2.1 the same day. The request is transient and not an entity.
The alternatives were self-service without approval, which lets anyone create an unverified cooperative in a real one's name, and staff-only creation, which fits a product sold by contract but not one that grows on its own.
D9. Webhooks and alert rules (decided)¶
Decided on 2026-10-30: events leave the platform through Pub/Sub, in both shapes, registered in the Webhooks domain of access; no alert rules in v1. A forwarder copies the events marked as deliverable outside to the topic platform.external.v1; each registration an Organization makes (createWebhookSubscription) is a Pub/Sub subscription on it, filtered by Organization and event types: a push subscription calls the customer's HTTPS endpoint with an OIDC token signed by Google, which the receiver verifies by issuer, audience and service account; a pull subscription is read by the customer's own Google service account, which is the better interface for cooperatives, insurers and integrators on Google Cloud. Google retries, backs off and dead-letters; a subscription whose deliveries fail for 24 hours is paused and its owner told, and the dead letters are readable and redeliverable for 30 days. There is no delivery worker, no HMAC secret and no fixed sender address: the rule of the interface guidelines about the Cloud NAT addresses no longer applies to deliveries, and the guidelines were rewritten.
Implementation clarification, 2026-11-02: Pub/Sub dead-letter forwarding uses an approximate delivery-attempt threshold; it is not triggered by a 24-hour timer. The decided 24-hour pause and owner notification require platform monitoring and subscription management. The 30-day failure history requires retained dead-letter messages and an application read/redelivery path. These mechanisms remain to be implemented and verified; avoiding a custom delivery worker does not make them automatic.
Alert rules, thresholds on values that fire a notification, are not in v1: the events that exist (data issues opened, sync runs failed, submissions outdated, entitlements changed, releases activated) are the alerts, and a customer's system applies its own thresholds to the data it reads through the API. An AlertRule entity would be a product feature with requirements of its own.
The alternatives were a notifications system of its own, right the day e-mail or push arrive and movable then at the cost of a host name, each system delivering its own events, and the custom webhooks the first proposal described.
D10. Nations and the reference data capability (decided)¶
Decided on 2026-10-30: option A. Nations are maintained under a capability of their owner, access.nations.manage (req 1006.39), and farm.reference_data.manage (req 1006.17) no longer names Nation. The rule that a capability's prefix is the system that owns the resource stays without exceptions. The alternatives were moving Nation to farm as reference data, with access caching a copy of a list of countries, and keeping the exception.
D11. Insurers and GraphQL (decided, insurers reopened)¶
Reopened and decided on 2026-11-11: insurers now, GraphQL still later. An Organization is always one of four types, Farm, Cooperative, Insurer or Professional, required and chosen at creation. An insurer reaches farm data only through the delegations farms give it, as a cooperative does, with the read-only Analyst role; it is named by a share code, since there is no directory of Organizations. REST serves every caller, insurers included; a read-only graph of farm at /graphql/v1 is still added only when a partner exists whose questions REST cannot foresee, as an additive interface version.
The decision of 2026-10-30 was no insurer and no GraphQL in v1: the data model kept two kinds of Organization, farm and cooperative, and an insurer was to become a third kind when a real one arrived.
D12. grant and news have no requirements (decided, news reopened)¶
Decided on 2026-10-30. grant has requirements now: a design folder [1] moonitoring-grants on Drive, next to the farm one, holds 0.requisiti-grezzi.md (the raw requirements, taken from the MVP in production and its documentation) and 1.requisiti-raffinati.txt in the format of the farm file, with entities 200 to 218 (sources, collections, calls, documents, extraction batches, provider requests, revisions, territories, municipalities, the farmer's research and farm, actions, farmer documents, facts, checks) and types 1200 to 1212 (the fact vocabulary, the verified record, purpose groups, species, availability). The three grant domains (Funding Calls, Research, Farmer Documents) were rebuilt from it, the traceability index reads both files, and the MooGrant site is recorded as the platform's fourth interface. Three questions it raised are D21 to D23.
Reopened for news on 2026-11-05, along the recommendation. Staff must write localized articles in the Console, with roles and a preview, which content files committed to a repository cannot give. news now has a small backend: an Articles domain that the Console calls at /api/news/articles/v1 on its own host, a news database, a media bucket and the staff capabilities news.articles.write and news.articles.publish (req 1006.45, 1006.46), with the entities Article, ArticleTranslation and NewsAsset (req 147 to 149). The public site stays static, rebuilt when an article is published and served at news.moonitoring.com, so readers never reach the backend. The alternative, a Console that commits Markdown to the repository through GitHub, would have kept staff permissions in GitHub instead of access and made every preview a build.
What follows is the decision of 2026-10-30, kept for its reasoning. news, then called blog, was a static site with no API: content files, a static site generator and Cloudflare Pages, with a third-party newsletter. Its Posts domain, its events and the capability blog.posts.manage (req 1006.37) were removed, and blog.newsletter left the feature examples of req 68.3. The system stays in the catalog as the Content system served from Cloudflare. It was renamed news on 2026-10-31 (MooNitoring News, news.moonitoring.com) and is built with Nuxt; the removed names above keep their old spelling.
D13. Where the contracts live: section 5 or section 11 (decided)¶
Decided on 2026-10-31: the contracts live in section 5, next to the systems, as the guidelines say; section 11. Contratti stays its author's placeholder until it is turned into a pointer to section 5 or into an Italian edition. Nothing in this proposal touches it.
D14. MCP between the agent runtime and the products (decided)¶
Decided on 2026-10-31. MCP, JSON-RPC over HTTPS between the agent runtime and a system's MCP server, is the sixth standard of Choosing an Interface: every tool is declared in the system's catalog with its capabilities, features, data class and risk; the server is a component of its system and calls its own use cases; a write waits for the user's confirmation.
D21. The farmer's research and the Organizations of access (decided)¶
Decided on 2026-10-31: the second option. A signed-in MooGrant farmer gets a pending Farm Organization at sign-up, created from the research's farm, so the two gates (grant.checks.run, feature grant.checks) always apply through it and the farm's canonical data can later feed the facts; req 212.8 binds the research to it. A guest research has no Organization and passes without capability; claiming a guest research on sign-in creates the Organization when the account has none. The approval flow of D8 applies: the Organization stays pending until staff approve it, which does not stop the farmer from using MooGrant.
D22. Guests on the platform (decided)¶
Decided on 2026-10-31: grant keeps its own guest credential, the farm-research cookie that lasts 90 days from the last visit, until a second product needs guests; access knows no anonymous sessions.
D23. Model routing for MooGrant (decided)¶
Decided on 2026-10-31. Farmer documents carry personal data and go to Vertex AI in the EU only; source documents are public and may use OpenRouter. The provider keys never leave ai-gateway: instead of the grant worker submitting provider batches itself, ai-gateway gains a BatchesService (submit and get a batch, idempotent by request hash, answers written create-only under a prefix the caller names), so the grant pipeline's journal of requests by hash keeps working and the gateway meters the usage. This replaces the earlier idea of a v2 batch mode.
5.15.3 Priority 3¶
D15. Tokens: lifetimes, refresh and the active Organization (decided)¶
Decided on 2026-10-31. Access tokens last five minutes for staff and fifteen for Organization users and service accounts; refresh tokens last 30 days, slide on use and are rotated on every use, and a reuse of a rotated token revokes the chain. The active Organization is chosen when the token is issued and can be switched at refresh. Identity Platform signs Organization users in today; an OAuth 2.0 server of our own comes when partners need client credentials and scopes, with the same token endpoint.
Implementation clarification, 2026-11-04: tokens are signed with ES256 (P-256) by a Cloud KMS key in Cloud HSM, because KMS offers Ed25519 only at software protection; the keys of /jwks therefore carry crv P-256 and y. No token carries capabilities, staff included: the claims are iss, sub, aud (organization, staff or client), org, cell, exp, iat and jti, and a system checks a staff member's capabilities against its copy of the staff roles, as it checks an Organization user's against its copy of memberships; the Console reads them from GET /staff/v1/me. Refresh tokens are stored by access as SHA-256 hashes in chains, as internal state and not as an entity of the data model.
D16. Events: envelope and topics (decided)¶
Decided on 2026-10-31, as proposed: CloudEvents 1.0 in JSON, one Pub/Sub topic per system with the event type as a message attribute, seven days of retention, a configured dead-letter threshold of five delivery attempts, the farm write number as an extension attribute, and platform.external.v1 for deliveries outside (D9). Implementation clarification, 2026-11-02: the attempt count and forwarding are best effort, not a guarantee of exactly five attempts; the required Pub/Sub IAM permissions and dead-letter subscriptions must also be configured.
D17. Not found or forbidden (decided)¶
Decided on 2026-10-31, as the contracts say: 404 for a resource the caller may not see, 403 only when the caller lacks the capability for a whole collection.
D18. Conflicts between sources (decided)¶
Decided on 2026-10-31: the refined requirements and the host rule win. The RBAC draft's platform.* prefixes, the raw requirements' functional control and the diagram's accounts, updates and bandi host names are superseded by the refined model (access.*, OfficialSubmission, access, releases, grant); the ingestion page of the architecture follows D1. The RBAC draft and the diagram labels remain to be updated in the design folder.
D19. Future work left out of v1 (decided)¶
Decided on 2026-10-31: batches and GroupMovementEvent, EggLot, ProductEvent and the agent connectors keep their schemas and wait for a concrete case, as the data model says; their operations stay out of v1 or marked future work.
D20. Who may run reconciliation and erasure (decided)¶
Decided on 2026-10-31: rule set changes, resolution re-runs and the execution of an erasure are staff operations behind farm.reconciliation_rules.manage and farm.erasure_requests.manage, now in the catalog (D5), held by few; the legal scope of erasure stays with the legal adviser, as req 63 notes, and does not block the contracts.
D24. Custom roles of an Organization (open)¶
Raised on 2026-11-10, when the predefined roles were confirmed (OrganizationPredefinedRoleEnum): Owner, Manager, Technician, Veterinarian, Advisor and Sync Client are global, with stable keys and explicit capabilities, and they are enough for now. The question is whether an Organization may later compose roles of its own from the catalog, as Google Cloud and GitHub allow.
- Option A, global roles only (today): staff with
access.organization_roles.manageadd a global role when several customers need it. Simple to audit; slow for one customer's need. - Option B, custom roles per Organization: a role gains an optional
organization; members with a new admin capability compose it from the catalog under the same rules as every role: they outrank it and hold every capability in it (req 6.4), no admin capability when it is to be delegated or given by link, neverfarm.sync_runs.write.
Recommendation: A until a customer asks, then B. Nothing in the contracts depends on it: OrganizationRoleAccessControl would gain one field, and access would add the create and update operations for members.
The data-scoped role Quality reader (milk tests and quality only) waits for a separate question, the resource scopes of farm.animal_records.read (req 1002); until then a cooperative uses Advisor.