@ethlete/agent-rules 0.1.0-next.11 → 0.1.0-next.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +23 -13
- package/content/hooks/context-warning.py +25 -6
- package/content/rules/lint-and-format.md +5 -4
- package/content/rules/reactive-state.md +1 -1
- package/content/rules/styling.md +8 -6
- package/content/skills/angular-patterns/SKILL.md +6 -6
- package/content/skills/api-source/SKILL.md +28 -19
- package/content/skills/figma-export/SKILL.md +16 -18
- package/content/skills/git-flow/SKILL.md +2 -2
- package/content/skills/handoff/SKILL.md +4 -4
- package/content/skills/query/SKILL.md +23 -12
- package/content/skills/rxjs-signals/SKILL.md +9 -3
- package/content/skills/sdk-docs/SKILL.md +6 -5
- package/content/skills/sdk-local-build/SKILL.md +25 -7
- package/content/skills/sdk-local-build/sdk-local-baseline.mjs +64 -0
- package/content/skills/sdk-source/SKILL.md +32 -32
- package/content/skills/story-styling/SKILL.md +3 -2
- package/content/skills/styleguide/SKILL.md +11 -6
- package/content/skills/styleguide/assets.md +54 -0
- package/content/skills/styleguide/changesets.md +17 -0
- package/content/skills/styleguide/file-structure.md +73 -0
- package/content/skills/styleguide/lint-rule-lookup.md +50 -0
- package/content/skills/styleguide/storybook-structure.md +25 -0
- package/content/skills/theming/SKILL.md +11 -11
- package/content/skills/timetrack/SKILL.md +1 -1
- package/package.json +1 -1
- package/src/lib/config.d.ts +3 -0
- package/src/lib/config.js +1 -0
- package/src/lib/config.js.map +1 -1
- package/src/lib/plan.d.ts +6 -5
- package/src/lib/plan.js +75 -12
- package/src/lib/plan.js.map +1 -1
- package/src/lib/sync.js +1 -4
- package/src/lib/sync.js.map +1 -1
- package/src/lib/targets/agents-skills.js +1 -1
- package/src/lib/targets/agents-skills.js.map +1 -1
- package/src/lib/targets/claude.js +0 -1
- package/src/lib/targets/claude.js.map +1 -1
- package/src/lib/targets/codex.js +2 -2
- package/src/lib/targets/codex.js.map +1 -1
- package/src/lib/targets/copilot.js +1 -1
- package/src/lib/targets/copilot.js.map +1 -1
- package/src/lib/targets/cursor.js +1 -1
- package/src/lib/targets/cursor.js.map +1 -1
- package/src/lib/targets/shared.d.ts +1 -8
- package/src/lib/targets/shared.js +2 -7
- package/src/lib/targets/shared.js.map +1 -1
- package/content/skills/styleguide/STYLEGUIDE.md +0 -520
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @ethlete/agent-rules
|
|
2
2
|
|
|
3
|
+
## 0.1.0-next.12
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#3069](https://github.com/ethlete-io/ethdk/pull/3069) [`a258308`](https://github.com/ethlete-io/ethdk/commit/a258308e0753eb65d08b3d7fb9a9f14507229047) Thanks [@github-actions](https://github.com/apps/github-actions)! - Agent rules: make generated guidance safer, reference-aware, example-validated, and cheaper to load.
|
|
8
|
+
|
|
9
|
+
- [#3069](https://github.com/ethlete-io/ethdk/pull/3069) [`973462e`](https://github.com/ethlete-io/ethdk/commit/973462ebb9ec7c2eef1aca3fc9f01615824d0074) Thanks [@github-actions](https://github.com/apps/github-actions)! - Agent rules: warn about unknown exclusions and apply Codex model-specific long-context pricing limits in the context warning hook.
|
|
10
|
+
|
|
3
11
|
## 0.1.0-next.11
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -111,7 +111,10 @@ Prettier rewrites them and `check` then reports drift on every run:
|
|
|
111
111
|
convention and never mentions a `commitlint` run - an agent that goes looking for a
|
|
112
112
|
promised validator and finds nothing reports the discrepancy instead of just
|
|
113
113
|
committing. Setting either one in `vars` overrides the detection.
|
|
114
|
-
- **`exclude`** -
|
|
114
|
+
- **`exclude`** - rule or skill names to skip entirely for every configured agent and
|
|
115
|
+
developer. For example, `"exclude": ["git-flow", "handoff"]` prevents those skills
|
|
116
|
+
from being generated; the next `sync` also removes copies generated previously. Unknown
|
|
117
|
+
names produce a warning so a typo cannot silently leave a skill enabled.
|
|
115
118
|
- **`claudeMdImportsAgentsMd`** - set (usually by `migrate`) when `CLAUDE.md` is an
|
|
116
119
|
`@AGENTS.md` import or symlink; the claude target then skips `.claude/rules/ethlete/`
|
|
117
120
|
so the rules don't load twice. `sync` warns when the flag is set but the import is
|
|
@@ -298,9 +301,9 @@ Available hooks:
|
|
|
298
301
|
context crosses 70% / 85% of the token budget, recommending a handoff. Under Claude the
|
|
299
302
|
budget is capped at the 200k long-context pricing boundary: on 1M-window models every
|
|
300
303
|
request past 200k input tokens bills the whole context at a premium rate, so the
|
|
301
|
-
warnings fire at ~140k/~170k instead of deep into the expensive range. Codex
|
|
302
|
-
|
|
303
|
-
|
|
304
|
+
warnings fire at ~140k/~170k instead of deep into the expensive range. Codex uses
|
|
305
|
+
the model-specific 272k pricing boundary for GPT-5.6, GPT-5.5 and GPT-5.4, and the
|
|
306
|
+
rollout's reported context window for models without that pricing rule.
|
|
304
307
|
|
|
305
308
|
Two things are Claude-only: the separate user-facing line (Codex documents only
|
|
306
309
|
`additionalContext`, so there the warning is folded into the text the model is told to
|
|
@@ -397,7 +400,8 @@ differ per developer, without touching any committed file:
|
|
|
397
400
|
{
|
|
398
401
|
"disableHooks": true,
|
|
399
402
|
"sdkSourcePath": "/absolute/path/to/ethlete-sdk",
|
|
400
|
-
"apiRepoPaths": { "hub": "../fut-hub-backend" }
|
|
403
|
+
"apiRepoPaths": { "hub": "../fut-hub-backend", "*": "../shared-backend" },
|
|
404
|
+
"apiRepoBranches": { "hub": "develop", "*": "main" }
|
|
401
405
|
}
|
|
402
406
|
```
|
|
403
407
|
|
|
@@ -414,15 +418,18 @@ differ per developer, without touching any committed file:
|
|
|
414
418
|
- **`apiRepoPaths`** - one checkout per app, keyed by the app's project name
|
|
415
419
|
(`{ "hub": "../fut-hub-backend" }`). The `api-source` skill reads it to confirm a
|
|
416
420
|
response shape, a status code or an enum in the API's own source instead of guessing it
|
|
417
|
-
from the client. Relative paths resolve from the repo root
|
|
418
|
-
|
|
421
|
+
from the client. Relative paths resolve from the repo root. Matching is exact; use the
|
|
422
|
+
explicit `"*"` key only when apps intentionally share a checkout.
|
|
423
|
+
- **`apiRepoBranches`** - the expected backend branch per app. It uses the same exact-key
|
|
424
|
+
and explicit `"*"` fallback rules. Omit it when branch identity is not part of the
|
|
425
|
+
environment contract; the source guide then treats the current branch as context.
|
|
419
426
|
|
|
420
427
|
Everything in this file is read at runtime, never by `sync`: the generated files stay
|
|
421
428
|
identical on every machine and in CI, which is what lets `check` diff them. That is also
|
|
422
429
|
why the file takes nothing beyond these keys - `sync`/`check` warn about unknown keys,
|
|
423
430
|
about an `sdkSourcePath` that is missing or is not an SDK checkout, and about an
|
|
424
|
-
`apiRepoPaths` entry that is not a directory
|
|
425
|
-
`.gitignore`.
|
|
431
|
+
`apiRepoPaths` entry that is not a directory, and invalid `apiRepoBranches` values. Add
|
|
432
|
+
the filename to your repo's `.gitignore`.
|
|
426
433
|
|
|
427
434
|
## Authoring content
|
|
428
435
|
|
|
@@ -447,7 +454,10 @@ compile: it is a Claude Code output style verbatim, so its frontmatter is Claude
|
|
|
447
454
|
as it is, apart from a marker line that records where it came from.
|
|
448
455
|
|
|
449
456
|
In a body, `{% varName %}` substitutes a variable, `{% skill:other-name %}` links to
|
|
450
|
-
another
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
457
|
+
another emitted package guide, and `{% resource:file.mjs %}` links to a bundled file.
|
|
458
|
+
Skill links are required dependencies: after scope, package, variable, and exclusion
|
|
459
|
+
filtering, `sync` and `check` fail with the source and missing target instead of emitting
|
|
460
|
+
a dangling name. Optional guidance must be self-contained when its guide is absent. Use
|
|
461
|
+
the structured marker instead of a plain `` `name` skill`` reference so validation can
|
|
462
|
+
see it. The delimiter is `{% … %}`, not `{{ … }}`, so Angular templates in examples pass
|
|
463
|
+
through untouched. Resource files get variable substitution too, but no links.
|
|
@@ -16,11 +16,10 @@ that matter here, all captured in AGENT_PROFILES:
|
|
|
16
16
|
carry a TokenUsageInfo. Codex's rollout format is explicitly not a stable
|
|
17
17
|
interface, so the parser searches each line for the usage object instead of
|
|
18
18
|
walking a fixed path.
|
|
19
|
-
* Context budget. Claude's budget is capped at
|
|
20
|
-
boundary
|
|
21
|
-
|
|
22
|
-
handing off into a fresh session ever would.
|
|
23
|
-
its budget is just the window.
|
|
19
|
+
* Context budget. Claude's budget is capped at its 200k long-context pricing
|
|
20
|
+
boundary. Codex uses the model-specific pricing boundary where OpenAI
|
|
21
|
+
documents one, and the reported window otherwise. Crossing either boundary
|
|
22
|
+
costs far more than handing off into a fresh session ever would.
|
|
24
23
|
* How a handoff is invoked. Claude has a slash command and /clear; Codex has
|
|
25
24
|
neither and reads the skill from disk. The skill's own name differs per repo
|
|
26
25
|
(`ethlete-handoff` where the generator installed it, `handoff` where the repo
|
|
@@ -63,6 +62,15 @@ CRITICAL_FRACTION = 0.85
|
|
|
63
62
|
# context at the long-context premium rate — so the budget never exceeds it.
|
|
64
63
|
PREMIUM_BOUNDARY = 200_000
|
|
65
64
|
|
|
65
|
+
# Codex models whose long-context pricing starts above 272k input tokens. Models
|
|
66
|
+
# without that pricing rule use their reported context window instead.
|
|
67
|
+
CODEX_PREMIUM_BOUNDARIES = (
|
|
68
|
+
("gpt-5.6", 272_000),
|
|
69
|
+
("gpt-5.5", 272_000),
|
|
70
|
+
("gpt-5.4-mini", None),
|
|
71
|
+
("gpt-5.4", 272_000),
|
|
72
|
+
)
|
|
73
|
+
|
|
66
74
|
# Context window (tokens) per model, matched by substring against the model id
|
|
67
75
|
# from the transcript — first match wins. Edit these as model windows change;
|
|
68
76
|
# anything unmatched falls back to DEFAULT_WINDOW.
|
|
@@ -83,6 +91,7 @@ DEFAULT_WINDOW = 200_000
|
|
|
83
91
|
AGENT_PROFILES = {
|
|
84
92
|
"claude": {
|
|
85
93
|
"premium_boundary": PREMIUM_BOUNDARY,
|
|
94
|
+
"premium_boundaries": (),
|
|
86
95
|
# Claude's transcript reports no window, so it is resolved from the model id.
|
|
87
96
|
"default_window": None,
|
|
88
97
|
"auto_modes": ("auto",),
|
|
@@ -101,6 +110,7 @@ AGENT_PROFILES = {
|
|
|
101
110
|
},
|
|
102
111
|
"codex": {
|
|
103
112
|
"premium_boundary": None,
|
|
113
|
+
"premium_boundaries": CODEX_PREMIUM_BOUNDARIES,
|
|
104
114
|
# Only reached if a rollout omits model_context_window; the CONTEXT_WINDOWS table
|
|
105
115
|
# holds Claude model ids and would never match a Codex one.
|
|
106
116
|
"default_window": 272_000,
|
|
@@ -214,6 +224,15 @@ def window_for(model):
|
|
|
214
224
|
return DEFAULT_WINDOW
|
|
215
225
|
|
|
216
226
|
|
|
227
|
+
def premium_boundary_for(profile, model):
|
|
228
|
+
"""Model-specific pricing boundary, falling back to the agent-wide value."""
|
|
229
|
+
if model:
|
|
230
|
+
for needle, boundary in profile["premium_boundaries"]:
|
|
231
|
+
if needle in model:
|
|
232
|
+
return boundary
|
|
233
|
+
return profile["premium_boundary"]
|
|
234
|
+
|
|
235
|
+
|
|
217
236
|
def claude_context_state(transcript_path):
|
|
218
237
|
"""(tokens, model, window) from the last main-chain assistant message.
|
|
219
238
|
|
|
@@ -376,7 +395,7 @@ def main():
|
|
|
376
395
|
|
|
377
396
|
tokens, model, reported_window = CONTEXT_READERS[agent](transcript_path)
|
|
378
397
|
window = reported_window or profile["default_window"] or window_for(model)
|
|
379
|
-
boundary = profile
|
|
398
|
+
boundary = premium_boundary_for(profile, model)
|
|
380
399
|
budget = min(window, boundary) if boundary else window
|
|
381
400
|
warn_tokens = int(budget * WARN_FRACTION)
|
|
382
401
|
critical_tokens = int(budget * CRITICAL_FRACTION)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lint-and-format
|
|
3
|
-
description: Run lint with --fix before fixing anything by hand, and format every edited file.
|
|
3
|
+
description: Run lint with --fix before fixing anything by hand, and format every edited source file Prettier supports.
|
|
4
4
|
kind: rule
|
|
5
5
|
scope: both
|
|
6
6
|
vars: [lintCommand, lintFixCommand, formatCommand]
|
|
@@ -16,10 +16,11 @@ so let them do the work before correcting anything by hand:
|
|
|
16
16
|
{%lintCommand%} # then re-run to see what needs a manual fix
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
For
|
|
20
|
-
|
|
19
|
+
For judgment calls lint cannot enforce, load the repository's focused guidance for the
|
|
20
|
+
code you are changing.
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
Format every edited Prettier-supported source file before wrapping up. Do not send binary,
|
|
23
|
+
generated, or unsupported files to this command:
|
|
23
24
|
|
|
24
25
|
```bash
|
|
25
26
|
{%formatCommand%}
|
|
@@ -12,4 +12,4 @@ scope: both
|
|
|
12
12
|
- **Bridge, don't copy.** Cross the boundary with `toSignal()` / `toObservable()`, never by
|
|
13
13
|
`.subscribe()`-ing and assigning the value somewhere.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
For subscriptions and effects, load the repository's focused reactive-state guidance.
|
package/content/rules/styling.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: styling
|
|
3
|
-
description: Component CSS is plain CSS in @layer components, and
|
|
3
|
+
description: Component CSS is plain CSS in @layer components, and hardcoded colours are never primary values.
|
|
4
4
|
kind: rule
|
|
5
5
|
scope: both
|
|
6
6
|
requires: ['@ethlete/core']
|
|
@@ -26,10 +26,12 @@ utility can win.
|
|
|
26
26
|
rule, so source order decides. Leave interaction states (`:hover`, `:focus-visible`,
|
|
27
27
|
`:active`) bare so they escalate and win.
|
|
28
28
|
|
|
29
|
-
**Never
|
|
30
|
-
from the surface and colour theming tokens
|
|
31
|
-
|
|
29
|
+
**Never use a hardcoded colour as the primary value.** Backgrounds, text, borders and
|
|
30
|
+
interaction states resolve from the surface and colour theming tokens
|
|
31
|
+
(`--et-surface-*-solid`, `--et-theme-color-*`). A static fallback inside
|
|
32
|
+
`var(--token, <fallback>)` is permitted, but not required.
|
|
32
33
|
|
|
33
34
|
Theme **names** (`brand`, `danger`, `dark-elevated`, …) are registered by the application;
|
|
34
|
-
the SDK ships none. Never hardcode
|
|
35
|
-
|
|
35
|
+
the SDK ships none. Never hardcode them as an SDK-defined union or reusable API contract.
|
|
36
|
+
If an app-specific example names one, label it as belonging to that app. Semantic colours
|
|
37
|
+
resolve by theme `type` (e.g. `injectErrorTheme()`).
|
|
@@ -22,10 +22,10 @@ DOM/`window`, output naming, class-member + decorator-metadata order, no
|
|
|
22
22
|
|
|
23
23
|
```html
|
|
24
24
|
<!-- ❌ runs every CD cycle -->
|
|
25
|
-
<button [disabled]="isDisabled()">
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
<button [disabled]="isDisabled()"></button>
|
|
26
|
+
|
|
27
|
+
<!-- ✅ computed signal -->
|
|
28
|
+
<button [disabled]="disabled()"></button>
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
## Lifecycle
|
|
@@ -51,8 +51,8 @@ DOM/`window`, output naming, class-member + decorator-metadata order, no
|
|
|
51
51
|
|
|
52
52
|
- Inline template/styles for small components; external `.html` / `.css` files
|
|
53
53
|
for complex ones.
|
|
54
|
-
- Component CSS is plain CSS wrapped in `@layer components`, with every colour
|
|
55
|
-
|
|
54
|
+
- Component CSS is plain CSS wrapped in `@layer components`, with every primary colour
|
|
55
|
+
value coming from the repository's theme tokens.
|
|
56
56
|
|
|
57
57
|
## Reactive state
|
|
58
58
|
|
|
@@ -31,7 +31,12 @@ app to checkout. They are per machine, so the map lives in the gitignored
|
|
|
31
31
|
```json
|
|
32
32
|
{
|
|
33
33
|
"apiRepoPaths": {
|
|
34
|
-
"hub": "../fut-hub-backend"
|
|
34
|
+
"hub": "../fut-hub-backend",
|
|
35
|
+
"*": "../shared-backend"
|
|
36
|
+
},
|
|
37
|
+
"apiRepoBranches": {
|
|
38
|
+
"hub": "develop",
|
|
39
|
+
"*": "main"
|
|
35
40
|
}
|
|
36
41
|
}
|
|
37
42
|
```
|
|
@@ -41,8 +46,8 @@ Read that file before searching anywhere. The rules:
|
|
|
41
46
|
- **The key is the app** as this repo names it - the workspace project name, which is
|
|
42
47
|
normally also the folder under `apps/`. Match the app you are working in.
|
|
43
48
|
- **A relative path resolves from the repo root**, not from the app folder.
|
|
44
|
-
- **
|
|
45
|
-
|
|
49
|
+
- **Require an exact app key.** If several apps intentionally share one API, configure
|
|
50
|
+
the explicit `"*"` fallback. Never treat an unrelated single entry as a fallback.
|
|
46
51
|
- **No matching entry - stop and ask.** Do not guess a sibling folder and do not clone
|
|
47
52
|
the repository. Say which app you needed the API for and offer the snippet above; the
|
|
48
53
|
file is gitignored, so adding it changes nothing for anyone else.
|
|
@@ -51,28 +56,32 @@ Without a checkout, fall back to what the running API tells you: the generated A
|
|
|
51
56
|
description if the project serves one (`/openapi.json`, `/swagger`, `/api/doc`), and the
|
|
52
57
|
real response body of the call you are debugging.
|
|
53
58
|
|
|
54
|
-
## 2.
|
|
59
|
+
## 2. Resolve the relevant files before judging checkout state
|
|
55
60
|
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
Search by the route, field, or error you already know. Record the contract, serializer,
|
|
62
|
+
handler, and tests that can answer the question. Then inspect checkout state only for
|
|
63
|
+
those paths:
|
|
58
64
|
|
|
59
65
|
```bash
|
|
60
|
-
git -C <apiRepoPath>
|
|
61
|
-
git -C <apiRepoPath> status
|
|
66
|
+
git -C <apiRepoPath> status -sb
|
|
67
|
+
git -C <apiRepoPath> status --short -- <relevant-paths>
|
|
68
|
+
git -C <apiRepoPath> diff -- <relevant-paths>
|
|
62
69
|
```
|
|
63
70
|
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
Unrelated dirty files are not evidence about these paths. Continue without blocking on
|
|
72
|
+
them. If a relevant file is dirty, distinguish the worktree implementation from the
|
|
73
|
+
committed or deployed behavior. Ask only when that difference changes the answer or the
|
|
74
|
+
user has to choose which behavior matters.
|
|
66
75
|
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
`apiRepoBranches` configures the expected branch per exact app key, with `"*"` as the
|
|
77
|
+
only fallback. If no branch is configured, report the current branch as context; do not
|
|
78
|
+
invent a blocking “development branch” requirement. A different or ahead branch matters
|
|
79
|
+
only when the relevant files differ in that commit range.
|
|
69
80
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
- **Dirty** - it holds someone's work in progress. Say so rather than quoting it as API
|
|
75
|
-
behaviour.
|
|
81
|
+
Use existing remote refs first. `git fetch` preserves the worktree but is not read-only:
|
|
82
|
+
it uses the network and mutates remote refs. Fetch only when freshness is material, and
|
|
83
|
+
request any approval the environment requires. Never switch, pull, stash, or reset the
|
|
84
|
+
checkout yourself.
|
|
76
85
|
|
|
77
86
|
## 3. The checkout is not the environment the app calls
|
|
78
87
|
|
|
@@ -111,7 +120,7 @@ they name the status code and the body for each case.
|
|
|
111
120
|
|
|
112
121
|
It is a different repository with its own branch, review and release process. Never edit
|
|
113
122
|
it while working on a task in this repo, and never change its git state without being
|
|
114
|
-
asked
|
|
123
|
+
asked. A fetch also needs to meet the freshness and approval conditions above.
|
|
115
124
|
|
|
116
125
|
When the fix belongs in the API, say so precisely: the endpoint, the field, and the
|
|
117
126
|
behaviour it should have. Then handle the API as it is today - a client workaround for a
|
|
@@ -15,18 +15,15 @@ Three things arrive from Figma, and each answers a different question:
|
|
|
15
15
|
| **`.css`** (Copy as CSS) | Named layers, auto-layout properties, typography metrics, design-token names | Any hierarchy at all — the dump is flat |
|
|
16
16
|
| **`.png`** (a screenshot) | Figma's own blue measurement overlays, and what the designer chose to frame | Nothing machine-readable |
|
|
17
17
|
|
|
18
|
-
**Ask for the `.svg` and the `.css` together
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
`17px` you inferred from a 12px outlined glyph is worse than an open question, because it
|
|
28
|
-
survives review as though it had been specified. If the pair genuinely cannot be produced, say
|
|
29
|
-
in the write-up which numbers are therefore guesses.
|
|
18
|
+
**Ask for the `.svg` and the `.css` together.** That pair is the preferred complete input:
|
|
19
|
+
the SVG is the export you can look at and measure, and the CSS names layers and records
|
|
20
|
+
type. A PNG earns its place only when it is a _screenshot_ carrying dev-mode annotations.
|
|
21
|
+
Exports expose rendered color values, but they do not identify the authoritative semantic
|
|
22
|
+
theme token.
|
|
23
|
+
|
|
24
|
+
If one artifact is missing, inspect the existing code and the artifact you do have. List
|
|
25
|
+
the exact facts that remain unknown and do not invent their measurements. Ask before a
|
|
26
|
+
structural choice or an untraceable numeric value, not before useful read-only inspection.
|
|
30
27
|
|
|
31
28
|
## 1. Read the export before touching code
|
|
32
29
|
|
|
@@ -126,8 +123,8 @@ it; the SVG has the tree but no names. The image is what disambiguates:
|
|
|
126
123
|
- **Authoritative:** geometry and typography metrics — widths, padding, gaps, `flex-grow`,
|
|
127
124
|
border radius, font size / weight / line-height / letter-spacing, and the breakpoints at
|
|
128
125
|
which the layout changes.
|
|
129
|
-
- **Never authoritative: colour.** Backgrounds, text, borders and
|
|
130
|
-
from the
|
|
126
|
+
- **Never authoritative: semantic colour choice.** Backgrounds, text, borders and
|
|
127
|
+
interaction states resolve from the repository's theme tokens. A hex in the
|
|
131
128
|
export is information about the _designer's_ palette, not a value to paste. Where the
|
|
132
129
|
export's colour and the token disagree, keep the token and note the delta for the design
|
|
133
130
|
review; the export can be wrong about contrast in a way the tokens are not. This holds
|
|
@@ -181,13 +178,14 @@ Harness gotchas, each of which will cost you an hour:
|
|
|
181
178
|
~1px wide of reality, so a label that wraps in the harness may well fit in the app. Check
|
|
182
179
|
before calling a wrap a defect.
|
|
183
180
|
- Playwright is CommonJS and unresolvable from a scratch directory — `createRequire` against
|
|
184
|
-
the repo root, as the template does.
|
|
185
|
-
|
|
181
|
+
the repo root, as the template does. When driving a story, follow the repository's
|
|
182
|
+
installed Storybook verification guidance if present.
|
|
186
183
|
|
|
187
184
|
## 5. Close the loop
|
|
188
185
|
|
|
189
186
|
Report the measured numbers next to the export's, per width — not "matches the design". Say
|
|
190
187
|
explicitly which parts of the export you deliberately did **not** implement and why (colour
|
|
191
188
|
kept as tokens, a label the product decided never to render, a field the API lacks). Then
|
|
192
|
-
|
|
193
|
-
|
|
189
|
+
report that the export files are no longer needed. Delete only artifacts this workflow
|
|
190
|
+
created and only when the workflow or user explicitly authorizes cleanup; otherwise let the
|
|
191
|
+
user decide whether supplied exports remain useful records.
|
|
@@ -27,7 +27,7 @@ npx ethlete-agents git-flow repair <branch> # rename a non-conforming one,
|
|
|
27
27
|
its parent Story's branch, so that branch has to exist first.
|
|
28
28
|
|
|
29
29
|
It reads the issue through the running Timetrack app, which holds this machine's only Jira
|
|
30
|
-
credentials
|
|
30
|
+
credentials. If it reports that the app is not running, ask the
|
|
31
31
|
user to start it. `--subject <text>` names a branch without reading Jira at all.
|
|
32
32
|
|
|
33
33
|
## The five shapes
|
|
@@ -84,4 +84,4 @@ legitimately target it. Leave a live one alone.
|
|
|
84
84
|
## Commit messages are a separate thing
|
|
85
85
|
|
|
86
86
|
Commits stay conventional (`feat(platform): Prefer a player's common name`) and carry **no
|
|
87
|
-
issue key** - the branch already has it.
|
|
87
|
+
issue key** - the branch already has it. Follow the repository's commit-message guidance.
|
|
@@ -94,9 +94,9 @@ resume from it.
|
|
|
94
94
|
3. Verify reality still matches: current branch, `git status`, last commit. If
|
|
95
95
|
they diverge from the handoff (e.g. someone committed in between), say what
|
|
96
96
|
changed and adapt - the repo is the truth, the handoff is the guide.
|
|
97
|
-
4. Read any
|
|
98
|
-
before CSS work) - same rules as always.
|
|
97
|
+
4. Read any focused repository guidance the handoff's work needs - same rules as always.
|
|
99
98
|
5. Continue with the **Next steps** section. Don't redo work listed under
|
|
100
99
|
_Done_; don't re-open questions under _Decisions_.
|
|
101
|
-
6. When every next step is complete
|
|
102
|
-
|
|
100
|
+
6. When every next step is complete, decide whether the handoff can be removed. Delete it
|
|
101
|
+
only if this workflow created it and the save workflow or user authorized cleanup.
|
|
102
|
+
Otherwise report that it is no longer needed and let the user decide.
|
|
@@ -97,23 +97,34 @@ requests; debounce at the input if you need it.
|
|
|
97
97
|
|
|
98
98
|
## Bridging a query into RxJS / other APIs
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
`(
|
|
102
|
-
|
|
100
|
+
For a callback that must start one request and return one correlated result, use a
|
|
101
|
+
manual query with `executeUntilSettled()`. Its frozen snapshot cannot be replaced by a
|
|
102
|
+
later execution, and the observable completes after that one result.
|
|
103
103
|
|
|
104
104
|
```ts
|
|
105
|
-
|
|
106
|
-
private
|
|
107
|
-
|
|
108
|
-
fetch(query: string) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
105
|
+
class ItemSource {
|
|
106
|
+
private itemsQuery = getItems({ onlyManualExecution: true });
|
|
107
|
+
|
|
108
|
+
fetch(query: string) {
|
|
109
|
+
return defer(() => executeUntilSettled(this.itemsQuery, { args: { queryParams: { q: query } } })).pipe(
|
|
110
|
+
map((snapshot) => {
|
|
111
|
+
const response = snapshot.response();
|
|
112
|
+
|
|
113
|
+
if (response === null) throw snapshot.error();
|
|
114
|
+
|
|
115
|
+
return response.items;
|
|
116
|
+
}),
|
|
117
|
+
);
|
|
118
|
+
}
|
|
114
119
|
}
|
|
115
120
|
```
|
|
116
121
|
|
|
122
|
+
Do not set a search signal and immediately return the shared `response` stream: the
|
|
123
|
+
previous response is retained during re-execution and can be the first non-null emission.
|
|
124
|
+
Unsubscribing from the wrapper stops result delivery but does not by itself abort the
|
|
125
|
+
promise-backed execution; use the query's reactive `withArgs` lifecycle when cancellation
|
|
126
|
+
is a requirement rather than a callback contract.
|
|
127
|
+
|
|
117
128
|
## Gotchas
|
|
118
129
|
|
|
119
130
|
- Signals-first: read `query.response()` in templates/computeds; it's **nullable**
|
|
@@ -34,9 +34,15 @@ const data = toSignal(obs$);
|
|
|
34
34
|
|
|
35
35
|
## Using RxJS correctly
|
|
36
36
|
|
|
37
|
-
- **
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
- **Tear down long-lived or manual subscriptions.** Finite streams that complete on their
|
|
38
|
+
own need no artificial lifecycle operator. For Angular lifecycle cleanup, prefer
|
|
39
|
+
`takeUntilDestroyed()` (it needs an injection context) or explicitly unsubscribe.
|
|
40
|
+
Do not use `takeWhile` as destruction cleanup: without another emission it stays
|
|
41
|
+
subscribed. Use `take(1)` or `first()` only when one emission is the operation's
|
|
42
|
+
intended semantics.
|
|
43
|
+
- **Place lifecycle teardown after higher-order operators** such as `switchMap`, so their
|
|
44
|
+
inner subscriptions are also covered. Other limiting and finalization operators do not
|
|
45
|
+
have a universal “last” position; place them where their semantics belong.
|
|
40
46
|
- **Side effects go in `tap()`**, never in the `subscribe()` callback - keep
|
|
41
47
|
`subscribe()` empty.
|
|
42
48
|
- **Don't reach for RxJS inside `effect()`/`computed()`.** Subscribing per run
|
|
@@ -28,7 +28,7 @@ Page URLs follow `{%docsBaseUrl%}/<lib>/<topic>`. The library sections:
|
|
|
28
28
|
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
29
29
|
| `/components/` | The active UI library - one guide per domain (see the list below) |
|
|
30
30
|
| `/core/` | Framework primitives: `theming`, `overlay-runtime`, `signal-utils`, `element-signals`, `animations`, `scrolling`, `drag-resize`, `directives-pipes`, `providers`, `seo`, `utilities` |
|
|
31
|
-
| `/query/` | Data fetching
|
|
31
|
+
| `/query/` | Data fetching; load the dedicated query guide first when this package emitted one |
|
|
32
32
|
| `/cdk/` | The predecessor UI toolkit, maintenance mode. Only for code that still uses it |
|
|
33
33
|
| `/contentful/`, `/cli/`, `/eslint/`, `/types/` | The remaining packages |
|
|
34
34
|
|
|
@@ -66,13 +66,14 @@ That list is a snapshot. The site itself is machine-readable, so fetch rather th
|
|
|
66
66
|
`-next` prereleases should read `{%docsBaseUrl%}` and `{%sdkStorybookUrl%}` only if
|
|
67
67
|
they are the matching prerelease deployments, otherwise expect drift and verify
|
|
68
68
|
against the installed `.d.ts` in `node_modules/@ethlete/<lib>`.
|
|
69
|
-
-
|
|
70
|
-
|
|
69
|
+
- **Use the SDK skills instead of searching `node_modules` ad hoc.** The installed `.d.ts`
|
|
70
|
+
files are authoritative for the public type surface this consumer can compile against.
|
|
71
|
+
Source matching that installed build is authoritative for runtime implementation details.
|
|
72
|
+
A dirty or ahead `next` checkout is not automatically source for the installed package.
|
|
73
|
+
If docs and installed types disagree, report the drift.
|
|
71
74
|
- **Never treat a `subtle` namespace as public API.** Anything exposed under `subtle` is
|
|
72
75
|
an unsupported escape hatch that can change without a major version.
|
|
73
76
|
|
|
74
77
|
## Related
|
|
75
78
|
|
|
76
|
-
- Data fetching has its own guide: {%skill:query%}
|
|
77
|
-
- Theming tokens and how to register themes: {%skill:theming%}
|
|
78
79
|
- When the docs cannot answer it, read the SDK source: {%skill:sdk-source%}
|
|
@@ -44,6 +44,21 @@ If a build stalls trying to reach Nx Cloud, re-run it with `NX_NO_CLOUD=true`.
|
|
|
44
44
|
|
|
45
45
|
## 3. Point this repo at the build
|
|
46
46
|
|
|
47
|
+
Preflight the manifest and lockfile before changing either:
|
|
48
|
+
|
|
49
|
+
Use {%resource:sdk-local-baseline.mjs%} to capture their exact bytes:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
git status --short -- <manifest> yarn.lock
|
|
53
|
+
sdk_local_baseline_dir=$(mktemp -d)
|
|
54
|
+
node <path-to-sdk-local-baseline.mjs> capture <manifest> yarn.lock "$sdk_local_baseline_dir"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
If either file already differs and you cannot tell who owns the edit, stop and ask. If
|
|
58
|
+
the user authorizes the experiment, keep the exact baseline above; pre-existing edits
|
|
59
|
+
are part of it and must survive byte-for-byte. If either file changes for another reason
|
|
60
|
+
during the experiment, stop and take a new agreed baseline before cleanup.
|
|
61
|
+
|
|
47
62
|
Edit the version specifiers in `package.json` (the one declaring the dependency - in a
|
|
48
63
|
workspace that is the workspace package, not necessarily the root):
|
|
49
64
|
|
|
@@ -97,17 +112,20 @@ reinstalling; the restart in step 3 is still required.)
|
|
|
97
112
|
## 6. Clean up when you are done
|
|
98
113
|
|
|
99
114
|
Leaving a `file:` dependency behind breaks every other checkout and CI, because the path
|
|
100
|
-
does not exist there. Restore
|
|
115
|
+
does not exist there. Restore the exact recorded baseline as part of the same task:
|
|
101
116
|
|
|
102
117
|
```bash
|
|
103
|
-
|
|
104
|
-
yarn install
|
|
105
|
-
|
|
118
|
+
node <path-to-sdk-local-baseline.mjs> restore <manifest> yarn.lock "$sdk_local_baseline_dir"
|
|
119
|
+
yarn install --immutable
|
|
120
|
+
node <path-to-sdk-local-baseline.mjs> verify <manifest> yarn.lock "$sdk_local_baseline_dir"
|
|
106
121
|
```
|
|
107
122
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
123
|
+
The verification proves that the local-build delta is gone while preserving any edits
|
|
124
|
+
that existed before it. Do not require either file to be globally clean, and do
|
|
125
|
+
not run whole-file `git checkout` or `git restore`. Report the temporary baseline path;
|
|
126
|
+
it can be removed after verification because this workflow created it. Never commit a
|
|
127
|
+
`file:` specifier or the lockfile it produced. If the verified fix is still unreleased,
|
|
128
|
+
say what has to be published instead of shipping a local path.
|
|
111
129
|
|
|
112
130
|
## Related
|
|
113
131
|
|