Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
46ea01e
chore(repo): remove graphify-specific exclusions
elkaix Jul 11, 2026
696b3a2
docs(architecture): define agent core deepening phases
elkaix Jul 11, 2026
7dfbaf9
docs(core): plan agent core deepening
elkaix Jul 11, 2026
6033c24
test(core): characterize agent request contracts
elkaix Jul 11, 2026
6c15a29
test(core): use public seams in request contracts
elkaix Jul 11, 2026
653dcb1
docs(tasks): mark core characterization complete
elkaix Jul 11, 2026
c86f252
feat(skills): add exhaustive skill catalogue
elkaix Jul 11, 2026
7826722
fix(skills): harden catalogue seam
elkaix Jul 11, 2026
0f84868
test(skills): cover reversed catalogue roots
elkaix Jul 11, 2026
fc7c612
feat(skills): bound provider-visible discovery
elkaix Jul 11, 2026
e4da0bd
fix(skills): harden bounded discovery
elkaix Jul 11, 2026
3f27af6
fix(skills): isolate catalogue query results
elkaix Jul 11, 2026
2681403
docs(tasks): mark skill catalogue complete
elkaix Jul 11, 2026
2a4585b
feat(soul): add observable request assembler
elkaix Jul 11, 2026
1786311
fix(soul): harden request assembly policy
elkaix Jul 11, 2026
22c0723
fix(soul): preserve empty request fragments
elkaix Jul 11, 2026
e0650d8
feat(soul): route agent turns through request assembly
elkaix Jul 11, 2026
c741c51
fix(soul): harden request source lifecycle
elkaix Jul 11, 2026
03f8686
fix(soul): close request lifecycle integrity gaps
elkaix Jul 11, 2026
a54a62e
feat(context): make incremental writes disk first
elkaix Jul 11, 2026
9f38294
fix(context): settle incremental persistence commits
elkaix Jul 11, 2026
ccf11f9
fix(context): preserve cancellation after commit failure
elkaix Jul 11, 2026
9254122
feat(soul): expose sanitized prompt manifests
elkaix Jul 11, 2026
8ca57de
fix(soul): anonymize prompt manifest identifiers
elkaix Jul 11, 2026
9711a80
docs(tasks): record phase 3 completion
elkaix Jul 11, 2026
6962158
feat(context): replace history atomically
elkaix Jul 11, 2026
a74c2bc
fix(context): close replacement integrity gaps
elkaix Jul 11, 2026
c407846
feat(context): migrate history rewrites to transactions
elkaix Jul 11, 2026
398452f
fix(context): preserve replacement lifecycle integrity
elkaix Jul 11, 2026
c32d6fb
fix(context): attribute lifecycle to committed operation
elkaix Jul 11, 2026
13fa500
docs(tasks): record phase 4 completion
elkaix Jul 11, 2026
ded5d7e
feat(agents): resolve definitions through a catalogue
elkaix Jul 11, 2026
3116485
fix(agents): harden catalogue source handling
elkaix Jul 11, 2026
013479b
fix(agents): preserve safe unknown field warnings
elkaix Jul 11, 2026
6468f73
feat(agents): publish resolved agent catalogue
elkaix Jul 11, 2026
53d2d2d
fix(agents): isolate catalogue compatibility projection
elkaix Jul 11, 2026
6bea07f
docs(tasks): record phase 5 completion
elkaix Jul 11, 2026
9d25842
test(toolset): add characterization harness
elkaix Jul 11, 2026
26bb239
test(toolset): characterize execution and lifecycle
elkaix Jul 11, 2026
970682b
fix(toolset): derive characterization decisions
elkaix Jul 11, 2026
a835380
test(core): use real compatibility state
elkaix Jul 11, 2026
476eac4
docs(core): record agent deepening release
elkaix Jul 11, 2026
be86772
fix(context): bound revert conflicts
elkaix Jul 11, 2026
a20bfda
docs(tui): design clean thinking activity rendering
elkaix Jul 11, 2026
3505c47
fix(tui): render thinking preview markdown
elkaix Jul 11, 2026
7bfc751
fix(tui): bound thinking-preview comment regex
elkaix Jul 11, 2026
4a54d4a
fix(tui): keep activity rows visually stable
elkaix Jul 11, 2026
be252db
docs(tui): record clean activity rendering
elkaix Jul 11, 2026
2735151
docs(tasks): record TUI rendering verification
elkaix Jul 11, 2026
d342166
docs(changelog): sync unreleased TUI entry
elkaix Jul 11, 2026
324a296
test(e2e): stabilize cancel-recovery wait under load
elkaix Jul 11, 2026
b9e9b90
fix(agentspec): reject escaping references and surface parser errors
elkaix Jul 11, 2026
314f3cb
Merge remote-tracking branch 'origin/main' into feat/agent-core-deepe…
elkaix Jul 11, 2026
bd65e37
fix(docs): exclude superpowers working docs from VitePress build
elkaix Jul 11, 2026
55c1552
fix(soul): harden agent-core seams from PR review
elkaix Jul 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .coderabbit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ reviews:
path_filters:
- "!dist/**"
- "!build/**"
- "!graphify-out/**"
- "!**/*.pyc"
- "!**/__pycache__/**"
# Prose and working-notes paths: keeps bot reviews focused on shipped
Expand Down
5 changes: 0 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,9 @@ src/pythinker_code/dashboard/static/

# Generated reports
.firecrawl/
# Graphify generated graph, cache, wiki, and Obsidian vault outputs
graphify-out/
graphify-out*/
src/pythinker_code/.understand-anything/
# understand-anything knowledge graph (any location)
.understand-anything/
.graphify_*.json
.graphify_*.txt
tests_ai/report.json
tests_ai/terminal_bench_2_cache/
jobs/
Expand Down
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,48 @@ GitHub Releases page; `0.8.0` is the new starting line.

## Unreleased

- **Agent-spec loading is more defensive and truthful.** Subagent `path`, `extend`, and
`system_prompt_path` references that resolve outside their spec's directory (or the built-in
agents directory) are now rejected instead of loaded, and the markdown agent catalogue no longer
reclassifies an unexpected parser error as a harmless "invalid field" skip — only genuinely
malformed frontmatter is skipped.
- **Thinking and subagent activity now render cleanly in the terminal.** Live reasoning previews
render complete Markdown without exposing top-level HTML comments, activity-tree rows remain
visually stable, and the coral shimmer is reserved for the active verb spinner.
- **Agent request compatibility is now executable and reviewable.** Provider handoff,
prompt ordering, persisted-versus-effective history, context JSONL restoration,
agent projections, and Toolset lifecycle behavior now have explicit compatibility
contracts guarding future agent-core changes.
- **Skill discovery is bounded without making skills unreachable.** Pythinker now
searches one deterministic `SkillCatalog`, keeps exhaustive exact-name resolution,
and sends only task-relevant candidates to the model within an 8,000-character
request budget. The exhaustive `Runtime.skills` mapping remains available during
the compatibility window.
- **Agent requests now have one observable assembly path.** Required guidance fails
closed, optional guidance reports sanitized degradation outcomes, and the new
`/prompt-manifest` command explains the latest request composition without storing
raw prompts, user text, or provenance paths.
- **Conversation history updates are transactional.** Normal appends persist before
changing memory, while compaction, pruning, revert, and clear flows use atomic
replacement with coherent cancellation and rollback behavior. Concurrent revert
conflicts now stop after a bounded retry budget instead of starving indefinitely.
Existing JSONL records and restoration behavior remain compatible.
- **Agent definitions now resolve through one source-aware catalogue.** YAML and
Markdown definitions share deterministic precedence, collision diagnostics, and
safe provenance handling. Unknown fields warn in this release, become errors in
the following minor release, and the `LaborMarket`, `AgentTypeDefinition`, and
generated-wrapper adapters remain through that strict-default release.
- **Tool execution and MCP lifecycle behavior now have deterministic fault coverage.**
Publication rebuilds preserve the previous MCP tool registry if registration
fails. Characterization crossed the execution-overhead threshold, but a controlled
private extraction measured slightly worse and was reverted, so
`PythinkerToolset` remains the implementation boundary.
- **Agent-core seams hardened from review.** Persisted usage/checkpoint records reject
boolean and negative token counts, `update_token_count` validates at the boundary, a
temporary system-prompt descriptor is closed if `fdopen` fails, request finalization
surfaces every provider acknowledgement failure, a failed skill projection is always
recorded as failed (never blurred to not-applicable), and request-assembly telemetry no
longer emits unbounded per-request token values as metric attributes.

## 0.57.0 (2026-07-05)

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Read both, in order:

1. **`AGENTS.md`** — non-negotiable repository rules. Always applies.
2. **`AGENTS.local`** — machine-specific / private local instructions (gitignored). Read it after
`AGENTS.md`. It may add workflow detail (e.g. the code-graph / graphify workflow) but must not
`AGENTS.md`. It may add workflow detail (e.g. a local code-graph workflow) but must not
weaken or override the rules in `AGENTS.md`.

@AGENTS.md
Expand Down
5 changes: 5 additions & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ export default withMermaid(defineConfig({
title: 'Pythinker Code Docs',
description: 'Pythinker Code Documentation',

// Internal superpowers working docs (plans/specs/reports) are gitignored and
// never linked in the published site's nav; exclude them from VitePress's
// page auto-discovery so their Markdown is not compiled by the Vue SFC parser.
srcExclude: ['**/superpowers/**'],

locales: {
en: {
label: 'English',
Expand Down
138 changes: 138 additions & 0 deletions docs/en/contributing/toolset-characterization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Toolset characterization

The Toolset characterization harness is a directional, local engineering aid. It uses
deterministic no-op tools and fake MCP-style inventories to make Pythinker's framework costs
visible without network access, credentials, hosted telemetry, or new runtime dependencies. Its
numbers describe the machine and checkout that produced them; they are not universal product
performance claims.

Task 12 establishes the schema, evaluator, and measurement harness. It deliberately makes no
extraction decision. Task 13 runs the full fault matrix and real five-run measurements before any
private `_ToolExecutionPipeline`, `_McpLifecycle`, or `_ToolRegistry` extraction may be considered.

## Run it

From the repository root:

```bash
uv run python scripts/benchmark_toolset.py --scenario all --runs 5 \
--output toolset-characterization.json
```

Use `--scenario execution`, `dedupe`, `advertisement`, or `mcp` to select one family. The default is
`all`. `--smoke` selects the smallest fixture in each chosen family and is intended only to validate
the runner and schema:

```bash
uv run python scripts/benchmark_toolset.py --scenario all --runs 1 --smoke
```

Without `--output`, the report is written as JSON to standard output. A non-positive run count, an
unknown scenario, or an unwritable output path exits nonzero.

## Fixtures and intervals

Fixture construction and warm-up are outside every named measured interval. All clocks use
`time.monotonic_ns()`.

- Execution runs parallel-safe and exclusive calls at concurrency 1, 10, and 100 through the public
`PythinkerToolset` facade. For `execution_mixed`, fixture `size` is a reader/writer pair count:
sizes 1, 10, and 100 therefore use concurrency and expected operation counts 2, 20, and 200.
`end_to_end` starts immediately before `handle` dispatch and ends after all returned results
settle. `tool_call` records absolute entry/exit intervals inside
deterministic no-op tools; overlapping intervals are merged before their critical-path duration
is subtracted from `end_to_end` for `framework_overhead`. Each call's read/write gate wait begins
immediately before requesting the shared or exclusive context and ends immediately after
admission, before `tool.call`. `read_write_gate_wait` is the union of those absolute wait
intervals, so overlapping queued calls count once and remain directly comparable with
`end_to_end` for the 25 percent gate. Every mixed scale uses an event-held reader from the first
pair and queues its exclusive writer before releasing it; the remaining pairs are dispatched in
call order behind that barrier, making contention deterministic without sleeps. The remaining
named lifecycle subphases are present with `measurement_status: unmeasured`
and an explanation because current Toolset exposes no stable boundary that would isolate them
without changing production behavior.
- Dedupe dispatches two same-step calls with identical 1 KiB, 100 KiB, or 1 MiB payloads. It uses
the same execution intervals and keeps payload construction outside the measured region.
- Advertisement projects 50, 500, and 5,000 real built-in, `PluginTool`, and `MCPTool` categories.
It records visibility policy enabled/disabled with hidden/unhidden entries, four aggregate reads
plus twenty individually timed repeated reads without a registry change, and a rebuild after
deterministic MCP publication. Each measured run records all twenty raw projection samples and
their nearest-rank p95; the registry repeatability decision uses the five within-run p95 values,
never a single projection or the 5,000-tool stress result. Setup remains
outside every named projection interval. Category and projection counts make the fixture behavior
independently checkable; the registry hash length-prefixes both policy projections in order.
- MCP runs the current background `load_mcp_tools`/`wait_for_mcp_tools`/`cleanup` lifecycle for 1,
10, and 50 configured servers, replacing network connection with a deterministic local inventory
adapter. `mcp_lifecycle` starts immediately before background loading and ends when
`wait_for_mcp_tools` settles after final registry publication. `time_to_first_inventory` ends when
the inventory is visible through the public Toolset registry (the local all-at-once publication
can make first and settled effectively the same boundary), and the report records the visible
count at that exact publication boundary; `time_to_settled_inventory` ends after
`wait_for_mcp_tools` returns; and `cleanup` covers `PythinkerToolset.cleanup()` entry through
return. `startup_to_ready` begins immediately before the actual `Runtime.create` call and ends at
that same settled-inventory point.

Concurrency and cancellation coordination use asyncio events and task completion, never timing
sleeps. The harness reports cancellation completion and post-run task leakage. Its fake adapters do
not start processes or sessions, so those leak counts remain explicit zeros. Cancellation probes
enter real queued reader and writer states through `PythinkerToolset.handle`, cancel them, then prove
that a later call recovers. Task 13 exercises the full failure matrix.

## Repeatability and reruns

Threshold decisions use exactly five isolated measured runs after warm-up. A threshold is crossed
only when at least four of five values exceed it and the median also exceeds it. No crossings means
uncrossed. Two or three crossings also remain uncrossed because the repeatability rule did not pass.
A single crossing is treated as an outlier and therefore inconclusive; it requests one complete
five-run rerun. The rerun replaces the inconclusive set for the decision, and a second rerun is never
requested.

The pure evaluator records both primary and rerun values, the selected crossing count and median,
the final `crossed`, `uncrossed`, or `inconclusive` state, and whether a rerun is still required.

## Extraction thresholds

Task 13 applies these approved gates with the repeatability rule:

- Execution pipeline: non-tool framework overhead exceeds 10 percent of p95 latency for short
in-process tools, or three independent recent changes repeatedly touch the same lifecycle region
and extraction deletes that shared state.
- MCP lifecycle: the defined lifecycle interval exceeds 20 percent of startup-to-ready with ten
servers; cleanup exceeds six seconds; a task, process, session, or publication leak is reproduced;
or three modules directly require MCP lifecycle state.
- Registry: advertisement exceeds 5 ms p95 at 500 tools, or recurring collision/rebuild/visibility
defects would be eliminated by one owner. The 5,000-tool stress fixture cannot trigger extraction
by itself.
- Read/write gate: gate wait exceeds 25 percent of end-to-end p95 in a realistic mixed workload, or
a second real consumer appears.

No threshold crossing, file length alone, duplicate old/new state, a new public interface, a
callback cycle, or a red characterization/cancellation test mandates no extraction.

## JSON schema

The versioned report contains:

- `environment`: Python version and implementation plus platform information;
- `scenarios[].fixture`: scenario kind, size, concurrency, payload size, and composition; mixed
execution explicitly reports `composition: reader/writer pairs`;
- `warmups` and `iterations`;
- `phases`: measured/unmeasured status, raw nanosecond samples, optional raw within-run sample
groups, median, nearest-rank p95, throughput where meaningful, and an explanation for unavailable
boundaries;
- deterministic `registry_hash`;
- `allocation_peak_bytes` and `retained_object_delta` from `tracemalloc`;
- `cancellation`: completion status and completion duration;
- `leaks`: pending task, process, and session counts;
- `task_count_peak`: the maximum observed asyncio task count during a measured sample;
- `operation_count`, `category_counts`, and `projection_counts`: independently checkable fixture
execution and advertisement behavior;
- `lifecycle_status`: `completed` for local execution/registry fixtures or `settled` after the fake
MCP inventory is fully published; and
- `decisions`: threshold, five primary values, optional five-value rerun, crossing count, median,
primary/rerun/final states, and rerun-required state.

Partial, smoke, and non-five-run reports keep an empty `decisions` array. The documented full
`--scenario all --runs 5` command deterministically derives all seven Task 13 decisions from the raw
scenario samples and writes them in the same report. A human-readable crossed/uncrossed/inconclusive
decision record remains a Task 13 deliverable after deterministic fault tests are complete.
15 changes: 15 additions & 0 deletions docs/en/customization/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,21 @@ Recognized frontmatter fields are `name`, `description`, `tools`, `model`, and
warning. Discovered markdown agents appear as `Agent` tool subagent types alongside the
built-in types.

### Agent definition validation rollout

Pythinker currently resolves YAML and repository markdown definitions into one agent catalogue.
Unknown definition fields are accepted with one aggregated startup warning per source in this
release. Warnings identify the field path but do not include field values, prompt content, or raw
absolute source paths.

The production policy will reject unknown fields in the immediately following minor release.
Fix warnings before upgrading: required YAML definitions will then fail to load, while an invalid
optional markdown definition will be skipped with a diagnostic. The existing `LaborMarket`
compatibility interface and generated markdown YAML wrappers will remain for that strict-default
release. Their earliest removal is one additional minor release later, and only after direct
catalogue launch has equivalent prompt, model, tool-policy, required-MCP, foreground, and
background behavior.

## Custom agent files

Agents are defined in YAML format. Load a custom agent with the `--agent-file` flag:
Expand Down
7 changes: 7 additions & 0 deletions docs/en/reference/slash-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ Aliases: `/report`

List the available subagent types, showing each agent's name, when to use it, its default model, and its tool posture.

### `/prompt-manifest`

Show the latest request-assembly status for the current session. The output includes opaque,
stable fragment identifiers, admission outcomes, and token estimates. It never includes prompt
content, user content, raw source names, raw file paths, credentials, or stack traces. Before the
first assembled request, the command reports that no manifest is available.

## Account and configuration

### `/login`
Expand Down
39 changes: 39 additions & 0 deletions docs/en/release-notes/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,48 @@ GitHub Releases page; `0.8.0` is the new starting line.

## Unreleased

- **Agent-spec loading is more defensive and truthful.** Subagent `path`, `extend`, and
`system_prompt_path` references that resolve outside their spec's directory (or the built-in
agents directory) are now rejected instead of loaded, and the markdown agent catalogue no longer
reclassifies an unexpected parser error as a harmless "invalid field" skip — only genuinely
malformed frontmatter is skipped.
- **Thinking and subagent activity now render cleanly in the terminal.** Live reasoning previews
render complete Markdown without exposing top-level HTML comments, activity-tree rows remain
visually stable, and the coral shimmer is reserved for the active verb spinner.
- **Agent request compatibility is now executable and reviewable.** Provider handoff,
prompt ordering, persisted-versus-effective history, context JSONL restoration,
agent projections, and Toolset lifecycle behavior now have explicit compatibility
contracts guarding future agent-core changes.
- **Skill discovery is bounded without making skills unreachable.** Pythinker now
searches one deterministic `SkillCatalog`, keeps exhaustive exact-name resolution,
and sends only task-relevant candidates to the model within an 8,000-character
request budget. The exhaustive `Runtime.skills` mapping remains available during
the compatibility window.
- **Agent requests now have one observable assembly path.** Required guidance fails
closed, optional guidance reports sanitized degradation outcomes, and the new
`/prompt-manifest` command explains the latest request composition without storing
raw prompts, user text, or provenance paths.
- **Conversation history updates are transactional.** Normal appends persist before
changing memory, while compaction, pruning, revert, and clear flows use atomic
replacement with coherent cancellation and rollback behavior. Concurrent revert
conflicts now stop after a bounded retry budget instead of starving indefinitely.
Existing JSONL records and restoration behavior remain compatible.
- **Agent definitions now resolve through one source-aware catalogue.** YAML and
Markdown definitions share deterministic precedence, collision diagnostics, and
safe provenance handling. Unknown fields warn in this release, become errors in
the following minor release, and the `LaborMarket`, `AgentTypeDefinition`, and
generated-wrapper adapters remain through that strict-default release.
- **Tool execution and MCP lifecycle behavior now have deterministic fault coverage.**
Publication rebuilds preserve the previous MCP tool registry if registration
fails. Characterization crossed the execution-overhead threshold, but a controlled
private extraction measured slightly worse and was reverted, so
`PythinkerToolset` remains the implementation boundary.
- **Agent-core seams hardened from review.** Persisted usage/checkpoint records reject
boolean and negative token counts, `update_token_count` validates at the boundary, a
temporary system-prompt descriptor is closed if `fdopen` fails, request finalization
surfaces every provider acknowledgement failure, a failed skill projection is always
recorded as failed (never blurred to not-applicable), and request-assembly telemetry no
longer emits unbounded per-request token values as metric attributes.

## 0.57.0 (2026-07-05)

Expand Down
Loading
Loading