mirror of
https://github.com/vector-im/element-call.git
synced 2026-11-13 01:12:29 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5f72f464d |
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: handoff
|
||||
description: Implement the first cut of a task and stop for a direction check before the quality pass.
|
||||
argument-hint: <the task>
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
Task: $ARGUMENTS
|
||||
|
||||
Stop after the first implementation and hand it back before the quality pass.
|
||||
|
||||
## Before the hand-off
|
||||
|
||||
- Run the narrowest check that rules out an obviously broken hand-off: the test
|
||||
file for the touched code, `tsc` on its own, a single story. Not the full
|
||||
`pnpm lint`, not the whole suite.
|
||||
- If a wider check is needed before feedback can be useful, keep it to the touched
|
||||
code and say why it was needed.
|
||||
- Leave wider refactors, extra tests and documentation polish out. They are
|
||||
follow-up work, not part of the first hand-off.
|
||||
|
||||
## The hand-off
|
||||
|
||||
Stop and ask whether the direction is right. The message says:
|
||||
|
||||
- what was built and where, in a few lines,
|
||||
- which check ran and what it returned, verbatim if it failed,
|
||||
- what was left out on purpose,
|
||||
- the decisions that could have gone another way.
|
||||
|
||||
Do not continue past this point on your own.
|
||||
|
||||
## After the user confirms
|
||||
|
||||
The skill ends here. Continue with the quality pass as usual.
|
||||
@@ -44,3 +44,7 @@ storybook-static
|
||||
|
||||
# Nested checkout made by scripts/layered.sh
|
||||
/matrix-js-sdk/
|
||||
|
||||
# Claude
|
||||
/.claude/*
|
||||
!/.claude/skills/
|
||||
|
||||
@@ -16,19 +16,35 @@ host's page. It is the MatrixRTC reference implementation.
|
||||
`@media`, global `i18next` or the `widget` global — take it from a provider.
|
||||
- The change works standalone, as a widget and as a component. Say what you checked.
|
||||
- Every gate below is green before you push.
|
||||
- Hand off after the first implementation, before the quality pass. Then commit on
|
||||
the user's word where the setup allows it, or hand them the message.
|
||||
|
||||
## Read before you
|
||||
|
||||
| … | … |
|
||||
| -------------------------------- | ---------------------------------------------------------- |
|
||||
| start a task of any size | [docs/agents/workflow.md](docs/agents/workflow.md) |
|
||||
| touch call logic or a view model | [docs/agents/architecture.md](docs/agents/architecture.md) |
|
||||
| write any code | [docs/agents/code-style.md](docs/agents/code-style.md) |
|
||||
| write a test, story or e2e spec | [docs/agents/testing.md](docs/agents/testing.md) |
|
||||
| open a PR | [CONTRIBUTING.md](CONTRIBUTING.md) |
|
||||
| implement from a feature spec | [FEATURES_SPEC/AGENTS.md](FEATURES_SPEC/AGENTS.md) |
|
||||
|
||||
## Scratch files live in `agent-workspace/`
|
||||
|
||||
Git-ignored. One kebab-case subfolder per task, matching the branch topic:
|
||||
`agent-workspace/<slug>/`.
|
||||
|
||||
| File | Purpose |
|
||||
| --------------------------- | -------------------------------------------------------------------------------- |
|
||||
| `plan.md` | The concrete approach — files, symbols, edit order — once direction is confirmed |
|
||||
| `commit-msg.txt` | `git commit -F agent-workspace/<slug>/commit-msg.txt` |
|
||||
| `pr-body.md` | `gh pr create --body-file agent-workspace/<slug>/pr-body.md` |
|
||||
| `implementation-summary.md` | What was built, decisions, trade-offs |
|
||||
| `NN-prompt.md` | Raw prompt text, numbered, when worth keeping |
|
||||
|
||||
- Nothing here is durable. The folder is ignored, so anything worth keeping is
|
||||
promoted into the PR body or a doc before the task ends.
|
||||
- Never write scratch files to the repo root. Stray `load_test_summary.md` and
|
||||
`config.json_` files are what this folder prevents.
|
||||
- Start `pr-body.md` from `.github/PULL_REQUEST_TEMPLATE.md` and fill every
|
||||
section.
|
||||
|
||||
## Gates
|
||||
|
||||
@@ -42,6 +58,11 @@ pnpm i18n:check
|
||||
`knip` fails on dead code; a file deliberately inert ahead of its consumer goes in
|
||||
`knip.ts` `ignoreFiles` with a reason.
|
||||
|
||||
Ready means every gate is green and the change is covered at the layers
|
||||
[testing.md](docs/agents/testing.md) asks for. Read the diff against
|
||||
[code-style.md](docs/agents/code-style.md) first. Re-run the whole checklist after
|
||||
any fix.
|
||||
|
||||
## A good PR
|
||||
|
||||
- One slice, under ~400 changed lines, green on its own, linked to its issue.
|
||||
|
||||
@@ -275,11 +275,7 @@ API is in `component/index.tsx`, `component/api.ts` and `component/host.ts`.
|
||||
|
||||
##### Develop
|
||||
|
||||
- `pnpm build:component` builds the library into `component/dist`. It ends
|
||||
with `scripts/check-component-bundle.mjs`, which fails the build on what a
|
||||
host must not be handed: an asset inlined as a data URL, a file referenced
|
||||
by an absolute path, the standalone app's client store worker, or a module
|
||||
over its size budget.
|
||||
- `pnpm build:component` builds the library into `component/dist`.
|
||||
- `pnpm dev:component` serves a test host on `https://localhost:3001`: two
|
||||
calls side by side in resizable boxes, with host messages logged at the
|
||||
bottom. It uses the development backend, `public/config.json` if present,
|
||||
|
||||
+5
-1
@@ -14,7 +14,11 @@ This folder contains documentation for setup, usage, and development of Element
|
||||
|
||||
Entry point: [AGENTS.md](../AGENTS.md).
|
||||
|
||||
- [Agent workflow](./agents/workflow.md)
|
||||
- [Architecture](./agents/architecture.md)
|
||||
- [Code style](./agents/code-style.md)
|
||||
- [Testing](./agents/testing.md)
|
||||
|
||||
Opt-in skills in [`.claude/skills/`](../.claude/skills/):
|
||||
|
||||
- `/handoff <task>` — get the first implementation back for a direction check
|
||||
before lint, the full suite and polish.
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
# Agent workflow
|
||||
|
||||
## Scratch files live in `agent-workspace/`
|
||||
|
||||
Git-ignored. One kebab-case subfolder per task, matching the branch topic:
|
||||
`agent-workspace/<slug>/`.
|
||||
|
||||
| File | Purpose |
|
||||
| --------------------------- | -------------------------------------------------------------------------------- |
|
||||
| `plan.md` | The concrete approach — files, symbols, edit order — once direction is confirmed |
|
||||
| `commit-msg.txt` | `git commit -F agent-workspace/<slug>/commit-msg.txt` |
|
||||
| `pr-body.md` | `gh pr create --body-file agent-workspace/<slug>/pr-body.md` |
|
||||
| `implementation-summary.md` | What was built, decisions, trade-offs |
|
||||
| `NN-prompt.md` | Raw prompt text, numbered, when worth keeping |
|
||||
|
||||
- Nothing here is durable. The folder is ignored, so anything worth keeping is
|
||||
promoted into the PR body, a doc, or a feature spec before the task ends.
|
||||
- Never write scratch files to the repo root. Stray `load_test_summary.md` and
|
||||
`config.json_` files are what this folder prevents.
|
||||
- Start `pr-body.md` from `.github/PULL_REQUEST_TEMPLATE.md` and fill every
|
||||
section.
|
||||
- Commit subjects are plain imperative English. No conventional-commits prefixes.
|
||||
- A `plan.md` and a feature spec sit at different altitudes; neither replaces the
|
||||
other. A spec in `FEATURES_SPEC/` is durable and deliberately abstract —
|
||||
behaviour, decisions, acceptance criteria — so the feature can be rebuilt against
|
||||
a `main` nobody has seen yet. A plan is one slice of it landing on today's
|
||||
`main`: the paths, symbols and edit order the spec's decisions and criteria must
|
||||
not name. `FEATURES_SPEC/AGENTS.md` wins wherever the two genuinely overlap.
|
||||
|
||||
## Hand off before the quality pass
|
||||
|
||||
- Implement the change, run the narrowest check that rules out an obviously broken
|
||||
handoff, then stop and ask whether the direction is right.
|
||||
- Full `pnpm lint`, the whole suite, coverage and benchmarks come after the
|
||||
direction is confirmed.
|
||||
- Wider refactors, extra tests and documentation polish are follow-up work, not
|
||||
part of the first handoff.
|
||||
- If a check is needed before feedback, keep it to the touched code and say why.
|
||||
|
||||
## Commit and PR readiness
|
||||
|
||||
- Commit once the user confirms direction, or asks for one. Not before. Where the
|
||||
setup has no git identity, a sandbox included, write the message to
|
||||
`commit-msg.txt` and hand it over instead.
|
||||
- Ready means every gate in [AGENTS.md](../../AGENTS.md#gates) is green and the
|
||||
change is covered at the layers [testing.md](./testing.md) asks for. Read the diff
|
||||
against [code-style.md](./code-style.md) first.
|
||||
- Re-run the whole checklist after any fix. Only on green, either way.
|
||||
- Once a human has started reviewing, fix forward — never force-push a
|
||||
regeneration over a review in progress.
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
"build:sdk:production": "pnpm build:sdk-target-based-on-call-view-model",
|
||||
"build:sdk:development": "pnpm build:sdk-target-based-on-call-view-model --mode development",
|
||||
"build:component": "pnpm build:component:js && pnpm build:component:types",
|
||||
"build:component:js": "pnpm build:full --config vite-component.config.js && node scripts/check-component-bundle.mjs",
|
||||
"build:component:js": "pnpm build:full --config vite-component.config.js",
|
||||
"build:component:types": "tsc -p component/tsconfig.build.json",
|
||||
"build:component:production": "pnpm build:component",
|
||||
"build:component:development": "pnpm build:component:js --mode development && pnpm build:component:types",
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks the component build's output for what a host must not be handed.
|
||||
*
|
||||
* A library build inlines every asset it meets as a data URL unless the import
|
||||
* says otherwise, and does so silently: one import without `no-inline` and the
|
||||
* module grows by the asset, encoded a third larger, that every host then
|
||||
* downloads and parses before the first call. MediaPipe's runtime alone is
|
||||
* 11 MB. A reference to a file by an absolute path is just as silent and
|
||||
* breaks at runtime instead, since a host serves the module from wherever it
|
||||
* likes. And the standalone app's client store worker, which carries a copy of
|
||||
* the Matrix SDK and its crypto, has no business in a package whose host
|
||||
* supplies the client: the bundler emits it on loading its import, used or
|
||||
* not (see src/utils/initClient.ts). This fails the build on each of those.
|
||||
*/
|
||||
|
||||
import { readdir, readFile, stat } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
|
||||
const dist = "component/dist";
|
||||
|
||||
/** The longest data URL a chunk may carry: enough for an icon, not a sound. */
|
||||
const inlineLimit = 16 * 1024;
|
||||
|
||||
/**
|
||||
* The module itself, in bytes. Well above where it sits with its assets as
|
||||
* files, so that only an asset finding its way back in trips it.
|
||||
*/
|
||||
const moduleBudget = 4 * 1024 * 1024;
|
||||
|
||||
/**
|
||||
* The modules a host bundles: the entry points and their chunks, at the top of
|
||||
* the package. What sits under `assets/` is fetched as it is, by URL.
|
||||
*/
|
||||
async function modules() {
|
||||
const names = await readdir(dist);
|
||||
return names.filter((name) => name.endsWith(".js")).map((n) => join(dist, n));
|
||||
}
|
||||
|
||||
const problems = [];
|
||||
|
||||
for (const name of await readdir(join(dist, "assets")))
|
||||
if (name.includes("IndexedDBWorker"))
|
||||
problems.push(
|
||||
`${dist}/assets/${name}: the standalone app's client store worker is ` +
|
||||
`in the package. Something the component reaches imports the worker; ` +
|
||||
`keep it on the standalone app's side (see src/utils/initClient.ts ` +
|
||||
`and the \`loadClient\` prop of ClientProvider).`,
|
||||
);
|
||||
|
||||
for (const file of await modules()) {
|
||||
const code = await readFile(file, "utf8");
|
||||
|
||||
for (const [, type, payload] of code.matchAll(
|
||||
/data:([a-z0-9/.+-]*);base64,([A-Za-z0-9+/=]*)/g,
|
||||
))
|
||||
if (payload.length > inlineLimit)
|
||||
problems.push(
|
||||
`${file}: a ${type} asset of ${Math.round(payload.length / 1024)} kB ` +
|
||||
`is inlined as a data URL. Import it with \`?url&no-inline\` so ` +
|
||||
`that it is emitted as a file.`,
|
||||
);
|
||||
|
||||
for (const [reference] of code.matchAll(/["'`]\/assets\/[^"'`]+["'`]/g))
|
||||
problems.push(
|
||||
`${file}: ${reference} is an absolute path, which only resolves ` +
|
||||
`against an origin that serves the package at its root. The build ` +
|
||||
`is meant to render relative references; check \`base\` in ` +
|
||||
`vite-component.config.ts.`,
|
||||
);
|
||||
}
|
||||
|
||||
const moduleSize = (await stat(join(dist, "element-call.js"))).size;
|
||||
if (moduleSize > moduleBudget)
|
||||
problems.push(
|
||||
`${dist}/element-call.js is ${Math.round(moduleSize / 1024 / 1024)} MB, ` +
|
||||
`over its ${moduleBudget / 1024 / 1024} MB budget. Something large ` +
|
||||
`has been bundled into the module that a host would rather fetch when ` +
|
||||
`it is needed.`,
|
||||
);
|
||||
|
||||
if (problems.length > 0) {
|
||||
console.error(`The component build is not fit to publish:\n`);
|
||||
for (const problem of problems) console.error(` ${problem}\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user