Docs menu

manage_proposals

This page answers how an AI builds in OpenX over MCP: what manage_proposals takes, how to check a proposal before sending it, who accepts it, and every refusal it can answer with.

Purpose

manage_proposals creates, updates or deletes proposals by committing a partial of the workspace's proposal groups. The same key updates a proposal and a new key creates one. It is the only tool that writes, and it writes only proposals.

A person accepts them in the workspace portal. There is no accept tool over MCP, by design, so an agent connected from outside never accepts its own proposals. Any workspace member can accept a proposal; some changes (access, licences, connected-app requests) need a management right. Accepting writes the record to the workspace; it does not deploy it. Greenfield is Azi's own, inside the workspace: there, the proposals Azi makes are accepted for you, as you and with your rights, and never anyone else's.

The structure

A proposal sits at ProposalGroups[groupId].Details.Bundles[bundleId].Proposals[lookup]. Bundles go inside Details, never beside it. Each proposal needs a Kind, a Lookup and a Proposed object. To remove proposals, pass Deletes with null values; they are processed before the merge.

InputMeaning
ProposalGroupsThe groups to create or update, by group id
DeletesProposals to take away, with null values
checkOnlytrue runs every check and writes nothing

A proposal to remove a record has Intent: "delete" and an empty Proposed object, or only Proposed: { Metadata: { Removal: { ... } } } with the record's delete settings. To place a new node on the canvas, put its position at Proposed.Metadata.Position: { X, Y } (on a surface's canvas, the surface's own entry for the node). No code places a node for you; lay it out by the convention on the Canvas page. A check (and a submit) also answers layoutWarnings for the nodes it proposes (LAYOUT_OVERLAP, LAYOUT_LINES_CROSS, LAYOUT_NO_POSITION), each with a freeSpot and a remedy, and otherLayoutWarnings, the count of warnings elsewhere on the canvas. A warning never makes a check invalid and never blocks a submit.

A job identity proposal is alone in its bundle, and a person accepts it in the App Identity window.

A cloud connection and a landing zone

Clouds and CALZ are proposed like any other kind, and each has one record, keyed Workspace. Proposing is not connecting or creating: a person accepts in the window and does their part there. In Cloud Connections the proposal shows over the normal Connect form (an existing subscription, or a new one under a billing scope), filled from it, and Accept proposal and Connect accepts it and connects with what the form holds. In Manage Private CALZ it is Accept Proposal and Create.

  • A cloud connection proposal carries its name and description, and the subscription, tenant and billing scope until the cloud is connected. They go in the cloud's own fields, as the codes get_azure_listing gives, and stay changeable until the person connects, when their own Azure sign-in decides the real ids. The secret is refused CREDENTIAL_IN_PROPOSAL, a connected cloud's subscription, tenant or billing scope IDENTITY_ALREADY_STORED, a field only the person sets PERSON_ONLY_FIELD, a field the connection writes for itself (ID, AuthKeyID, AuthKeyExpires) ACTUATOR_OWNED_FIELD, and any other field UNKNOWN_CLOUD_FIELD.
  • A landing zone can be proposed once a cloud is connected. It carries its name and description, and its region and resource group only while nothing is provisioned (else CALZ_ALREADY_PROVISIONED). The region is the Azure slug, such as eastus2, never its display name (REGION_NOT_A_SLUG). A value the landing zone's creation writes, such as StorageAccountName or KeyVaultName, is refused ACTUATOR_OWNED_FIELD, and any other field UNKNOWN_CALZ_FIELD.
  • The kinds that need the landing zone (Provenance, IoT, Data Stores, Communications, App Identity) wait until the cloud is really connected and the landing zone created. Proposed before that, even in the same bundle as the Clouds or CALZ proposal, they are refused PRIVATE_LANDING_ZONE_REQUIRED.

Check before sending

Call get_workspace with includeGuides: true for the kind's guide, schema and examples. Then send the same proposal with checkOnly: true: it runs every check, and for a warm query also the api's own check, and writes nothing. It answers { checked, valid, refusals }, each refusal with its remedy. checked: false means a check could not run, so the proposal is not called valid.

Example

{
  "checkOnly": true,
  "ProposalGroups": {
    "g1": {
      "Details": {
        "Bundles": {
          "b1": {
            "Proposals": {
              "p1": {
                "Kind": "DataConnections",
                "Lookup": "boiler-4",
                "Proposed": { "Details": { "Name": "boiler-4" } }
              }
            }
          }
        }
      }
    }
  }
}

Without checkOnly, the answer is committed: true, the committed groupIds and the updated workspace (the get_workspace body), so no second read is needed. If the proposals were committed but the read-back failed, the answer is committed: true and the groupIds, with RefreshFailed and a Remedy.

Refusals

A malformed structure answers the issues found and how the structure must look. Every other refusal names the kind, the lookup and, where it can, the field path, never a value, with a code, a message and a remedy. The remedy is, in order: the refusal's own, the kind's own sentence for that code, the shared sentence below, or a pointer to the kind's guide.

Refused before any check

CodeMessage
REQUIRED"Proposal must have Kind, Lookup, and Proposed fields"
STATUS_NOT_AGENT_WRITABLE"Status is set by acceptance in the workspace portal, never by a proposal. Omit the field."
DELETE_LEAF_INVALID"A removal carries no fields except its record's delete settings." Propose it with an empty Proposed, or with only Proposed.Metadata.Removal
UNKNOWN_KIND"Unknown proposal Kind '...'. Known kinds: ..."
PROPOSED_DETAILS_METADATAA position under Proposed.Details.Metadata, which is never drawn
POSITION_NOT_DRAWNA position on the proposal itself rather than Proposed.Metadata.Position
MANAGE_PROPOSALS_WORKSPACE_UNREAD"The workspace was not read for this sign-in, so no proposal was checked or submitted."

Refused by the checks

CodeWhat it means
KIND_NOT_IN_PACK"This kind is not turned on in this workspace's capabilities yet, so it cannot be proposed, changed or removed." The answer lists the kinds that are on, as ActiveKinds
IDENTITY_NOT_CONFIGUREDNo App Identity is set up, so access rights and access configurations cannot be proposed yet
SINGLETON_PROPOSAL_EXISTSThe workspace holds one record of this kind and a proposal for it is already pending
VALIDATOR_ERRORThe kind's checks could not complete; a fault on the server side, not the proposal
CHECKS_UNAVAILABLE"The workspace capability checks could not be loaded, so nothing was committed. Try again shortly."
WARM_QUERY_CHECK_UNAVAILABLEcheckOnly only: the api's warm-query check could not run, so the proposal is not called valid
SCHEMA_MISMATCH"This field does not match the shape the workspace stores for this kind. Read proposal_schema for the field, then correct it."
UNKNOWN_INTENTA proposal either changes a record or removes one

Refused removals

CodeWhat it means
KIND_NOT_DELETABLE"Records of this kind are never removed from a workspace."
REMOVER_NOT_ALLOWEDThis kind cannot be removed from here; some kinds are a person's to remove
KIND_TEARDOWN_UNSUPPORTEDRemoving this kind is not built yet
FROM_SURFACE_NOT_SUPPORTED"This kind is removed as a whole record, not from one surface."
DELETE_WITH_PROPOSED_FIELDSA removal carries only its delete settings; propose any edit separately
TARGET_IS_PROPOSAL_ONLYOnly a pending proposal describes this record; reject that proposal instead
TARGET_NOT_FOUND"There is no saved record by that name to remove."
ALREADY_STAGED"This record is already waiting to be removed on the next Deploy."
RECORD_STAGED_FOR_DELETEThe record is waiting to be removed on the next Deploy, so it cannot be changed
REFERENCED_BYSomething else still points at it; remove what is listed first, or remove them together
SURFACE_NOT_EMPTYThe surface still holds things
DATA_STORE_HAS_DATABASESDatabases still live in the data store
DATABASE_HAS_TABLESThe database still holds containers
DATABASE_STILL_READA warm query or a surface still reads the database
ACTUATOR_NOT_REGISTEREDNothing is registered to take this kind down in Azure, so the removal stops

Each kind also has codes of its own; the kind's guide (from get_workspace with includeGuides: true) and the proposal_schema resource carry them.

When the server fails

A commit the server could not make answers "Workspace commit failed (...). Nothing was committed. This is a fault on the server, not a problem with the proposal." Another failure answers "Managing proposals failed on the server. Nothing may have been committed; call get_workspace before retrying."

On this page