Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
84a7351
feat(scope): add durable scope organization
PsiACE Aug 27, 2026
3a4c196
feat(api): expose scope and binding capabilities
PsiACE Aug 27, 2026
0a07227
feat(codex): bind sessions to scopes
PsiACE Aug 27, 2026
0930209
feat(context): read direct scope references
PsiACE Aug 27, 2026
bc028a2
feat(artifact): publish exact revisions across scopes
PsiACE Aug 27, 2026
efedeea
feat(stats): aggregate scope selections
PsiACE Aug 27, 2026
a14df2d
fix(scope): bound external reference index keys
PsiACE Aug 27, 2026
76817f9
feat(report): project handoffs by scope selection
PsiACE Aug 27, 2026
29c39cb
feat(codex): constrain report reads to session scope
PsiACE Aug 27, 2026
fbdace6
fix(dsh): bind observation requests to scope selections
PsiACE Aug 27, 2026
f3f3001
docs: describe scope-based observation views
PsiACE Aug 27, 2026
4622c01
test(scope): align observability fixtures
PsiACE Aug 27, 2026
b3e5f73
Merge remote-tracking branch 'origin/master' into codex/scope-organiz…
PsiACE Aug 27, 2026
3624d9c
Merge remote-tracking branch 'origin/master' into codex/scope-organiz…
PsiACE Aug 30, 2026
43b4e5e
refactor(scope): remove project and workstream semantics
PsiACE Aug 30, 2026
3eeb0d5
chore: complete license headers
PsiACE Aug 30, 2026
2e54977
fix(pi): bind observation commands to current scope
PsiACE Aug 30, 2026
a494985
Merge remote-tracking branch 'origin/master' into codex/scope-organiz…
PsiACE Sep 1, 2026
2b4db7b
feat(stats): retain per-scope aggregation details
PsiACE Sep 1, 2026
a65e454
feat(handoff): preserve cross-scope provenance
PsiACE Sep 1, 2026
f3fc52b
feat(integrations): resolve framework scopes through server
PsiACE Sep 1, 2026
38eb29b
feat(plugins): bind agent hosts to server scopes
PsiACE Sep 1, 2026
65b8085
docs(scope): explain server-owned integration bindings
PsiACE Sep 1, 2026
8eceeb6
Merge remote-tracking branch 'origin/master' into codex/scope-organiz…
PsiACE Sep 1, 2026
acb1043
fix(e2e): provision server-owned scopes
PsiACE Sep 1, 2026
01de1ca
refactor(api): expose scopes as REST resources
PsiACE Sep 1, 2026
7e4c7a1
test: focus scope coverage on observable behavior
PsiACE Sep 1, 2026
d89ea58
refactor(dsh): remove duplicate API generation
PsiACE Sep 1, 2026
3dccafe
fix(scope): harden hierarchy and integration boundaries
PsiACE Sep 1, 2026
4de90be
chore(dsh): restore lockfile license header
PsiACE Sep 1, 2026
eb0ca26
fix(scope): avoid database-specific savepoints
PsiACE Sep 1, 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
18 changes: 9 additions & 9 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ POWERCONTEXT_SERVER_AUTH_ENABLED=false
# POWERCONTEXT_SERVER_AUTH_TOKEN=replace-me

# Dashboard -------------------------------------------------------------------
# Every Coding Agent below uses this same Scope ID.
# The Dashboard discovers Scopes from the Server. The Server creates a default Scope on first startup.
POWERCONTEXT_SERVER_DASHBOARD_ENABLED=true
POWERCONTEXT_SERVER_DASHBOARD_SCOPES='[{"scope_id":"project:quickstart","display_name":"Quick Start"}]'

# Logging, metrics, and tracing -----------------------------------------------
POWERCONTEXT_SERVER_LOGGING_LEVEL=INFO
Expand Down Expand Up @@ -110,13 +109,14 @@ POWERCONTEXT_CLIENT_TIMEOUT=10
# POWERCONTEXT_CLIENT_API_TOKEN=replace-me

# Coding Agent integrations ---------------------------------------------------
# Keep these values equal to the Dashboard scope above. Load .env before starting the selected Agent.
POWERCONTEXT_CODEX_SCOPE_ID=project:quickstart
POWERCONTEXT_CLAUDE_SCOPE_ID=project:quickstart
POWERCONTEXT_DSH_SCOPE_ID=project:quickstart
POWERCONTEXT_OPENCODE_SCOPE_ID=project:quickstart
POWERCONTEXT_PI_SCOPE_ID=project:quickstart
POWERCONTEXT_LANGGRAPH_SCOPE_ID=project:quickstart
# Load .env before starting the selected Agent. Codex binds each Session through the Scope service.
# Set an integration's SCOPE_ID only to select an existing Scope explicitly.
# POWERCONTEXT_CODEX_SCOPE_ID=scp_existing
# POWERCONTEXT_CLAUDE_SCOPE_ID=scp_existing
# POWERCONTEXT_DSH_SCOPE_ID=scp_existing
# POWERCONTEXT_OPENCODE_SCOPE_ID=scp_existing
# POWERCONTEXT_PI_SCOPE_ID=scp_existing
# POWERCONTEXT_LANGGRAPH_SCOPE_ID=scp_existing

# Local Server endpoints used by integrations.
POWERCONTEXT_CLAUDE_SERVER_URL=http://127.0.0.1:8000
Expand Down
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ js-test: ## Install, build, and test the DeepSeek Harness plugin.
@pnpm --dir integrations/dsh/plugins/powercontext test
@pnpm --dir integrations/dsh/plugins/powercontext build
@git diff --exit-code -- \
integrations/dsh/plugins/powercontext/openapi/powercontext.yaml \
integrations/dsh/plugins/powercontext/src/operations.generated.ts \
integrations/dsh/plugins/powercontext/lib
@pnpm --dir integrations/dsh/plugins/powercontext test
Expand Down
36 changes: 19 additions & 17 deletions docs/en/development/server-web-ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,22 @@ versioned prefixes.

## Understand Dashboard data

The browser authenticates against `/dashboard/scopes`, then requests `/v1/stats` with the selected `scope_id` and a
`30d` period. The Server reads one scoped snapshot and returns inventory, model usage, and recall statistics.
The browser authenticates against `/dashboard/scopes`, builds the shared Scope selector, then posts the selected
`ScopeSelection` and period to `/v1/stats`. The selector exposes three observation views: `all`, one root's `subtree`,
or one `exact` Scope. A Parent relation organizes the selector; it does not make parent data visible to a child.

| Dashboard value | Source |
| --- | --- |
| Sources | Current scoped Source journal position |
| Memory entries | Entries in the current Memory Artifact |
| Artifacts | Current Artifact heads grouped by family |
| Pending review | Current Candidate heads grouped by family and status |
| Sources | Selected Scopes' Source journal positions |
| Memory entries | Entries in the selected Scopes' Memory Artifacts |
| Artifacts | Selected Scopes' Artifact heads grouped by family |
| Pending review | Selected Scopes' Candidate heads grouped by family and status |
| Model usage | Persisted daily generation and embedding usage |
| Recall hits, token reduction, and savings trend | Persisted daily recall measurements for the configured estimator |

The Runtime performs these reads in one database transaction and calculates totals, pending Sources, family counts,
daily buckets, and token reduction on the Server. The browser presents `ready_preparations` as recall hits and plots the
The Runtime resolves the selection to exact Scope IDs, aggregates totals, pending Sources, family counts, daily buckets,
and token reduction on the Server, and returns both the selection and resolved IDs. The browser presents
`ready_preparations` as recall hits and plots the
signed daily `token_reduction` as the savings trend. Each heatmap cell combines those two fields for its date. Its fixed
bands are no hit, hit without a positive reduction, 1–255, 256–1023, and 1024 or more estimated tokens reduced. The
fixed thresholds keep sparse activity and outliers from changing the meaning of every other cell.
Expand All @@ -101,17 +103,17 @@ or rendering contract only after a second page needs the same behavior.

## Add the Handoff Report page

When Handoff Report is enabled, the Server hosts the scope Handoff page at `/handoff-reports` without requiring the scoped-statistics Dashboard or its configured scope list. The optional Dashboard remains at `/` when separately enabled. The pages share only `base.html`, the header and footer, `auth.js`, theme state, and locale state; their statistics and report calculations remain independent.
When Handoff Report is enabled, the Server hosts a read-only report page at `/handoff-reports`; the Dashboard remains
optional. Both pages load Scopes from `/dashboard/scopes` and use `scope-selection.js` to expose the same `all`,
`subtree`, and `exact` views.

The Handoff Report page obtains exact `scope_id` values with committed Handoffs from `POST /v1/handoff-reports/scopes/list-known` and uses them in a searchable scope combobox. Selecting a scope sends its required `scope_id` to `POST /v1/handoff-reports/get`; neither the Project catalog nor `project_id` participates in report selection. The page presents the exact current Handoff snapshot at full width.
The page posts the selected `ScopeSelection` to `/v1/handoff-reports/get`. The Server resolves it to exact Scope IDs
and projects each Scope's descriptor and latest exact Handoff. A Scope without a committed Handoff remains visible as
`no_handoff`. Parent does not infer Context sharing, and the report does not edit Handoff state.

The current snapshot displays objective, current state, disposition, next action, and known omissions as one Handoff document. One Edit action opens all five fields, and one Save Revision action prepares and commits the complete document as a new immutable Handoff Revision. Scope switching and background refresh pause while the editor is open. Receiver-side decisions are not part of this page; existing continuity records remain available in the read-only Continuity timeline. Apart from the explicit revision write, the browser formats returned `summary`, `coverage`, Workstream state, and digests without recalculating report semantics.

When known-scope discovery succeeds but no scope has a committed Handoff, the page replaces report controls with a clearly labeled, data-free template preview. Retry enumerates Handoff heads again; the first committed scope replaces the preview. The preview neither creates a Handoff nor requests fabricated report data.

The page requests the current day in UTC by default and provides current-day, ISO-week, calendar-month, and custom date-range inputs. The custom end date is inclusive in the UI and is converted to the exclusive start of the next day for the API. The current scope application normalizes this input but supplies no Activity events, reports `activity_coverage=not_configured`, and returns no period comparison. Handoff status comes from the current exact selection and must not be presented as a historical period-end state.

The overview request may disable evidence checks for lower latency. A Markdown download makes a separate request with `format=markdown`, `download=true`, and evidence checks enabled by default. The browser never reconstructs Markdown from rendered DOM or canonical JSON. Both background refresh and browser download currently require a stored bearer token even when Server authentication is disabled; initial and manual report loads still work without one. Disabling Handoff Report removes the `/handoff-reports` page and its API while leaving the original Dashboard route, scope selection, and statistics request unchanged.
JSON is the browser projection. Markdown download repeats the same selection with `format=markdown` and
`download=true`; the browser does not reconstruct Markdown from the rendered DOM. Disabling Handoff Report removes
the page and report API without changing Dashboard selection or statistics behavior.

## Preserve the security boundary

Expand Down
14 changes: 7 additions & 7 deletions docs/en/docs/explanation/core-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ Agent turn. Every value belongs to a scope.

## Scope is the isolation boundary

Every content operation uses a `scope_id`. The scope selects an isolated Source journal, Memory lifecycle, Candidate
inbox, Handoff history, and related runtime state. Integrations can derive a scope from a repository or path, while
public interfaces also allow callers to supply one explicitly.
Every content operation uses a `scope_id`. The Scope selects an isolated Source journal, Memory lifecycle, Candidate
inbox, Handoff history, and related runtime state. Scope IDs are opaque Server identifiers. Integrations resolve an
explicit Scope, a durable binding, or the Server default; repository, path, session, and Agent identities are binding
inputs rather than Scope IDs.

A scope ID selects data. It does not prove user identity, grant tool access, or authorize execution.
A Scope ID selects data. It does not prove user identity, grant tool access, or authorize execution.

## Sources preserve evidence

Expand Down Expand Up @@ -89,9 +90,8 @@ inspected boundary and returns a temporary Prepared Handoff. Committing a Handof
the user wants a milestone. The receiver resolves the Handoff and records an Acknowledgement; a Task Outcome preserves
the final status and checks as Source evidence.

The [Handoff Report](../how-to/use-handoff-report.md) projects current Handoff Revisions for inspection and export. The
current scope report does not yet include Activity events or period comparison, and it does not rewrite Memory or the
underlying Handoff history.
The [Handoff Report](../how-to/use-handoff-report.md) projects the latest Handoff Revision in each selected Scope for
inspection and export. It is read-only and does not rewrite Memory or the underlying Handoff history.

## Interfaces expose different parts of the same Server

Expand Down
23 changes: 11 additions & 12 deletions docs/en/docs/how-to/configure-claude-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ PowerContext Server data.

For each user prompt, the Hook:

1. derives the same project scope as the Codex integration;
1. resolves the current Scope from explicit, session, workspace, and default bindings;
2. calls `POST /v1/context/prepare` at most once;
3. strictly validates `powercontext.prepared-context.v1` and injects it unchanged through `additionalContext`;
4. independently captures the prompt as ordinary Content Source evidence.
Expand All @@ -73,20 +73,19 @@ Skill.
Scope resolution uses this order:

1. `POWERCONTEXT_CLAUDE_SCOPE_ID`, when explicitly set;
2. the Git-private Workstream binding shared with Codex;
3. the normalized `remote.origin.url` of the Git top-level directory;
4. a `local:sha256:<digest>` identifier derived from the resolved project directory.
2. a durable session binding stored by PowerContext;
3. a durable workspace binding stored by PowerContext;
4. the Server's default Scope.

Claude Code and Codex therefore resolve the exact same scope in a checkout that has a Workstream binding. Without a
binding they still share the normalized remote scope. Bind a known Workstream with the bundled resolver:
Bind a known Scope to the checkout with the bundled resolver:

```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/project_scope.py" \
--cwd "$PWD" --bind-workstream "WORKSTREAM_SCOPE_ID"
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/workspace_scope.py" \
--cwd "$PWD" --bind-scope "SCOPE_ID"
```

The local fallback is stable for one resolved directory, but it is not intended to join unrelated checkouts. Set an
explicit scope only when that separation or sharing is deliberate.
The resolver hashes the workspace path only as an external binding key. It never generates a Scope ID from a Git
remote or directory. Set an explicit Scope only when that separation or sharing is deliberate.

## Use explicit Memory and Handoff operations

Expand Down Expand Up @@ -122,8 +121,8 @@ export POWERCONTEXT_CLAUDE_CAPTURE_PROMPTS=false
claude
```

Use `POWERCONTEXT_CLAUDE_SCOPE_ID` only when the Memory scope must intentionally differ from both the Git remote and
local project path.
Use `POWERCONTEXT_CLAUDE_SCOPE_ID` only when the current work must intentionally override durable bindings and the
Server default.

`POWERCONTEXT_CLAUDE_FLUSH_ON_CAPTURE=true` makes the Hook wait for Source processing and is intended for tests, not
normal interactive use.
Expand Down
25 changes: 9 additions & 16 deletions docs/en/docs/how-to/configure-codex.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,20 @@ handoff this work
```

The `project-context` Skill treats that imperative as explicit authorization to create one durable Handoff milestone.
If the catalog contains multiple Workstreams, Codex first opens a native picker; one Workstream is selected
automatically when it is the only candidate. Codex binds the selected Workstream to the checkout, inspects the current
conversation and repository, assembles the objective, branch and worktree state, changed files, observed checks,
blockers, omissions, and next action, then calls `handoff_current_work` followed by `commit_handoff`. After a successful
commit, Codex reports the selected Workstream and exact Handoff Revision; the user does not need to fill in the Handoff
content or confirm the commit again.
Codex inspects the current conversation and repository, assembles the objective, branch and worktree state, changed
files, observed checks, blockers, omissions, and next action, then calls `handoff_current_work` followed by
`commit_handoff` in the current Session Scope. After a successful commit, Codex reports the exact Handoff Revision; the
user does not need to fill in the Handoff content or confirm the commit again.

`交接`, `交接当前工作`, and `commit a handoff` use the same behavior. To inspect the proposed content without writing,
ask to `preview the handoff without committing`; the Skill renders the proposed fields in chat and calls no write
tool. Discussing Handoff design or asking how it works does not authorize a write.

Codex resolves scope in this order: an explicit `POWERCONTEXT_CODEX_SCOPE_ID`, a Workstream scope persistently bound
to the current Git workspace, the normalized Git remote, and finally the project path. Later Codex sessions in the
same workspace reuse that scope.

The picker returns the Workstream's human-facing `work_id` and authoritative `scope_id`. The `project-context` Skill
passes that exact scope to the resolver's `--bind-workstream` operation and verifies the result. The binding lives in
`powercontext/codex-workspace.json` below the Git-private directory, outside the worktree and commits. A one-line
Handoff then continues the selected Workstream's Artifact lifecycle and creates the next Revision. If the MCP client
does not support native elicitation, the tool returns structured choices instead; the integration must still obtain an
explicit selection and must not choose silently.
At Session start, Codex resolves Scope in this order: an explicit `POWERCONTEXT_CODEX_SCOPE_ID`, an existing Session
binding, a host-managed workspace binding, and the Server's default Scope. The selected Scope is fixed to the Session.
Repository and directory identities are lookup inputs only; they never generate a Scope ID. The prompt hook uses the
binding for recall and capture, while `PreToolUse` injects it into data-plane tools so Agent input cannot redirect a
read or write. The host must create or bind a different Scope when the Session changes work boundaries.

The Hook calls `POST /v1/context/prepare` once before Codex analyzes the prompt. It requests an 8000-byte total budget,
strictly validates `powercontext.prepared-context.v1`, and injects the returned content unchanged. The Runtime labels
Expand Down
4 changes: 3 additions & 1 deletion docs/en/docs/how-to/configure-dsh.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ The plugin has two paths to the same Server:
- before each model step it asks the Runtime to prepare one final, bounded context value, then independently captures the user's prompt as Source evidence;
- named `pc_*` tools call the public HTTP API to remember, search, revise, retire, and audit Memory.

Memory scope comes from the normalized Git remote when one is available, or from the session workspace path otherwise. Set `POWERCONTEXT_DSH_SCOPE_ID` when the session has no workspace cwd, or when the scope must be independent of both. The plugin does not fall back to the Harness process directory.
The plugin resolves one Server-owned Scope in this order: `POWERCONTEXT_DSH_SCOPE_ID`, a durable binding for the
session workspace, then the Server default. The workspace path is hashed only as an external binding key. A missing
workspace therefore uses the Server default instead of the Harness process directory.

The plugin calls `POST /v1/context/prepare` once before the model analyzes the prompt. Explicit `remember_memory` calls do not require a model.

Expand Down
12 changes: 5 additions & 7 deletions docs/en/docs/how-to/configure-hermes.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,13 @@ hermes powercontext search "Python package manager"
```

Inside an interactive Hermes session, `/pc status` should reach the same active provider. Use `/pc ` followed by
Tab/Down to inspect the available Memory, Handoff, Experience, Skill, review, statistics, trace, and Workstream
Tab/Down to inspect the available Memory, Handoff, Experience, Skill, review, statistics, trace, and Scope
commands. Hermes 0.20.4 does not provide enough invocation context to route gateway slash commands safely, so the
companion rejects gateway invocations; use the provider's Hermes tools in gateway sessions.

The provider uses `http://127.0.0.1:8000` by default. In a Git workspace, Workstream persistence first reads the
shared `.git/powercontext/codex-workspace.json` scope binding. An explicit scope configuration takes precedence.
Without either value, the provider derives a scope from the active Hermes profile and gateway user identifier; for a
local CLI session without a user identifier, it derives a stable value from `HERMES_HOME`.
The provider uses `http://127.0.0.1:8000` by default. The Server resolves an explicit Scope first, then durable session
and workspace bindings, and finally its default Scope. Hermes hashes the workspace path only as an external binding
key; it does not generate Scope IDs from profiles, users, repositories, or directories.

## Configure the connection

Expand All @@ -72,15 +71,14 @@ the file:
| `POWERCONTEXT_HERMES_BASE_URL` | PowerContext Server URL |
| `POWERCONTEXT_HERMES_AUTHORIZATION` | Complete authorization header, such as `Bearer <token>` |
| `POWERCONTEXT_HERMES_TOKEN` | Bare-token shorthand used when `AUTHORIZATION` is absent |
| `POWERCONTEXT_HERMES_SCOPE_ID` | Explicit scope or scope template |
| `POWERCONTEXT_HERMES_SCOPE_ID` | Explicit server-owned Scope ID |
| `POWERCONTEXT_HERMES_MAX_BYTES` | Maximum prepared-context size, from 512 to 32768 bytes |
| `POWERCONTEXT_HERMES_TIMEOUT` | HTTP request timeout in seconds |
| `POWERCONTEXT_HERMES_CAPTURE_TURNS` | Capture completed turns as Sources |
| `POWERCONTEXT_HERMES_FLUSH_ON_SESSION_END` | Run Memory extraction at session end |
| `POWERCONTEXT_HERMES_CAPTURE_PRE_COMPRESS` | Capture filtered new turns before compression; disabled by default |
| `POWERCONTEXT_HERMES_EVALUATION_TRACE` | Record recalled context in sensitive local JSONL traces; disabled by default |
| `POWERCONTEXT_HERMES_EVALUATION_TRACE_PATH` | Override the evaluation trace directory |
| `POWERCONTEXT_HERMES_WORKSTREAM` | Read the shared Git-private Workstream binding; enabled by default |

Let the Hermes wizard store authorization in its protected `.env` secret store; do not put the token in
`config.json`. Use plain HTTP only for a loopback Server. See [Deploy the Server](deploy-server.md) before connecting
Expand Down
Loading
Loading