Instructions for agents (and context for humans) working in this repo. The repo was built by a multi-agent workflow and its discipline is part of the project: unusually dense objective gates are what make delegated implementation safe.
- Semantic tie-breaker for runtime behavior: the Component Model spec +
design/mvp/canonical-abi/definitions.py(in thethird_party/component-modelsubmodule), with wasmtime as corroborating evidence — never the other way around. See docs/architecture.md §1 for the parity policy. - Interface contracts between workstreams live in
contracts/(plan format, descriptor IR, intrinsics, digest, embedder API). Contract changes are versioned events made only by the orchestrator; implementation tracks report contract friction, they never edit around it. - Design and decisions: docs/architecture.md. Consumer track: docs/consumers.md. Upstream links: docs/references.md.
The justfile is the command surface: recipe bodies are the exact commands,
and each CI job runs exactly one gha:: recipe (just ci = exactly CI;
.github/justfile holds the job bodies). Run the recipes your change can
affect; the full pass before commit is:
just gates # everything below, in this order; see the justfile (or
# `just --list`) for the recipe list and per-gate one-linersConformance discipline: the harness fails loudly on unexpected failures and
on stale xfails; per-browser deltas live in harness/browser/expectations/
with stale-delta detection. Never absorb a regression into an xfail/overlay
without a named class and a tracking issue.
Work is parallelized across model-pinned subagents defined in the operator's global opencode config — deliberately not vendored into this repo, so all repo-specific context (contracts, spec authorities, gates) travels in each dispatch prompt.
| Agent | Model | Role |
|---|---|---|
| orchestrator (primary session) | fable | planning, contracts, dispatch, integration, review, all commits |
coder |
sonnet | implementation tracks against pinned contracts |
coder-hard |
opus | subtle tracks: shim internals, CABI edge cases, scheduler periphery |
reviewer |
fable | parallel code review when the orchestrator is the bottleneck |
explore |
haiku | fast read-only codebase search |
Dispatch rules:
- Every track prompt names: territory (paths owned), governing
contracts (
contracts/*.md+ design-doc sections), and gates (exact commands). Territories are disjoint across concurrent tracks. - Subagents never commit (permission-enforced); the orchestrator commits after review.
- The task-scheduler core is single-owner (coherence risk):
coder-hardat most, under close orchestrator review; parallelism stays at the periphery.
Review protocol: every track is reviewed against its contracts before commit
— by the orchestrator inline, or by reviewer subagents in parallel. A
review dispatch must name the diff scope, the governing contracts/*.md,
and — for anything touching CABI/async semantics —
docs/architecture.md §5–§7 plus the spec sources
(definitions.py as tie-breaker): the reviewer judges only against named
authorities and flags unnamed ones rather than filling gaps from memory.
Revision rounds go back to the same coder session via task_id (context
intact), not a fresh agent.
Failure recovery (content-filter false positives, driver interrupts): an
aborted task call kills neither the child session (context persists in the
opencode db) nor its effects (files/commands persist on disk). Ladder:
- Locate the orphan (
opencode-agent-sessions <parent-session-id>, on PATH); resume viatask_id— "summarize status, then continue". - Two failed resumes → assume poisoned context: fresh agent, handoff prompt = original track + "partial work exists, audit state first" + artifact pointers. Gates arbitrate what's already done.
- Repeated failures across fresh contexts → escalate to the human; the trigger may live in the artifacts themselves.
Standing rules:
- After any fan-out, reconcile launched-vs-completed before proceeding — a missing result is not missing work.
- Never run one-off
npm:specifiers (e.g.deno run npm:yaml) from the workspace root: Deno records them into the rootdeno.lock, silently dirtying the tree. Use python3 or run from/tmp; checkgit diff deno.lockbefore staging. mainis branch-protected: required checks = thecoreCI matrix, force-pushes and deletions blocked, auto-merge enabled. Admin direct pushes still work (enforce_admins: false), but PR + auto-merge is the preferred delivery: it gets the required checks for free. Thebrowserjob is deliberately NOT a required PR check (it runs post-merge only, gating the prerelease) — do not add it to the protection contexts or PRs will never merge.- Versioning (README §Consuming):
@polyengine/{runtime,translator,wasi, ct-runner}version in lockstep, and the manifests always carry the NEXT release. Still 0.x/unstable but caret-honest: a PR that breaks the published surface bumps the lockstep minor in the same PR; compatible work leaves the version alone. Releases are cut from a greenmaincommit via release.ymlworkflow_dispatchwithrelease=true(guards: lockstep, tag-exists, greenpre-<shorthash>present), followed immediately by a manifest-bump PR to the next patch — the four manifests plus runtime's copy-identity constantRUNTIME_VERSION(runtime/src/embedder/copy.ts;just test-runtimepins the sync).@polyengine/protocolversions independently; bumping its manifest publishes it for real at the next cut. Prereleases (pre-<shorthash>, every greenmain) are GitHub releases carrying artifacts only — JSR and npm are published by cut releases exclusively. - Breaking changes are declared by PR label, one per package:
breaking/{runtime,translator,wasi,ct-runner,protocol}. A label asserts that the PR breaks that package's published surface (caret-incompatible); no label means compatible. The labels are read LIVE from the API wherever they are consulted — never from an event payload — because retroactive edits are expected and load-bearing: noticing at cut time that a merged PR was mislabelled and fixing the label there is a supported workflow, and the cut re-reads the whole window.tools/version-guard/check.tsenforces them in four places (just test-version-guardcovers its logic):localmode, first injust gatesand an unconditionalgha::corestep (label-free tree checks — lockstep agreement, monotonicity, the protocol byte-identity tear check — so pre-push runs and direct pushes are covered without PR context);prmode ingha::core(lockstep agreement, monotonicity, label ↔ minor-bump agreement both ways, protocol-tear warning — an early warning only, since label edits deliberately do not re-trigger CI);publishmode in release.yml's publish step, both modes (in-tree protocol must be byte-identical to the published version its manifest names — the authoritative tear guard, which PR-time checks cannot own because they miss post-run label edits, direct pushes to main, and their own staleness at cut time; on the prerelease path nothing publishes, so it is early detection of a tear the next cut would hit); andcutmode onrelease=true, which turns the window's labels into the minor-bump requirement and renders the release notes. - The host ABI is versioned by
@polyengine/protocol, gated by goldens (contracts/embedder-api.md §"The host-ABI surface and its version"). The conventions suite (runtime/tests/conventions/, ridesjust test-runtime; focused run:just test-conventions) pins the host-facing lift/lower behavior as committed transcripts underruntime/tests/conventions/golden/. Modifying or deleting a golden asserts a host-ABI behavior change and requiresbreaking/protocolin the same PR (the reviewed behavior-neutral escape is theconventions-fixlabel); adding goldens is free. version-guard enforces this inprmode (advisory, live labels) and authoritatively incutmode (window-wide diff of the goldens dir; M/D requires protocol on a later minor line than the last cut, or aconventions-fixwindow PR). Host modules import@polyengine/protocolat most — the runtime's exported surface is application-only — so lockstep releases that leave the goldens byte-identical cannot touch a host-provider package. - Cutting a release. (1) Sanity pass, the step no machine can do:
enumerate the window —
gh pr list --search "base:main merged:>=<date of the last cut>"(orgh api repos/$R/compare/v<last>...main --jq '.commits[].sha') plusgit log v<last>..origin/main --first-parent --onelinefor direct pushes — and read titles and diffs against the labels. Fix labels retroactively NOW; a MISSING breaking label is the one failure mode every mechanical check here is blind to. (2) Verify the manifests against the final label set (breaking ⇒ the lockstep minor must already be ahead of the last cut) and thatRUNTIME_VERSION(runtime/src/embedder/copy.ts) matches; check protocol's manifest too if protocol moved. (3) Confirm the sha you are cutting has its greenpre-<shorthash>release — release.yml refuses otherwise, and that refusal is the green-pipeline proof. (4) Dispatch release.yml withrelease=true(gh workflow run release.yml -f release=true --ref main). (5) Land the post-cut manifest-bump PR to the next patch immediately: the four lockstep manifests +RUNTIME_VERSION. (6) Confirm the npm-publish.yml run release.yml dispatched, and spot-check the dist-tags (npm view @polyengine/runtime dist-tags) —latestmust name the cut. (Thepredist-tag is retired with the prerelease-publishing flow and stays frozen wherever it last pointed.) - Two registries, one version (protocol rides its own manifest version on
both). JSR is published inline by release.yml; npm
is published by npm-publish.yml, triggered by the GitHub release, from
packages built by
tools/npm-build/build.ts(dnt). The npm side reads name/version/exports out of the samedeno.jsonmanifests, so adding an entry point or bumping a version needs no second edit — butjust test-npmis the gate that proves it, and the property it exists to pin is that cross-package imports stay npm dependencies rather than inlined source (duplicate copies are the multi-runtime-copy failure mode the protocol brands exist to diagnose). npm auth is OIDC trusted publishing keyed on thenpm-publish.ymlfilename; there is no npm token in the repository or its secrets. - Consumer checkouts (the polymorph family, under
~/p/polymorph/) are strictly read-only: verifygit statusin any consumer tree you ran commands near, before and after. Build artifacts go to/tmpor a redirectedCARGO_TARGET_DIR, never into consumer trees. - Findings against foreign repos go in the tracker files
(
upstream-component-model-repo-findings.md,upstream-consumer-findings.md), not inline notes; filing them is the operator's call.