Skip to content

Commit f593a5b

Browse files
committed
Merge remote-tracking branch 'origin/feat/expert-talk' into pr-clean-175
2 parents 6186933 + b03396d commit f593a5b

962 files changed

Lines changed: 51872 additions & 11706 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.

.agents/skills/agent-core-dev/orient.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,10 @@ There is no domain-layer numbering — a domain may import any other domain, gui
6868

6969
## Comment convention
7070

71-
`packages/agent-core-v2/AGENTS.md` bans comments: no file headers, no section banners, no statement-level narration — the code is the source of truth. The only exception is JSDoc attached to exported symbols, which flows into the generated `.d.ts` and the consumers' IDE hover. Tooling directives (`eslint-disable`, `@ts-expect-error`, …) are banned too: fix the underlying lint/type problem instead, and put negative type-safety cases in compiler-asserted fixtures. DI scope is carried by registration: `LifecycleScope.App`, `LifecycleScope.Session`, or `LifecycleScope.Agent`. A `workspace*` filename marks workspace-domain ownership, not a DI scope (see service-authoring.md).
71+
`packages/agent-core-v2/AGENTS.md` bans comments entirely: no file headers, no section banners, no statement-level narration, no JSDoc (not even on exported symbols) — the code is the source of truth. The only exception is a load-bearing lint-suppression directive (`oxlint-disable` / `eslint-disable`) for a deliberate pattern; other tooling directives (`@ts-expect-error`, …) are banned: fix the underlying lint/type problem instead, and put negative type-safety cases in compiler-asserted fixtures. DI scope is carried by registration: `LifecycleScope.App`, `LifecycleScope.Session`, or `LifecycleScope.Agent`. A `workspace*` filename marks workspace-domain ownership, not a DI scope (see service-authoring.md).
7272

7373
## Red lines (this stage)
7474

7575
- Import via the `#/...` alias (mapped to `src/`); never reach into another domain's internals by relative path.
7676
- Short-lived may inject long-lived; never the reverse.
77-
- No comments — not file headers, not beside statements; exported-symbol JSDoc is the only exception.
77+
- No comments — not file headers, not beside statements, not JSDoc; a load-bearing lint-suppression directive is the only exception.

.agents/skills/agent-core-dev/service-authoring.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ What belongs here:
164164
- **Helper classes / functions** used only by this impl (e.g. a built-in writer, an `extractError` helper) — co-located in the same file.
165165
- **Top-level `registerScopedService(...)`** — one per Service the file owns; importing the impl file runs the registration.
166166

167-
Base class: extend `Service` (from `#/_base/di/service`) when the unit needs capability calls on `this``provide` / `effect` / `on` / `get` / `ref` (e.g. contributing a record to a `collection` token). `Service` extends `Disposable`, so `_register` keeps working; constructor-time `provide` / `on` / `effect` calls are buffered and flushed by the kernel after construction, while `get` / `ref` throw inside the constructor (dependencies stay constructor parameters). Otherwise extend `Disposable` — both are full DI units; a service whose own members collide with the `Service` vocabulary (`name` / `state` / `config` / `get`) must stay on `Disposable` (leave a NOTE comment saying so).
167+
Base class: extend `Service` (from `#/_base/di/service`) when the unit needs capability calls on `this``provide` / `effect` / `on` / `get` / `ref` (e.g. contributing a record to a `collection` token). `Service` extends `Disposable`, so `_register` keeps working; constructor-time `provide` / `on` / `effect` calls are buffered and flushed by the kernel after construction, while `get` / `ref` throw inside the constructor (dependencies stay constructor parameters). Otherwise extend `Disposable` — both are full DI units; a service whose own members collide with the `Service` vocabulary (`name` / `state` / `config` / `get`) must stay on `Disposable`.
168168

169169
## Constructor conventions
170170

@@ -296,9 +296,8 @@ Importing the package therefore fires every `register*` side effect, exactly as
296296

297297
## Comments
298298

299-
- **No comments** (orient.md): no file headers, no statement-level narration; the only exception is JSDoc attached to exported symbols.
299+
- **No comments** (orient.md): no file headers, no statement-level narration, no JSDoc; the only exception is a load-bearing lint-suppression directive.
300300
- **Methods and fields carry no comments by default.** Well-named identifiers and types say *what*; the code is the source of truth for *how*.
301-
- Write an inline comment only when the *why* is non-obvious (a hidden constraint, a subtle invariant, a workaround). One short line.
302301
- For unimplemented stubs, throw `NotImplementedError('feature')` rather than `throw new Error('TODO: …')` (errors.md).
303302

304303
## Complete minimal example

.agents/skills/agent-core-dev/test.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Resolving by interface is what makes `registerScopedService(ISut, Sut, …)` par
2121

2222
Pure functions, value objects, and services with **no** `@IService` dependencies may be constructed directly.
2323

24-
The only other exception is a test that genuinely needs **two independent instances** of the same service with different dependencies (e.g. constructing two `TurnService`s with different `ILoopRunner`s). A singleton-per-container resolution cannot produce both, so `ix.createInstance(Impl)` is acceptable there — annotate it with a comment explaining why.
24+
The only other exception is a test that genuinely needs **two independent instances** of the same service with different dependencies (e.g. constructing two `TurnService`s with different `ILoopRunner`s). A singleton-per-container resolution cannot produce both, so `ix.createInstance(Impl)` is acceptable there — state the reason in the test name and local identifiers.
2525

2626
## Two harnesses
2727

.agents/skills/agent-core-dev/verify.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Walk the stages you touched and confirm:
2121
- **Design** — scope follows state identity; no `Map<sessionId, …>` at `App`; dependency arrows do not make a foundational layer know an upstream one; no cycle was routed around.
2222
- **Implement** — no `new` on `@IService`-carrying classes; `@IX` on constructor params only (service params after static params); interface + impl carry `_serviceBrand`; decorator names unique; coded errors only; flags for unreleased behavior.
2323
- **Test** — SUT resolved by interface; stubs under `test/`; scope tests re-register after `_clearScopedRegistryForTests()`; teardown through one `DisposableStore`.
24-
- **Files** — no comments (exported-symbol JSDoc excepted); registration runs from the impl file's top level; the new domain is exported from `src/index.ts`.
24+
- **Files** — no comments (no JSDoc either); registration runs from the impl file's top level; the new domain is exported from `src/index.ts`.
2525

2626
Then re-read the [global red lines](SKILL.md#global-red-lines) once — they catch most cross-stage mistakes in a single scan.
2727

.agents/skills/release/SKILL.md

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,46 +10,45 @@ mechanics and failure modes.
1010

1111
## Release model
1212

13-
- **Changesets, not tags.** Contributors land PRs with `.changeset/*.md` entries (authored via the
14-
tracked `gen-changesets` skill, `.agents/skills/gen-changesets/SKILL.md`). Versions and CHANGELOGs
15-
are machine-generated from those entries. Nobody edits a version number by hand.
16-
- **Two-phase CI flow** on every push to `main` (`.github/workflows/release.yml`):
17-
1. *Pending changesets exist* → changesets action runs `pnpm run version:release`
18-
(= `changeset version`) and opens/updates the **`ci: release packages`** PR.
19-
2. *That PR merges* → next run finds no pending changesets but bumped versions → publishes via
20-
`node scripts/release/changeset-publish-idempotent.mjs`, creates the GitHub Release at tag
21-
`@pythoughts/pythinker-code@<version>`, and fans out to downstream jobs.
22-
- **Publishing is CI-only** via npm Trusted Publishing (OIDC, `id-token: write`). The workflow
23-
deliberately sets **no `NPM_TOKEN`** — changesets prefers a token over OIDC when one is set, so
24-
adding it would silently downgrade publishing to a long-lived secret. Never "fix" a publish
25-
failure by adding NPM_TOKEN, and never run `changeset publish` locally.
26-
- The root `publish` script in `package.json` chains the full local gate
27-
(`typecheck → lint → sherif → test → build → lint:pkg → changeset publish`) — it exists for gate
28-
parity, not for actually publishing from a laptop.
13+
- **Changesets, not manual version edits.** Contributor PRs add `.changeset/*.md`. The changesets
14+
action creates or updates the `ci: release packages` PR. Merging that PR publishes the public npm
15+
package and creates `@pymodel/pythinker-code@<version>`.
16+
- **npm publishing is CI-only.** Trusted Publishing uses OIDC. Do not add `NPM_TOKEN`; a token takes
17+
precedence over OIDC. Do not run `changeset publish` locally.
18+
- **Private lanes use the push boundary.** `publishedPackages` only lists packages published to npm.
19+
Desktop and VS Code are private workspaces, so `detect-lane-bumps.mjs` compares their versions at
20+
`github.event.before` and `github.sha`.
21+
- **Desktop Stable and Beta are tag-driven.** The required `cut-desktop-tag` job creates
22+
`desktop-v<version>`, which starts `desktop-release.yml`; prerelease versions publish to the
23+
explicit Beta feed.
24+
- **Desktop Nightly is default-branch driven.** `nightly.yml` calls `desktop-release.yml` after each
25+
scheduled main build and publishes a signed Nightly prerelease only when the main commit changed.
26+
- **VS Code is isolated.** `vscode-release.yml` supports `workflow_call` and version-checked manual
27+
dispatch. Existing registry versions are skipped by the publisher scripts, so recovery is safe.
2928

3029
## What publishes
3130

32-
`.changeset/config.json` `ignore` list excludes almost every internal package
33-
(`agent-core`, `pyaos`, `kosong`, `server`, dashboards, web, …). Effective publishable set =
34-
non-private, non-ignored workspace packages — in practice **`@pythoughts/pythinker-code`** and the
35-
SDK-adjacent packages not on the ignore list. When adding a workspace package, decide its ignore/
36-
publish status explicitly, and remember `flake.nix` workspace lists must be updated by hand
37-
(root `AGENTS.md`).
31+
`@pymodel/pythinker-code` is the public npm package. Desktop and VS Code package files are private;
32+
their versions are release signals but changesets does not publish them to npm. When adding a
33+
workspace, set its `private` and changesets policy explicitly and update `flake.nix`.
3834

3935
## release.yml job map
4036

4137
| Job | Trigger | Notes |
4238
|---|---|---|
43-
| `Release` | every main push | install → build catalog → `pnpm build` → changesets action |
44-
| `Redeploy code.pythinker.com` | `packages_published == 'true'` | runs `scripts/release/verify-release-consistency.mjs`, then POSTs `DOKPLOY_CDN_DEPLOY_WEBHOOK` (skips with a warning if the secret is unset) |
45-
| `Update Homebrew tap` | published | `scripts/release/update-brew-formula.mjs` with `TAP_GITHUB_TOKEN` (skips if unset) |
46-
| `Deploy docs` | published | reusable `docs-deploy.yml` |
47-
| `Native release artifact` | `pythinker_native_release == 'true'` | reusable `_native-build.yml`, macOS signing/notarization secrets |
48-
| `Publish native release assets` | native release | `produce-manifest.mjs` then `gh release upload <tag> … --clobber` |
39+
| `Release` | every main push after CI + Nix | Detect lane versions, build, run changesets |
40+
| `Cut desktop release tag` | desktop version changed | Required and idempotent; App token makes the tag trigger the desktop workflow |
41+
| `Desktop Nightly` | scheduled main build | Reusable workflow; signed assets and the explicit Nightly update feed |
42+
| `Publish VS Code extension` | extension version changed | Reusable workflow; six VSIX targets, both registries, provenance |
43+
| `Native release artifact` | CLI was published | Six signed/tested zips, checksums, provenance |
44+
| `Publish native release assets` | native builds passed | All-or-nothing immutable upload with `manifest.json` |
45+
| `Redeploy CDN` + verify | native assets published | Webhook may retry; verification is the hard gate |
46+
| `Update Homebrew tap` | CLI was published | App token scoped to `homebrew-tap` contents |
47+
| `Release lane summary` | always | One table with provenance state; fails when an expected enabled lane failed or skipped |
4948

50-
`pythinker_native_release` and the release tag come from
51-
`apps/pythinker-code/scripts/native/resolve-release.mjs`, driven by the changesets action's
52-
`publishedPackages` output; the tag format is `@pythoughts/pythinker-code@<version>`.
49+
Set `RELEASE_LANE_DESKTOP`, `RELEASE_LANE_VSCODE`, `RELEASE_LANE_CDN`, or
50+
`RELEASE_LANE_BREW` to exactly `disabled` for a conscious temporary opt-out. Missing credentials are
51+
otherwise errors.
5352

5453
## Failure modes and known lessons
5554

@@ -61,28 +60,42 @@ publish status explicitly, and remember `flake.nix` workspace lists must be upda
6160
genuinely half-published release — read the log; do not blind-rerun.
6261
- **Version PR looks wrong.** Never patch the `changeset-release/main` branch by hand. Fix or add
6362
changesets on `main`; the next workflow run regenerates the PR.
64-
- **Native builder fails after npm publish succeeded.** npm state is final; native jobs are
65-
re-runnable against the same workflow run (`gh run rerun <id> --failed`). `--clobber` on asset
66-
upload makes re-runs safe.
63+
- **Beta or Nightly checks Stable.** GitHub does not infer update channels. Confirm the release is a
64+
prerelease and contains `beta*.yml` or `nightly*.yml`; do not rename Stable manifests.
65+
- **Native builder fails after npm publish succeeded.** npm state is final. Re-run failed jobs from
66+
the same run before any assets upload. A complete asset set is an idempotent no-op. A partial set
67+
must not be filled from a rebuild; keep it or publish a new patch version.
6768
- **CDN not updated after publish.** `verify-release-consistency.mjs` gates the webhook: local
6869
`apps/pythinker-code/package.json` version must equal the npm `latest` dist-tag (plus sane
6970
`beta`/`dev` tags). A mismatch means the checkout in the job predates the release commit or npm
70-
propagation lag — check `npm view @pythoughts/pythinker-code dist-tags` before touching anything.
71+
propagation lag — check `npm view @pymodel/pythinker-code dist-tags` before touching anything.
7172
Dokploy deploy specifics: see memory `cdn-dokploy-deploy-pipeline`.
7273
- **`pnpm install` fails in CI or locally.** `engine-strict=true` + Node `>=24.15.0` — check
7374
`.nvmrc` before debugging anything else.
7475
- **Pre-push hook** (`scripts/pre-push.sh` via simple-git-hooks) gates local pushes; a hook failure
7576
is a real gate failure — fix the cause, never `--no-verify`.
7677

78+
## Recovery
79+
80+
| Symptom | Command | Safety |
81+
|---|---|---|
82+
| Desktop tag job failed | `git tag desktop-v<VERSION> <RELEASE_SHA> && git push origin desktop-v<VERSION>` | Confirm the tag does not exist first; pushing it starts a public release workflow |
83+
| VS Code lane partially failed | `gh workflow run vscode-release.yml --ref <RELEASE_SHA> -f expected-version=<VERSION>` | Version is checked; both publishers skip versions already present |
84+
| Native matrix failed before upload | `gh run rerun <RUN_ID> --failed` | Reuses the same run and commit; do not mix a rebuilt partial asset set |
85+
| CDN is stale | Re-run the failed `Redeploy CDN` or verification job | Do not republish npm; nightly reconciliation remains red until aligned |
86+
| Unknown lane drift | `pnpm release:status` | Read-only; queries npm, GitHub Releases, CDN, Marketplace, and Open VSX |
87+
7788
## Verification commands
7889

7990
```bash
8091
gh run list --workflow=release.yml --branch=main -L 3 # workflow health
92+
gh run list --workflow=nightly.yml --branch=main -L 3 # Nightly desktop health
8193
gh pr list --search 'ci: release packages in:title' --state open
82-
npm view @pythoughts/pythinker-code version # published version
83-
npm view @pythoughts/pythinker-code dist-tags --json
84-
node scripts/release/verify-release-consistency.mjs # local == npm latest
85-
gh release view "@pythoughts/pythinker-code@<version>" # assets + manifest.json
94+
pnpm release:status # all live lanes
95+
npm view @pymodel/pythinker-code dist-tags --json
96+
node scripts/release/verify-release-consistency.mjs
97+
gh release view "@pymodel/pythinker-code@<version>"
98+
gh attestation verify <artifact> -R PyModel/pythinker-code
8699
```
87100

88101
## Hard rules (mirror tracked contracts)

.agents/skills/write-tui/DESIGN.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,13 @@
8383
Select a model (type to search)
8484
Tab toggle provider · ↑↓ navigate · Enter select · Esc cancel ← hint 首项即 Tab 切换
8585
← 空行
86-
All Pythinker Code openai ← tab 条:激活项填充背景(primary 底 + text 字 + bold),其余 textMuted
86+
All Pythinker Code openai ← tab bar: active uses selectionBg + inverseText + bold; others use textMuted
8787
← 空行
8888
❯ ...
8989
```
9090

9191
- tab 条位置:**在 hint 行下方**,且**上下各一空行**(与 hint、与列表都隔开)。
92-
- 激活 tab`chalk.bgHex(colors.primary).hex(colors.text).bold(\` ${label} \`)`;非激活:`chalk.hex(colors.textMuted)`。两者可见宽度一致,切换不抖动。
92+
- Active tab: `chalk.bgHex(colors.selectionBg).hex(colors.inverseText).bold(\` ${label} \`)`; inactive tab: `chalk.hex(colors.textMuted)`. Both have the same visible width.
9393
- 第一个 tab 恒为 `All`(聚合所有 provider);**默认停在 `All`**。仅当显式传 `initialTabId`(如 `/provider` 新增完跳转)才停在指定 provider tab。
9494
- `Tab` / `Shift+Tab` 循环切换;hint 行首项即 `Tab toggle provider`
9595
- 当前模型在所在 tab 内仍以 `` + ` ← current` 标记,切 tab 不丢失定位。

.changeset/add-discussion-mode.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": minor
3+
---
4+
5+
Add experimental Expert Talk for automatic two-model analysis, reciprocal review, and fused answers.

.changeset/calm-doctors-check.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Make `pythinker doctor` validate `config.toml` with the current schema in every engine mode.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Use a stable dot in the tab title while the agent is running.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pymodel/pythinker-code": patch
3+
---
4+
5+
Keep task output previews responsive for large logs and valid at UTF-8 byte boundaries.

0 commit comments

Comments
 (0)