os-platform
Use this skill when the user asks about current Open Software / os-platform state or an agent needs to keep tracked work current: Issues, Bounties, Projects, Orgs, Submissions, Comments, Activity, Contributors, Product Memory, the team Timeline, or whether API endpoints are real-backed. Prefer the bundled script over reading code, seed data, frontend fixtures, or docs when the question is about production data.
The authoritative workflow convention (memory before work, one Issue per
reviewable outcome, milestone posts, offline degrade) is AGENTS.md → "OS
Platform (shared brain)". This skill is the REST tooling that implements it.
Quick Start
Run commands from this skill directory:
python3 scripts/os_platform.py status
python3 scripts/os_platform.py issues list open-software --q "wallet" --limit 10
python3 scripts/os_platform.py issues list open-software --assignee me --status todo,in_progress
python3 scripts/os_platform.py issues search open-software "wallet bug" --status todo
python3 scripts/os_platform.py issues show open-software 123
python3 scripts/os_platform.py issues create open-software --title "Fix wallet sync" --body "Issue details" --type bug --priority urgent
python3 scripts/os_platform.py issues assign open-software 123
python3 scripts/os_platform.py issues status open-software 123 in_review
python3 scripts/os_platform.py issues take open-software 123 --yes
python3 scripts/os_platform.py files upload ./evidence.mp4 --purpose attachment
python3 scripts/os_platform.py issues attach open-software 123 --path ./evidence.mp4 --public
python3 scripts/os_platform.py comments add open-software 123 --body "Opened PR #456."
Configuration:
- The default API base URL is
https://app.opensoftware.co/api.
OS_PLATFORM_API_BASE_URL can override the default unless --base-url is passed.
OS_PLATFORM_API_KEY is required unless --api-key is passed. The script sends it as Authorization: Bearer ....
OS_PLATFORM_USER_AGENT can override the default os-platform-cli/2.0 (+https://opensoftware.co) User-Agent.
- Optional project defaults can be stored in
os-platform.json in the workspace root or a parent directory. Supported fields are org and limit; both are optional.
- Never ask the user to paste an API key into chat. Ask them to set the environment variable in their shell or agent runtime.
- The installer does not prompt for or write environment values. If the API key is missing, tell the user to set it first.
Routing Rules
The script is deterministic. Read commands do not mutate platform state. Write commands create Issues, assign the authenticated user, update status, add comments, upload files, or attach uploaded files. issues take remains the confirmed shortcut that moves a todo Issue to in_progress; if the Issue has no assignee, it first assigns it to the authenticated API user. Do not rely on the script to decide user intent. Route the request before calling it, and verify writes with a read.
Use the user prompt first, then os-platform.json, then ask the user for missing required parameters. Do not guess an org, issue number, project, or contributor when the prompt and config do not provide one.
- Org state or details: use
org get <org>.
- Project lists or details: use
projects list <org> or project get <org> <project>.
- Issue lists, filters, or work queues: use
issues list <org> with the narrowest filters.
- Issue searches by rough user phrasing: use
issues search <org> "<query>" with narrow filters when useful.
- A specific Issue/Bounty by number: use
issues show <org> <number>.
- Creating an Issue for new work: use
issues create <org> --title <title> --body <body> with --type and --priority when known. Contract types are feature, bug, improvement, design, docs, refactor, and other; priorities are none, low, med, high, and urgent. The CLI passes both values through for platform validation.
- Assigning yourself on an Issue you already own: use
issues assign <org> <number>; --to me is accepted but optional. The command refuses to replace another assignee unless --force is passed deliberately, then re-reads the Issue to catch a concurrent assignment race.
- Keeping an owned Issue current: use
issues status <org> <number> <status> with proposed, todo, in_progress, in_review, completed, or cancelled. It refuses another user's Issue unless --force is deliberate; terminal completed and cancelled transitions require --yes.
- Taking a todo Issue: after the user confirms they want to work on it, use
issues take <org> <number>; use --yes only when confirmation already happened in chat or another trusted workflow.
- Uploading a file: use
files upload <path> for a private attachment, add --public only when public download access is intended, and use --purpose attachment|avatar when the default attachment is not appropriate.
- Attaching a file to an Issue: the platform only attaches PUBLIC files (error 2015 otherwise, verified live). Use
issues attach <org> <number> --file-id <fil_xxx> with a public upload, or --path <path> --public to upload-then-attach; --public is required with --path and acknowledges that anyone with the URL can download the file. The command preserves all existing attachments.
- Adding an Issue comment: use
comments add <org> <number> --body <body>.
- Reading Issue submissions, activity, or comments: use the scoped command with
<org> and <issue-number>.
- Contributors: use
contributors list <org> or contributors show <org> <user-handle>.
If the user asks for issues and omits org, read os-platform.json; if it has org, use it. If no org is available, ask the user which org to use before running the script.
When the user asks for their own tasks or issues assigned to them, pass --assignee me (the script resolves me/@me to the authenticated API user via GET /v1/users/me, so no handle lookup is needed). When the user asks for issues to work on generally, prioritize todo issues assigned to the user or with no assignee before other todo issues. If the user identity is unclear, prefer unassigned todo issues.
Agent status lifecycle
Use the platform as the source of truth for tracked work whenever it is available.
- For a pre-existing Issue, read it before starting, assign yourself when you take ownership, and move it to
in_progress.
- For an ad hoc request with no Issue, create one first, then assign yourself and move it to
in_progress.
- Route taking ownership through
issues take; its todo check and confirmation discipline are authoritative. Use separate issues assign and issues status commands only for Issues you already own.
- Keep the Issue current while work advances: use
in_review when the PR opens, completed after it merges, and cancelled when the work is deliberately abandoned or will not be done.
- Reference the Issue id in the PR, for example
JUN-123 or a Closes JUN-123 line. The Org's GitHub pr-links webhook integration creates the platform PR link automatically; there is no CLI PR-link write command. If the integration does not link it, add the PR URL with comments add.
- Add comments for durable progress or handoff context when useful. Do not replace or rewrite the Issue body to record progress.
- Read the Issue after each write to verify the platform applied it. A write response alone is not durable evidence.
Product Memory, Teams, Timeline (live since 2026-08)
The platform now carries per-Product Memory (distilled facts: decision |
convention | context | reference), Teams (platform-level rosters), and a
team-scoped Timeline (derived Issue/Memory/membership events + posted updates).
Reads via the script's raw escape hatch (GET-only):
python3 scripts/os_platform.py raw GET /v1/orgs/june/memories
python3 scripts/os_platform.py raw GET /v1/orgs/june/memories/<slug>
python3 scripts/os_platform.py raw GET /v1/teams/os-core/timeline --query actor=<handle> --query since=<iso>
python3 scripts/os_platform.py raw GET /v1/me/teams
Writes: prefer the os_platform_* MCP tools when connected (schema-validated).
On raw REST (curl), field gotchas: memories take body, posts take
body_markdown + product_handle; the API silently ignores unknown JSON
fields by design, so verify the response echoes what you set.
- Read the Product Memory index (embedded in
GET /v1/orgs/<org>
via MCP os_platform_get_product) before non-trivial work; write durable
learnings back instead of local notes files.
- Post to the team wall only at milestones a teammate would act on (blocked and
stopping, consequential decision, non-obvious ship, cross-person start) —
routine status flips are already derived timeline events.
- Read the timeline before asking a human "what's the status?".
Specific Issue Triage
When the user asks about a specific issue, fetch the live issue first, then inspect the current local codebase before suggesting implementation work.
- Use
issues show <org> <number> to get the live issue title, body, labels, project, and status.
- Search the local codebase for terms from the issue title/body, related route names, component names, API paths, labels, and project handles.
- Compare the issue request with the current implementation, tests, and nearby patterns before recommending work.
- Ground suggestions in both the issue data and code references; say when the codebase does not provide enough evidence.
- If the issue is actionable, suggest a concise implementation path and likely test or verification commands. If it is not actionable, explain what information is missing.
Available Script
scripts/os_platform.py is an API helper for platform reads and controlled Issue workflow writes. It uses only Python standard library modules; file uploads invoke curl for streaming multipart transfer.
Core commands:
python3 scripts/os_platform.py status
python3 scripts/os_platform.py org get <org>
python3 scripts/os_platform.py projects list <org>
python3 scripts/os_platform.py project get <org> <project>
python3 scripts/os_platform.py issues list <org>
python3 scripts/os_platform.py issues search <org> "<query>"
python3 scripts/os_platform.py issues show <org> <number>
python3 scripts/os_platform.py issues create <org> --title <title> --body <body>
python3 scripts/os_platform.py issues assign <org> <number>
python3 scripts/os_platform.py issues status <org> <number> <status>
python3 scripts/os_platform.py issues attach <org> <number> --file-id <fil_xxx>
python3 scripts/os_platform.py issues take <org> <number>
python3 scripts/os_platform.py files upload <path>
python3 scripts/os_platform.py submissions list <org> <issue-number>
python3 scripts/os_platform.py activity list <org> <issue-number>
python3 scripts/os_platform.py comments list issue <org> <issue-number>
python3 scripts/os_platform.py comments add <org> <issue-number> --body <body>
python3 scripts/os_platform.py contributors list <org>
python3 scripts/os_platform.py contributors show <org> <user-handle>
python3 scripts/os_platform.py raw GET /v1/...
Common flags:
--limit N caps list output.
--json prints the unwrapped data payload.
--full prints the full unwrapped data without compact summarization. issues show always does this, so its --full flag is a backward-compatible no-op.
--base-url URL overrides OS_PLATFORM_API_BASE_URL and the default base URL.
issues take:
- Fetches the Issue first.
- Refuses to update unless the current status is
todo.
- Assigns the Issue to the authenticated API user first when the Issue has no assignee.
- Prompts before moving the Issue to
in_progress, unless --yes is passed.
Other writes:
issues create creates an Org-scoped Issue with a required title and body plus optional contract type and priority values. The metadata flags remain pass-through values and do not use client-side choices.
issues assign fetches the Issue and resolves the authenticated user before assigning. assignee_user_id is the authoritative identity: a missing or empty value is unassigned, even if a sparse assignee display object exists. It is a no-op when already self-assigned, refuses another current assignee, and accepts --force for a deliberate replacement. After a PATCH, it re-reads and fails if another writer won the assignment race.
issues status fetches the Issue and authenticated user first, prints its external id, title, and current status, and accepts proposed, todo, in_progress, in_review, completed, or cancelled. It refuses another user's Issue unless --force is passed. completed and cancelled require --yes; without it, the command prints what would change and exits without writing.
files upload sends multipart file, is_public, and purpose fields to POST /v1/files; purpose is attachment or avatar and defaults to attachment.
issues attach reads the Issue's existing files[].id values and sends the complete list plus the new id as file_ids through the Issue PATCH. --path uploads first with private attachment defaults.
comments add adds a Markdown comment to an Issue.
scripts/install.sh installs this skill into a local agent skills directory. It defaults to ~/.codex/skills, supports --dest, --source, --repo, --ref, --path, and --force, and never stores credentials.
Workflow
- Decide whether the question is about current production state. If yes, use this skill.
- If the target route is unclear, read
references/api-map.md.
- Run the narrowest script command that answers the question.
- Use default compact output for summaries.
issues show always returns the full Issue; use --json or --full on other reads when exact fields matter.
- Cite the live API result in your answer, and mention if a route appears fixture-backed or unreachable.
Language Rules
- User-facing product language says Issue.
- Internal tables/code may say Bounty.
- The product is Open Software and the platform/repo is os-platform.
- If the API path says
bounties, explain results to users as Issues unless discussing internal implementation.
Safety
- Run write commands only when the user or a trusted workflow has established the intended platform mutation. Verify each write with a read before any fan-out.
- The script does not retry writes internally. The platform contract has no idempotency support, so re-running
issues create or comments add after a timeout or other ambiguous failure can duplicate the write. Read before retrying.
- Issue body edits remain append-only: fetch the full current body first, append, and never overwrite existing content. The script does not expose body editing.
- Do not print or persist
OS_PLATFORM_API_KEY.
- Do not infer private data from missing public data. A 404 on private/member-only resources can mean hidden, missing, or inaccessible.
- Treat production data as current at request time, not as a durable local fact.
Reference
Read references/api-map.md for endpoint mappings, useful filters, and real-vs-fixture guidance.
1---2name: os-platform3description: Query and update live Open Software os-platform production data through the platform API. Use when an agent needs current Issues/Bounties, Orgs, Projects, Submissions, Comments, Activity, Contributors, Product Memory, the team Timeline, or API status, or needs to create, assign, move, or comment on tracked work.4---56# os-platform78Use this skill when the user asks about current Open Software / os-platform state or an agent needs to keep tracked work current: Issues, Bounties, Projects, Orgs, Submissions, Comments, Activity, Contributors, Product Memory, the team Timeline, or whether API endpoints are real-backed. Prefer the bundled script over reading code, seed data, frontend fixtures, or docs when the question is about production data.910The authoritative workflow convention (memory before work, one Issue per11reviewable outcome, milestone posts, offline degrade) is AGENTS.md → "OS12Platform (shared brain)". This skill is the REST tooling that implements it.1314## Quick Start1516Run commands from this skill directory:1718```bash19python3 scripts/os_platform.py status20python3 scripts/os_platform.py issues list open-software --q "wallet" --limit 1021python3 scripts/os_platform.py issues list open-software --assignee me --status todo,in_progress22python3 scripts/os_platform.py issues search open-software "wallet bug" --status todo23python3 scripts/os_platform.py issues show open-software 12324python3 scripts/os_platform.py issues create open-software --title "Fix wallet sync" --body "Issue details" --type bug --priority urgent25python3 scripts/os_platform.py issues assign open-software 12326python3 scripts/os_platform.py issues status open-software 123 in_review27python3 scripts/os_platform.py issues take open-software 123 --yes28python3 scripts/os_platform.py files upload ./evidence.mp4 --purpose attachment29python3 scripts/os_platform.py issues attach open-software 123 --path ./evidence.mp4 --public30python3 scripts/os_platform.py comments add open-software 123 --body "Opened PR #456."31```3233Configuration:3435- The default API base URL is `https://app.opensoftware.co/api`.36- `OS_PLATFORM_API_BASE_URL` can override the default unless `--base-url` is passed.37- `OS_PLATFORM_API_KEY` is required unless `--api-key` is passed. The script sends it as `Authorization: Bearer ...`.38- `OS_PLATFORM_USER_AGENT` can override the default `os-platform-cli/2.0 (+https://opensoftware.co)` User-Agent.39- Optional project defaults can be stored in `os-platform.json` in the workspace root or a parent directory. Supported fields are `org` and `limit`; both are optional.40- Never ask the user to paste an API key into chat. Ask them to set the environment variable in their shell or agent runtime.41- The installer does not prompt for or write environment values. If the API key is missing, tell the user to set it first.4243## Routing Rules4445The script is deterministic. Read commands do not mutate platform state. Write commands create Issues, assign the authenticated user, update status, add comments, upload files, or attach uploaded files. `issues take` remains the confirmed shortcut that moves a `todo` Issue to `in_progress`; if the Issue has no assignee, it first assigns it to the authenticated API user. Do not rely on the script to decide user intent. Route the request before calling it, and verify writes with a read.4647Use the user prompt first, then `os-platform.json`, then ask the user for missing required parameters. Do not guess an org, issue number, project, or contributor when the prompt and config do not provide one.4849- Org state or details: use `org get <org>`.50- Project lists or details: use `projects list <org>` or `project get <org> <project>`.51- Issue lists, filters, or work queues: use `issues list <org>` with the narrowest filters.52- Issue searches by rough user phrasing: use `issues search <org> "<query>"` with narrow filters when useful.53- A specific Issue/Bounty by number: use `issues show <org> <number>`.54- Creating an Issue for new work: use `issues create <org> --title <title> --body <body>` with `--type` and `--priority` when known. Contract types are `feature`, `bug`, `improvement`, `design`, `docs`, `refactor`, and `other`; priorities are `none`, `low`, `med`, `high`, and `urgent`. The CLI passes both values through for platform validation.55- Assigning yourself on an Issue you already own: use `issues assign <org> <number>`; `--to me` is accepted but optional. The command refuses to replace another assignee unless `--force` is passed deliberately, then re-reads the Issue to catch a concurrent assignment race.56- Keeping an owned Issue current: use `issues status <org> <number> <status>` with `proposed`, `todo`, `in_progress`, `in_review`, `completed`, or `cancelled`. It refuses another user's Issue unless `--force` is deliberate; terminal `completed` and `cancelled` transitions require `--yes`.57- Taking a todo Issue: after the user confirms they want to work on it, use `issues take <org> <number>`; use `--yes` only when confirmation already happened in chat or another trusted workflow.58- Uploading a file: use `files upload <path>` for a private attachment, add `--public` only when public download access is intended, and use `--purpose attachment|avatar` when the default `attachment` is not appropriate.59- Attaching a file to an Issue: the platform only attaches PUBLIC files (error 2015 otherwise, verified live). Use `issues attach <org> <number> --file-id <fil_xxx>` with a public upload, or `--path <path> --public` to upload-then-attach; `--public` is required with `--path` and acknowledges that anyone with the URL can download the file. The command preserves all existing attachments.60- Adding an Issue comment: use `comments add <org> <number> --body <body>`.61- Reading Issue submissions, activity, or comments: use the scoped command with `<org>` and `<issue-number>`.62- Contributors: use `contributors list <org>` or `contributors show <org> <user-handle>`.6364If the user asks for issues and omits org, read `os-platform.json`; if it has `org`, use it. If no org is available, ask the user which org to use before running the script.6566When the user asks for their own tasks or issues assigned to them, pass `--assignee me` (the script resolves `me`/`@me` to the authenticated API user via `GET /v1/users/me`, so no handle lookup is needed). When the user asks for issues to work on generally, prioritize todo issues assigned to the user or with no assignee before other todo issues. If the user identity is unclear, prefer unassigned todo issues.6768## Agent status lifecycle6970Use the platform as the source of truth for tracked work whenever it is available.7172- For a pre-existing Issue, read it before starting, assign yourself when you take ownership, and move it to `in_progress`.73- For an ad hoc request with no Issue, create one first, then assign yourself and move it to `in_progress`.74- Route taking ownership through `issues take`; its `todo` check and confirmation discipline are authoritative. Use separate `issues assign` and `issues status` commands only for Issues you already own.75- Keep the Issue current while work advances: use `in_review` when the PR opens, `completed` after it merges, and `cancelled` when the work is deliberately abandoned or will not be done.76- Reference the Issue id in the PR, for example `JUN-123` or a `Closes JUN-123` line. The Org's GitHub `pr-links` webhook integration creates the platform PR link automatically; there is no CLI PR-link write command. If the integration does not link it, add the PR URL with `comments add`.77- Add comments for durable progress or handoff context when useful. Do not replace or rewrite the Issue body to record progress.78- Read the Issue after each write to verify the platform applied it. A write response alone is not durable evidence.7980## Product Memory, Teams, Timeline (live since 2026-08)8182The platform now carries per-Product Memory (distilled facts: decision |83convention | context | reference), Teams (platform-level rosters), and a84team-scoped Timeline (derived Issue/Memory/membership events + posted updates).8586Reads via the script's `raw` escape hatch (GET-only):8788```bash89python3 scripts/os_platform.py raw GET /v1/orgs/june/memories90python3 scripts/os_platform.py raw GET /v1/orgs/june/memories/<slug>91python3 scripts/os_platform.py raw GET /v1/teams/os-core/timeline --query actor=<handle> --query since=<iso>92python3 scripts/os_platform.py raw GET /v1/me/teams93```9495Writes: prefer the `os_platform_*` MCP tools when connected (schema-validated).96On raw REST (curl), field gotchas: memories take `body`, posts take97`body_markdown` + `product_handle`; the API silently ignores unknown JSON98fields by design, so verify the response echoes what you set.99100- Read the Product Memory index (embedded in `GET /v1/orgs/<org>`101 via MCP `os_platform_get_product`) before non-trivial work; write durable102 learnings back instead of local notes files.103- Post to the team wall only at milestones a teammate would act on (blocked and104 stopping, consequential decision, non-obvious ship, cross-person start) —105 routine status flips are already derived timeline events.106- Read the timeline before asking a human "what's the status?".107108## Specific Issue Triage109110When the user asks about a specific issue, fetch the live issue first, then inspect the current local codebase before suggesting implementation work.1111121. Use `issues show <org> <number>` to get the live issue title, body, labels, project, and status.1132. Search the local codebase for terms from the issue title/body, related route names, component names, API paths, labels, and project handles.1143. Compare the issue request with the current implementation, tests, and nearby patterns before recommending work.1154. Ground suggestions in both the issue data and code references; say when the codebase does not provide enough evidence.1165. If the issue is actionable, suggest a concise implementation path and likely test or verification commands. If it is not actionable, explain what information is missing.117118## Available Script119120`scripts/os_platform.py` is an API helper for platform reads and controlled Issue workflow writes. It uses only Python standard library modules; file uploads invoke `curl` for streaming multipart transfer.121122Core commands:123124```bash125python3 scripts/os_platform.py status126python3 scripts/os_platform.py org get <org>127python3 scripts/os_platform.py projects list <org>128python3 scripts/os_platform.py project get <org> <project>129python3 scripts/os_platform.py issues list <org>130python3 scripts/os_platform.py issues search <org> "<query>"131python3 scripts/os_platform.py issues show <org> <number>132python3 scripts/os_platform.py issues create <org> --title <title> --body <body>133python3 scripts/os_platform.py issues assign <org> <number>134python3 scripts/os_platform.py issues status <org> <number> <status>135python3 scripts/os_platform.py issues attach <org> <number> --file-id <fil_xxx>136python3 scripts/os_platform.py issues take <org> <number>137python3 scripts/os_platform.py files upload <path>138python3 scripts/os_platform.py submissions list <org> <issue-number>139python3 scripts/os_platform.py activity list <org> <issue-number>140python3 scripts/os_platform.py comments list issue <org> <issue-number>141python3 scripts/os_platform.py comments add <org> <issue-number> --body <body>142python3 scripts/os_platform.py contributors list <org>143python3 scripts/os_platform.py contributors show <org> <user-handle>144python3 scripts/os_platform.py raw GET /v1/...145```146147Common flags:148149- `--limit N` caps list output.150- `--json` prints the unwrapped `data` payload.151- `--full` prints the full unwrapped data without compact summarization. `issues show` always does this, so its `--full` flag is a backward-compatible no-op.152- `--base-url URL` overrides `OS_PLATFORM_API_BASE_URL` and the default base URL.153154`issues take`:155156- Fetches the Issue first.157- Refuses to update unless the current status is `todo`.158- Assigns the Issue to the authenticated API user first when the Issue has no assignee.159- Prompts before moving the Issue to `in_progress`, unless `--yes` is passed.160161Other writes:162163- `issues create` creates an Org-scoped Issue with a required title and body plus optional contract type and priority values. The metadata flags remain pass-through values and do not use client-side choices.164- `issues assign` fetches the Issue and resolves the authenticated user before assigning. `assignee_user_id` is the authoritative identity: a missing or empty value is unassigned, even if a sparse `assignee` display object exists. It is a no-op when already self-assigned, refuses another current assignee, and accepts `--force` for a deliberate replacement. After a PATCH, it re-reads and fails if another writer won the assignment race.165- `issues status` fetches the Issue and authenticated user first, prints its external id, title, and current status, and accepts `proposed`, `todo`, `in_progress`, `in_review`, `completed`, or `cancelled`. It refuses another user's Issue unless `--force` is passed. `completed` and `cancelled` require `--yes`; without it, the command prints what would change and exits without writing.166- `files upload` sends multipart `file`, `is_public`, and `purpose` fields to `POST /v1/files`; purpose is `attachment` or `avatar` and defaults to `attachment`.167- `issues attach` reads the Issue's existing `files[].id` values and sends the complete list plus the new id as `file_ids` through the Issue PATCH. `--path` uploads first with private attachment defaults.168- `comments add` adds a Markdown comment to an Issue.169170`scripts/install.sh` installs this skill into a local agent skills directory. It defaults to `~/.codex/skills`, supports `--dest`, `--source`, `--repo`, `--ref`, `--path`, and `--force`, and never stores credentials.171172## Workflow1731741. Decide whether the question is about current production state. If yes, use this skill.1752. If the target route is unclear, read `references/api-map.md`.1763. Run the narrowest script command that answers the question.1774. Use default compact output for summaries. `issues show` always returns the full Issue; use `--json` or `--full` on other reads when exact fields matter.1785. Cite the live API result in your answer, and mention if a route appears fixture-backed or unreachable.179180## Language Rules181182- User-facing product language says **Issue**.183- Internal tables/code may say **Bounty**.184- The product is **Open Software** and the platform/repo is **os-platform**.185- If the API path says `bounties`, explain results to users as Issues unless discussing internal implementation.186187## Safety188189- Run write commands only when the user or a trusted workflow has established the intended platform mutation. Verify each write with a read before any fan-out.190- The script does not retry writes internally. The platform contract has no idempotency support, so re-running `issues create` or `comments add` after a timeout or other ambiguous failure can duplicate the write. Read before retrying.191- Issue body edits remain append-only: fetch the full current body first, append, and never overwrite existing content. The script does not expose body editing.192- Do not print or persist `OS_PLATFORM_API_KEY`.193- Do not infer private data from missing public data. A 404 on private/member-only resources can mean hidden, missing, or inaccessible.194- Treat production data as current at request time, not as a durable local fact.195196## Reference197198Read `references/api-map.md` for endpoint mappings, useful filters, and real-vs-fixture guidance.