Skip to content

Commit b736cdc

Browse files
authored
Merge pull request #2063 from jaylfc/sync/dev-to-master-beta42
Merge dev into master: v1.0.0-beta.42
2 parents 67f9f2c + e2d931d commit b736cdc

129 files changed

Lines changed: 17327 additions & 1689 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/skills/taos-development-skill/SKILL.md

Lines changed: 129 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,16 @@ Procedures and architecture for contributing to
2222
routes/ ← one APIRouter module per feature area (~127 modules)
2323
templates/ ← minimal: agent_debugger.html only (frontend is React SPA)
2424
channel_hub/ ← framework-agnostic messaging: connectors + MessageRouter
25-
adapters/ ← thin per-framework agent adapters (~25 lines each)
25+
adapters/ ← per-framework agent adapters (generic ~20 lines, acp ~500); each declares verification_status
2626
cluster/ ← distributed compute: worker registry, task routing, GPU lease
2727
worker/ ← cross-platform worker apps (system tray, Android, iOS)
2828
*_store.py, base_store.py ← data layer: top-level BaseStore subclasses (aiosqlite); one SQLite file per store; no stores/ dir
2929
chat/ projects/ mcp/ ← chat, project board/canvas/A2A, MCP proxy+permissions
3030
installers/ containers/ ← model/app installers; Docker + LXC backends
3131
migrations/ ← DB migrations
3232
desktop/ ← React + TypeScript SPA (Vite)
33-
app-catalog/ ← YAML app manifests + catalog.yaml (~108 apps)
34-
tests/ ← pytest suite (~4,845 tests)
33+
app-catalog/ ← YAML app manifests + catalog.yaml
34+
tests/ ← pytest suite (large; counts rot - trust the tree)
3535
docs/ ← documentation; agent manual compiled from docs/agent-manual/
3636
```
3737

@@ -47,10 +47,8 @@ Procedures and architecture for contributing to
4747
lifespan (`app.state.secrets`, …). Never reference a migration-added column inside `SCHEMA`.
4848
- **Config** - `AppConfig` dataclass in `config.py`; YAML serialisation; async-locked saves via
4949
`save_config_locked()`; typed backends (`rkllama`, `ollama`, `openai`, `anthropic`, …).
50-
- **Templates** - all real UI work goes in the `desktop/` React SPA. The Pico CSS + htmx guidance
51-
applies **only** to the single legacy Jinja template (`agent_debugger.html`): **Pico CSS utility
52-
classes only** (no other CSS framework), htmx (`hx-get`, `hx-target`, `hx-swap`) for dynamic
53-
partials, semantic HTML, ARIA labels on interactive elements without visible text.
50+
- **Templates** - all real UI work goes in the `desktop/` React SPA. One legacy Jinja template
51+
exists (`agent_debugger.html`); if you must touch it, match its existing Pico CSS + htmx style.
5452
- **Frontend** - React + TypeScript SPA in `desktop/`. Built with Vite: `npm run build` outputs
5553
to `static/desktop/` (gitignored). For development: `npm run dev` serves with hot reload on
5654
port 5173. One concern per component; API calls in dedicated hooks or service files.
@@ -92,10 +90,11 @@ Procedures and architecture for contributing to
9290
--title "feat(scope): description" --body "Fixes #<issue>. Tests: N/N pass."
9391
gh pr ready <PR#>
9492
```
95-
Do NOT wait for CI to *start* - fork PRs are gated behind maintainer workflow approval, so a
96-
fresh fork PR's matrix run may never begin on its own; mark ready once the CODE is done and
97-
local tests pass. This does NOT mean ignore red CI: once the matrix runs you own making it
98-
green - re-check `gh pr checks <PR#>` and fix any red matrix job before considering the task done.
93+
Fork CI approval is per-contributor: a FIRST-TIME contributor's workflow runs sit in
94+
`action_required` until a maintainer approves (mark ready once the CODE is done and local tests
95+
pass, then surface the approval need); a RETURNING contributor's CI runs automatically - no
96+
approval wait, and you own making it green (`gh pr checks <PR#>`: test 3.12/3.13 + lint +
97+
spa-build all pass) before considering the task done.
9998

10099
6. **Never commit directly to `dev` or `master`.** All work happens on branches.
101100
Main only receives merges via upstream PR approval.
@@ -147,6 +146,9 @@ uv run pytest tests/ --ignore=tests/e2e -n auto
147146
- Python 3.12 + 3.13 on every PR/push; 3.11 on nightly cron only
148147
- GitHub Actions: `.github/workflows/ci.yml` in upstream repo
149148
- Uses `uv sync --frozen` and `pytest -n auto`
149+
- Also required: `spa-build` (npm build + tsc + **vitest** - a desktop type error or failing
150+
component test fails CI), a "Verify app starts" `create_app` import smoke, `lint`
151+
(`compileall`), and `cla`. The doc-gate is a separate workflow.
150152

151153
## CLA - HUMAN signs
152154

@@ -180,24 +182,37 @@ I have read the CLA Document and I hereby sign the CLA
180182

181183
## Post-Push Bot Review Cycle
182184

183-
After pushing a PR and marking it ready, automated bots (Kilo, CodeRabbit) run reviews.
184-
Address their findings **before** surfacing the PR for human maintainer review - this
185-
eliminates the wasteful push→block→manual-check→unblock→re-dispatch cycle.
185+
After pushing a PR and marking it ready, automated bots review it. The reliable gate is
186+
**Kilo Code Review + Gitar**. CodeRabbit is unreliable - a "pass" check can be a rate-limited
187+
no-op, so never treat a CodeRabbit pass alone as evidence of review (its findings, when it does
188+
run, still get folded). Qodo (`qodo-code-review`) appears on old PRs but is paused. Address all
189+
findings **before** surfacing the PR for human maintainer review.
186190

187191
### Procedure
188192

189193
1. **Push PR and mark ready.** Wait ~10 minutes for bot reviews to complete.
190-
2. **Pull bot comments:**
194+
2. **Pull bot findings - reviews AND inline comments, all bots.** The login FORM differs by API
195+
surface, so filter accordingly: the GraphQL command (`gh pr view --json`) returns BARE logins
196+
(`kilo-code-bot`, `gitar-bot`, `coderabbitai`, `qodo-code-review`), while the REST command
197+
(`gh api .../comments`) returns the `[bot]`-suffixed form (`kilo-code-bot[bot]`). The GraphQL
198+
filter below uses a substring `test()` so it matches the bare form; the REST command does not
199+
filter, so the suffix is harmless there.
191200
```bash
192-
gh pr view <PR#> --repo jaylfc/taOS --json comments --jq \
193-
'.comments[] | select(.author.login == "kilo-code-bot" or .author.login == "coderabbitai[bot]")'
201+
# Review summaries + issue comments (GraphQL, bare logins):
202+
gh pr view <PR#> --repo jaylfc/taOS --json reviews,comments --jq \
203+
'(.reviews[], .comments[]) | select((.author.login? // "") | test("kilo-code-bot|gitar-bot|coderabbitai|qodo")) | {login: .author.login, body: .body}'
204+
# Inline (line-anchored) review comments (REST, [bot]-suffixed) - Kilo/CodeRabbit post most findings here:
205+
gh api repos/jaylfc/taOS/pulls/<PR#>/comments --jq \
206+
'.[] | {login: (.user.login? // ""), path, line, body}'
194207
```
208+
Check which commit SHA each bot actually reviewed - a "pass" on a stale commit is not a pass
209+
on your head.
195210
3. **If issues found:** fix all findings in a single commit, re-run local tests, push,
196211
then go back to step 1 (max 2 cycles). If findings still persist after 2 cycles, stop -
197212
do not loop further; surface the remaining findings to the human.
198-
4. **Only block for maintainer review when bots are clean** - 0 CRITICAL, 0 WARNING.
199-
If a SUGGESTION-only finding is genuinely not applicable, note the rationale in a
200-
PR comment before blocking.
213+
4. **Only block for maintainer review when bots are clean** - 0 CRITICAL, 0 WARNING - and fold
214+
EVERY finding, nits and suggestions included. If a SUGGESTION is genuinely not applicable,
215+
note the rationale in a PR comment before blocking.
201216

202217
### Severity tiers
203218

@@ -207,14 +222,59 @@ eliminates the wasteful push→block→manual-check→unblock→re-dispatch cycl
207222
| WARNING | Must fix before blocking for review |
208223
| SUGGESTION | Fix or explain why not applicable |
209224

210-
### Time estimates
225+
## PR lifecycle discipline (fold-first, rebase, closures)
211226

212-
| Phase | Duration |
213-
|-------|----------|
214-
| First bot pass (Kilo + CodeRabbit) | ~10 min |
215-
| Fix cycle (if needed) | ~5–10 min |
216-
| Second bot pass (if re-pushed) | ~10 min |
217-
| **Worst case (2 cycles)** | **~30 min** |
227+
The review pipeline only works if the open-PR set stays small and every finding
228+
gets closed out. These rules are load-bearing; the maintainer gates on them.
229+
230+
### Fold-first: findings outrank new work
231+
232+
If ANY of your open PRs has an unaddressed maintainer fold list or bot finding,
233+
addressing it comes BEFORE opening a new PR. Priority order each work session:
234+
235+
1. Fold open findings on existing PRs (maintainer comments first, then bot findings).
236+
2. Rebase any of your PRs that show CONFLICTING against the base branch.
237+
3. Only then start a new slice.
238+
239+
A finding folded within hours merges the same day; a finding left while you open
240+
new PRs stalls the whole train behind it (the maintainer will not merge past an
241+
open finding, ever).
242+
243+
### Rebase cadence and the stale-base rule
244+
245+
- dev moves fast. When your PR shows CONFLICTING, rebase onto current dev
246+
promptly - a CONFLICTING PR is invisible to the merge queue.
247+
- A green CI run computed BEFORE the base branch moved is STALE. Two individually
248+
green PRs can conflict semantically with zero textual conflict (see the
249+
#2009/#1932 App-key incident, fixed in #2041). If dev advanced since your last
250+
CI run, rebase (or push an empty commit) so CI re-runs against the current base
251+
before asking for merge.
252+
- Keep your open-PR count small (aim under 10). A wide-open set guarantees most
253+
of it is permanently CONFLICTING and re-review effort is wasted.
254+
255+
### Closing PRs: always link the successor
256+
257+
Never close a PR silently. In the closing comment state exactly one of:
258+
259+
- "Superseded by #NNNN" (and confirm every still-open finding from this PR is
260+
in the successor's scope), or
261+
- "Landed via #NNNN" (when the content merged through another PR), or
262+
- "Abandoned because <reason>".
263+
264+
A close without a successor link reads as lost work and forces the maintainer
265+
into git forensics (this happened with #1927/#1924 - both were legitimate
266+
"landed via" closures that looked like data loss for hours).
267+
268+
### One PR per slice
269+
270+
- A fix and the test that proves it belong in ONE PR (pitfall 13 in
271+
docs/contributor-pitfalls.md).
272+
- Never open a sibling PR for a slice that already has one. If a fresh branch is
273+
genuinely needed, open the new PR, link it, and close the old one with the
274+
supersede note in the same action.
275+
276+
Read docs/contributor-pitfalls.md before every PR - fold lists reference its
277+
items by number (for example "pitfall 5").
218278

219279
## Common fix patterns
220280

@@ -233,6 +293,41 @@ eliminates the wasteful push→block→manual-check→unblock→re-dispatch cycl
233293
- **Debugging a test:** confirm it uses the async `client` fixture and that `tmp_data_dir` setup is
234294
complete; check the store's `init()`; isolate with `pytest <path>::<test> -v`.
235295

296+
## Frontend CSRF contract (session-authenticated SPA calls)
297+
298+
Every mutating route requires `X-CSRF-Token` on cookie-session requests (`verify_csrf` is attached
299+
router-wide). Any SPA `fetch` that POSTs/PUTs/PATCHes/DELETEs must attach the double-submit header:
300+
use `withCsrf(init)` from `desktop/src/lib/csrf.ts`, or the `taosFetch` wrapper
301+
(`desktop/src/lib/taos-fetch.ts`) which applies it automatically. **A raw
302+
`fetch("/api/...", {method:"POST"})` passes vitest AND pytest (tests bypass CSRF) but 403s
303+
"CSRF token missing" in production** - this exact class shipped as a bug (#1977). Bearer-token
304+
(agent JWT) calls are CSRF-exempt; only cookie sessions need the header.
305+
306+
## Agent auth model (Bearer JWT vs session)
307+
308+
Agents authenticate with grant-gated registry JWTs (`Authorization: Bearer ...`), which are
309+
**CSRF-exempt**; human sessions use cookies + the CSRF header. Access derives from an ACTIVE grant
310+
per `(agent, project)` - not from token claims - and no-grant yields an existence-hiding 404.
311+
Agent-facing routes must ALSO be allowlisted in `tinyagentos/auth_middleware.py` (see Pitfalls).
312+
Onboarding surfaces: project invites (`routes/project_invites.py`, URL + PIN), OS-level agent
313+
invites (`/api/agents/invites`), and the auth-request consent flow. Source of truth:
314+
`docs/agent-coordination.md` section "Agent API surface (scoped registry JWT)".
315+
316+
## Adapter verification vocabulary
317+
318+
Every adapter in `tinyagentos/adapters/__init__.py` declares
319+
`verification_status: tested | beta | experimental | broken` (plus `tracking_issue`, required for
320+
`broken`). `tested` and `beta` are the verified tiers (`verified_only` filtering returns both);
321+
`broken` blocks the deploy wizard. A new or edited adapter must set this correctly - defaulting a
322+
new framework to `experimental` is the norm until it passes round-trip verification.
323+
324+
## Version sync (release bumps)
325+
326+
Four files carry the version and must stay identical: `pyproject.toml`, `tinyagentos/__init__.py`,
327+
`desktop/package.json` (all `1.0.0-beta.N`), and `uv.lock`'s `tinyagentos` entry in PEP 440
328+
normalized form (`1.0.0bN`). Only pyproject vs uv.lock is test-gated
329+
(`tests/test_version_lock_sync.py`); the other two drift silently, so check all four on any bump.
330+
236331
## Documentation gate
237332

238333
A gate blocks PRs that add or remove certain feature code without a matching doc update
@@ -293,10 +388,11 @@ controls which hardware profiles see the app as recommended.
293388

294389
- **Full test suite is too large to run locally without `-n auto`.** Use the canonical
295390
gate: `uv run pytest tests/ --ignore=tests/e2e -n auto`. CI handles the full matrix.
296-
- **CI may show `action_required` on every PR from a fork.** GitHub requires maintainer approval
297-
for workflow runs from first-time contributor forks. This can re-trigger on each new PR even after
298-
previous PRs were approved - it's per-workflow-run, not per-contributor. Surface to the human;
299-
do NOT poll or re-push.
391+
- **Fork CI approval is first-time-only.** The repo's policy requires maintainer approval only for
392+
a contributor's FIRST fork PR (`action_required` on the CI run - surface to the human, do NOT
393+
poll or re-push). A returning contributor's CI runs automatically; if your checks show only bots
394+
green but no test/spa-build jobs at all, check `gh api "repos/jaylfc/taOS/actions/runs?head_sha=<sha>"`
395+
for `action_required` before assuming CI passed - bot-only green is NOT CI green.
300396
- **No formatter/linter *config* yet, but CI is not silent.** There is no `.ruff.toml` and no
301397
`[tool.ruff]`/`[tool.black]`/`[tool.mypy]` section in `pyproject.toml` (ruff may land soon -
302398
check `pyproject.toml` before assuming). CI does run a `lint` job (`python -m compileall
@@ -342,21 +438,8 @@ controls which hardware profiles see the app as recommended.
342438

343439
## Issue triage
344440

345-
### Finding actionable issues
346-
1. Filter GitHub issues by `good first issue` or `help wanted` labels
347-
2. Check issue age - fresh issues (< 2 weeks) have highest chance of being unclaimed
348-
3. Read the issue body carefully - look for clear reproduction steps
349-
4. Check if anyone is already assigned
350-
351-
### Difficulty estimation
352-
- **Catalog app addition** (~30 min): New manifest.yaml + catalog.yaml entry
353-
- **Bug fix** (1–3 hours): Reproduce → find root cause → fix + regression test
354-
- **Feature** (3+ hours): Design → implement → tests → documentation
355-
356-
### Before starting work
357-
1. Sync from upstream: `git fetch upstream dev`
358-
2. Verify the issue is still open and unassigned
359-
3. Comment on the issue: "Working on this - will open a draft PR"
441+
taOS-specific rules only: verify the issue is still open and **unassigned**, comment
442+
"Working on this - will open a draft PR" before starting, and sync from upstream `dev` first.
360443

361444
## First-run setup
362445

.claude/skills/taos-development-skill/soul.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,15 @@ desktop SPA and a YAML app catalog. It runs hardware-frugally across a Python 3.
1414
active development happens on `dev`. PRs go to `jaylfc/taOS:dev`.
1515
2. **Python 3.11 floor.** The pyproject.toml pins `>=3.11,<3.14`. `match`/`case` and `X | None`
1616
unions are available; most modules use `from __future__ import annotations`.
17-
3. **Conventional commits, no AI attribution.** `feat: fix: docs: refactor: test: chore:`. No
18-
"Co-authored-by" or "Generated with" trailers.
19-
4. **Draft-first; mark ready when the CODE is done, not when CI is green.** Fork PRs are gated
20-
behind maintainer workflow approval - waiting for green CI on a fork PR is a deadlock. Create
21-
the PR as draft, verify tests locally, then mark ready immediately.
17+
3. **Conventional commits, no AI attribution - anywhere public.** `feat: fix: docs: refactor:
18+
test: chore:`. No "Co-authored-by" or "Generated with" trailers, and the same rule extends to
19+
PR bodies, issue comments, and docs: no AI attribution and no em dashes in any public-facing
20+
text.
21+
4. **Draft-first; mark ready when the CODE is done.** Create the PR as draft, verify tests
22+
locally, then mark ready immediately. Fork-CI approval is first-time-only: a FIRST fork PR's
23+
CI waits for maintainer approval (waiting on it is a deadlock - mark ready and surface it); a
24+
RETURNING contributor's CI runs automatically, and you own making it green before the task is
25+
done.
2226
5. **The human account-holder signs the CLA - not the agent.** An agent must NOT post the CLA
2327
acceptance comment on the maintainer's behalf; it is a legal agreement. If the CLA check fails,
2428
surface the bot's link to the human.
@@ -50,10 +54,8 @@ System-level recovery is a human decision.
5054
- **One concern per module.** No cross-importing between route modules.
5155
- **Routes access stores via `request.app.state`** (dependency injection), not by importing stores
5256
directly. Check existing routes for the pattern.
53-
- **All real UI work goes in the `desktop/` React SPA.** The Pico CSS + htmx rules below apply
54-
**only** to the single legacy Jinja template (`agent_debugger.html`):
55-
- **Pico CSS utility classes only** for that template. No other CSS framework.
56-
- **htmx** (`hx-get`, `hx-target`, `hx-swap`) for its dynamic partials.
57+
- **All real UI work goes in the `desktop/` React SPA.** One legacy Jinja template exists
58+
(`agent_debugger.html`); if you must touch it, match its Pico CSS + htmx style.
5759
- **Semantic HTML; ARIA labels** on interactive elements without visible text.
5860
- **`async def`** route handlers; **`await`** all I/O.
5961
- **Pydantic** request/response models.

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ jobs:
112112
- uses: actions/checkout@v7
113113

114114
- name: Set up Node
115-
uses: actions/setup-node@v4
115+
uses: actions/setup-node@v7
116116
with:
117117
node-version: "20"
118118
cache: "npm"

0 commit comments

Comments
 (0)