demo-ship — deploy, prove, package, open, notify
The contract: work is never handed back unverified. This skill ends with a demo page open in the browser, an e2e transcript proving the cloud endpoint answered correctly, and a ding + iMessage. If any stage fails, fix and re-run that stage — do not present a partially verified demo.
Stage 1 — Deploy
Target is the shared function app unless another is named:
- Function app:
<function-app>(sub<subscription-id>, RG<resource-group>), modelgpt-5.2-chat. - Account: your work account (NOT a guest identity, NOT a personal account). Refresh
with
az login --tenant <tenant-id>if tokens are stale. - Deploy:
cd rapp_ai && func azure functionapp publish <function-app> --build remote - If the cloud returns 503 after deploy: tenant policy may have flipped storage
publicNetworkAccessoff again on the function's storage account — runaz storage account update -n <storage-account> -g <resource-group> --public-network-access Enabledand restart the app. Check this FIRST on any 503, before diagnosing code. - Local variant:
cd rapp_ai && func starton port 7072 (7071 is reserved for the brainstem). Consumers readRAPP_AI_PORT/RAPP_AI_URL.
Stage 2 — Prove it end-to-end (non-negotiable)
Run the demo script / e2e test against the endpoint YOU just deployed (cloud URL for cloud deploys — never claim cloud readiness from a local run):
- Get the function key yourself (
az functionapp keys listwith the subscription pinned); do not ask Kody for creds that are already inlocal.settings.jsonor az. - Drive the REAL conversation flow the demo uses, in order, one agent call per turn (step-wise demo rule: the orchestrator calls ONE connected agent per turn, verbatim, then stops).
- Check
agent_logsin every response — a plausibleassistant_responsewith faked tool results is a FAIL (gpt-4.1-class models fabricate tool output). - If the demo has a reset agent ("reset the demo"), run it first so the run starts from a clean state, and run it again at the end so Kody's first take is clean.
- Save the request/response transcript to the project's outputs folder; the final summary must quote at least one real response as evidence.
Stage 3 — The demo page
Generate ONE self-contained HTML demo page (no external deps), following the house demo pattern:
- M365 Copilot chat look; up-arrow drives the script (each press advances to the next scripted user turn), plus a per-step copy-to-clipboard button (clipboard survives Teams formatting; this is why the buttons exist).
- Attach-front / lookup-backend: if the flow involves an uploaded document, the page front-stages the artifact (downloadable, correct file type — PDFs stay PDFs, never silently converted) and the backend reads its bundled copy by name. Never pretend to read from a source the AI wouldn't have access to.
- Endpoint config at the top of the file:
RAPP_AI_URLdefault to the deployed function, overridable, so the same page drives local and cloud. - Style: match the demo's real palette. NO emojis anywhere. NO default magenta/purple AI gradient. Plain professional Microsoft look.
- Customer-facing hygiene: the page describes the end agents and customer value only — no RAPP/build/tooling talk, no other customers' project names.
Stage 4 — Pop it open
Open the page (open <file> or the live URL) so it is on screen when Kody comes back.
If a repo/editor is part of the ask ("so I can demo from there"), open that too.
Stage 5 — Notify
afplay /System/Library/Sounds/Glass.aiff AND iMessage <your-phone-number> via
osascript -e 'tell application "Messages" to send "<one-line status + what to click>" to buddy "<your-phone-number>"'.
The message states what was deployed, that e2e passed (with the count of turns
verified), and what file/URL is open for him.
Failure discipline
- A stage that fails gets fixed and re-run; the summary reports what failed and what the fix was. Never report "done" with a caveat buried below it.
- If deploy succeeds but e2e fails and the fix isn't obvious within a few attempts, STOP, leave the environment as-is, and report exactly which turn failed with the raw response — do not paper over it in the demo page.
- Never deploy from a dirty tree without committing locally first (feature branch is fine); the deployed artifact must be reproducible from a commit.
Parameters
The typed contract this capability answers to (JSON Schema — the deterministic layer):
{
"properties": {
"file": {
"description": "Derived from `<file>` used in the documented command at line 61.",
"type": "string"
},
"function_app": {
"description": "Derived from `<function-app>` used in the documented command at line 12.",
"type": "string"
},
"resource_group": {
"description": "Derived from `<resource-group>` used in the documented command at line 12.",
"type": "string"
},
"storage_account": {
"description": "Derived from `<storage-account>` used in the documented command at line 18.",
"type": "string"
},
"tenant_id": {
"description": "Derived from `<tenant-id>` used in the documented command at line 14.",
"type": "string"
}
},
"required": [],
"type": "object"
}
Run this — do not improvise
This capability's deterministic implementation is a RAPP single-file agent, linked beside this file as demo_ship_agent.py and embedded as the fenced Python below (sha256 db89175939f94c05…; a byte-exact copy is also vaulted in the capsule comment at the end of this file). On a host with sandbox execution, run the linked file directly — if it is missing, write the fence contents verbatim to demo_ship_agent.py first:
python3 demo_ship_agent.py '{"key": "value"}' # arguments as one JSON object
echo '{"key": "value"}' | python3 demo_ship_agent.py # or on stdin
python3 demo_ship_agent.py --tool # emit the JSON tool contract
Treat stdout as a tool result. If it reports missing or unresolved inputs, stop and collect them. If it returns steps, execute those steps in order exactly as returned; if it returns instructions, follow them with the supplied inputs. Otherwise use the result verbatim. Do not invent behavior beyond that output. On a host without code execution, treat the Parameters schema and the code below as the exact specification and never paraphrase a step. Never edit inside the generated markers; a converter-equipped host can instead restore the original file checksum-verified with the installed rapp-agent-converter/scripts/toast.py convert SKILL.md --to agent.
"""DemoShip -- Deploy the current agents to the cloud function, prove them end-to-end against the
LIVE endpoint, generate the M365-styled HTML demo page, pop it open, and notify.
One verb for the whole chain — never hand work back unverified. USE THIS SKILL when
Kody says anything like: "upload to the function and test", "deploy and give me the
demo", "push the latest to the function then test", "give me the demo page / test
script / html walkthrough", "so I can test", "pop open the demo", "run the demo
script against the cloud", or names a customer demo that needs to be live-testable.
Also use for the local variant ("pop it up locally so I can test here").
Generated by the rapp skill from demo-ship. The RCI capsule at the bottom of this file carries the full original; `toast.py convert` restores it byte-exact."""
import json
import re
import sys
try:
from agents.basic_agent import BasicAgent
except ImportError: # running OUTSIDE the brainstem -- stay executable anyway.
class BasicAgent: # noqa: D101 - minimal stand-in, same contract
def __init__(self, name=None, metadata=None):
if name:
self.name = name
if metadata:
self.metadata = metadata
def perform(self, **kwargs):
return "Not implemented."
def system_context(self):
return None
def to_tool(self):
return {"type": "function", "function": {
"name": self.name,
"description": self.metadata.get("description", ""),
"parameters": self.metadata.get("parameters", {})}}
# The procedural layer, verbatim from the source capability. The brainstem
# returns this to the model, so the skill's instructions still drive behaviour
# -- now behind a typed, deterministic tool contract.
INSTRUCTIONS = '# demo-ship — deploy, prove, package, open, notify\n\nThe contract: **work is never handed back unverified.** This skill ends with a demo\npage open in the browser, an e2e transcript proving the cloud endpoint answered\ncorrectly, and a ding + iMessage. If any stage fails, fix and re-run that stage —\ndo not present a partially verified demo.\n\n## Stage 1 — Deploy\n\nTarget is the shared function app unless another is named:\n\n- Function app: `<function-app>` (sub `<subscription-id>`, RG `<resource-group>`), model `gpt-5.2-chat`.\n- Account: your work account (NOT a guest identity, NOT a personal account). Refresh\n with `az login --tenant <tenant-id>` if tokens are stale.\n- Deploy: `cd rapp_ai && func azure functionapp publish <function-app> --build remote`\n- If the cloud returns 503 after deploy: tenant policy may have flipped storage\n `publicNetworkAccess` off again on the function's storage account — run\n `az storage account update -n <storage-account> -g <resource-group> --public-network-access Enabled`\n and restart the app. Check this FIRST on any 503, before diagnosing code.\n- Local variant: `cd rapp_ai && func start` on **port 7072** (7071 is reserved for the\n brainstem). Consumers read `RAPP_AI_PORT` / `RAPP_AI_URL`.\n\n## Stage 2 — Prove it end-to-end (non-negotiable)\n\nRun the demo script / e2e test against the endpoint YOU just deployed (cloud URL for\ncloud deploys — never claim cloud readiness from a local run):\n\n- Get the function key yourself (`az functionapp keys list` with the subscription\n pinned); do not ask Kody for creds that are already in `local.settings.json` or az.\n- Drive the REAL conversation flow the demo uses, in order, one agent call per turn\n (step-wise demo rule: the orchestrator calls ONE connected agent per turn, verbatim,\n then stops).\n- Check `agent_logs` in every response — a plausible `assistant_response` with faked\n tool results is a FAIL (gpt-4.1-class models fabricate tool output).\n- If the demo has a reset agent ("reset the demo"), run it first so the run starts\n from a clean state, and run it again at the end so Kody's first take is clean.\n- Save the request/response transcript to the project's outputs folder; the final\n summary must quote at least one real response as evidence.\n\n## Stage 3 — The demo page\n\nGenerate ONE self-contained HTML demo page (no external deps), following the house\ndemo pattern:\n\n- M365 Copilot chat look; **up-arrow drives the script** (each press advances to the\n next scripted user turn), plus a per-step **copy-to-clipboard button** (clipboard\n survives Teams formatting; this is why the buttons exist).\n- Attach-front / lookup-backend: if the flow involves an uploaded document, the page\n front-stages the artifact (downloadable, correct file type — PDFs stay PDFs, never\n silently converted) and the backend reads its bundled copy by name. Never pretend\n to read from a source the AI wouldn't have access to.\n- Endpoint config at the top of the file: `RAPP_AI_URL` default to the deployed\n function, overridable, so the same page drives local and cloud.\n- Style: match the demo's real palette. NO emojis anywhere. NO default magenta/purple\n AI gradient. Plain professional Microsoft look.\n- Customer-facing hygiene: the page describes the end agents and customer value only —\n no RAPP/build/tooling talk, no other customers' project names.\n\n## Stage 4 — Pop it open\n\nOpen the page (`open <file>` or the live URL) so it is on screen when Kody comes back.\nIf a repo/editor is part of the ask ("so I can demo from there"), open that too.\n\n## Stage 5 — Notify\n\n`afplay /System/Library/Sounds/Glass.aiff` AND iMessage `<your-phone-number>` via\n`osascript -e 'tell application "Messages" to send "<one-line status + what to click>" to buddy "<your-phone-number>"'`.\nThe message states what was deployed, that e2e passed (with the count of turns\nverified), and what file/URL is open for him.\n\n## Failure discipline\n\n- A stage that fails gets fixed and re-run; the summary reports what failed and what\n the fix was. Never report "done" with a caveat buried below it.\n- If deploy succeeds but e2e fails and the fix isn't obvious within a few attempts,\n STOP, leave the environment as-is, and report exactly which turn failed with the\n raw response — do not paper over it in the demo page.\n- Never deploy from a dirty tree without committing locally first (feature branch is\n fine); the deployed artifact must be reproducible from a commit.'
# Ordered commands lifted verbatim from the capability's own documentation.
STEPS = [
{
"cmd": "az login --tenant <tenant-id>",
"line": 14
},
{
"cmd": "cd rapp_ai && func azure functionapp publish <function-app> --build remote",
"line": 15
},
{
"cmd": "az storage account update -n <storage-account> -g <resource-group> --public-network-access Enabled",
"line": 18
},
{
"cmd": "cd rapp_ai && func start",
"line": 20
},
{
"cmd": "az functionapp keys list",
"line": 28
},
{
"cmd": "open <file>",
"line": 61
}
]
class DemoShipAgent(BasicAgent):
def __init__(self):
self.name = 'DemoShip'
self.metadata = {
"name": "DemoShip",
"description": "Deploy the current agents to the cloud function, prove them end-to-end against the\nLIVE endpoint, generate the M365-styled HTML demo page, pop it open, and notify.\nOne verb for the whole chain \u2014 never hand work back unverified. USE THIS SKILL when\nKody says anything like: \"upload to the function and test\", \"deploy and give me the\ndemo\", \"push the latest to the function then test\", \"give me the demo page / test\nscript / html walkthrough\", \"so I can test\", \"pop open the demo\", \"run the demo\nscript against the cloud\", or names a customer demo that needs to be live-testable.\nAlso use for the local variant (\"pop it up locally so I can test here\").",
"parameters": {
"properties": {
"file": {
"description": "Derived from `<file>` used in the documented command at line 61.",
"type": "string"
},
"function_app": {
"description": "Derived from `<function-app>` used in the documented command at line 12.",
"type": "string"
},
"resource_group": {
"description": "Derived from `<resource-group>` used in the documented command at line 12.",
"type": "string"
},
"storage_account": {
"description": "Derived from `<storage-account>` used in the documented command at line 18.",
"type": "string"
},
"tenant_id": {
"description": "Derived from `<tenant-id>` used in the documented command at line 14.",
"type": "string"
}
},
"required": [],
"type": "object"
}
}
super().__init__(name=self.name, metadata=self.metadata)
def perform(self, **kwargs): # toaster:generated-perform
missing = [k for k in self.metadata["parameters"].get("required", [])
if k not in kwargs]
if missing:
return json.dumps({"status": "error",
"missing_required": missing}, indent=2)
resolved, unresolved = [], set()
for step in STEPS:
cmd = step["cmd"]
for key, value in kwargs.items():
for token in ("<" + key.replace("_", "-") + ">",
"<" + key + ">",
"{{" + key + "}}",
"$" + key.upper()):
cmd = cmd.replace(token, str(value))
for leftover in re.findall(r"<[a-zA-Z][a-zA-Z0-9 _.-]{1,40}>", cmd):
unresolved.add(leftover)
resolved.append(cmd)
return json.dumps({"status": "ok",
"steps": resolved,
"unresolved_placeholders": sorted(unresolved),
"note": "Resolved deterministically by the agent; "
"run in order. Nothing was executed here."},
indent=2)
if __name__ == "__main__":
# Standalone entry point: the deterministic layer runs with NO brainstem,
# no framework, no install. This is what lets a "simple SKILL.md" platform
# keep real determinism -- the host model shells out to this file instead
# of improvising the procedure in prose.
# echo '{"arg": "value"}' | python3 demo_ship_agent.py
# python3 demo_ship_agent.py '{"arg": "value"}'
# python3 demo_ship_agent.py --tool # emit the JSON tool contract
_a = sys.argv[1:]
if _a and _a[0] == "--tool":
print(json.dumps(DemoShipAgent().to_tool(), indent=2))
else:
_raw = _a[0] if _a else (sys.stdin.read().strip() or "{}")
print(DemoShipAgent().perform(**json.loads(_raw)))
# rci-capsule:v1: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