mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
feat: vendor permission system source
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
---
|
||||
issue: 6
|
||||
issue_title: "Log resolved config paths at startup so misconfiguration is debuggable"
|
||||
---
|
||||
|
||||
# Log resolved config paths at startup
|
||||
|
||||
## Problem Statement
|
||||
|
||||
There is no way for a user to see which permission-config files the extension actually loaded.
|
||||
When permissions do not work as expected, debugging requires reading source to figure out the search paths.
|
||||
A single review-log entry listing all resolved paths and their existence status would unblock most debugging.
|
||||
|
||||
## Goals
|
||||
|
||||
- At extension startup (and on each `session_start`), emit a single `config.resolved` review-log entry listing every config path the extension considers and whether each file exists.
|
||||
- Also emit the entry to the debug log when debug logging is enabled.
|
||||
- Expose a `getResolvedConfigPaths()` method on `PermissionManager` so the path set is testable without IO side effects.
|
||||
- The log entry appears regardless of whether any rules are ultimately matched.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- A `pi config`-style TUI view (mentioned in the issue as a bonus; separate work).
|
||||
- Changing any policy semantics, merge precedence, or on-disk identity.
|
||||
- Logging the *contents* of each config file (only paths and existence).
|
||||
|
||||
## Background
|
||||
|
||||
### Relevant modules
|
||||
|
||||
- `src/permission-manager.ts` — `PermissionManager` constructor receives `globalConfigPath`, `agentsDir`, `projectGlobalConfigPath`, `projectAgentsDir`, plus internally computes `legacyGlobalSettingsPath` and `globalMcpConfigPath`.
|
||||
The paths are stored as private fields but are not externally queryable.
|
||||
- `src/extension-config.ts` — `CONFIG_PATH` is the extension's own `config.json`.
|
||||
`loadPermissionSystemConfig` loads it and returns a result with optional warning.
|
||||
- `src/index.ts` — `piPermissionSystemExtension` wires everything together.
|
||||
`refreshExtensionConfig()` runs at module load and on every `session_start`.
|
||||
`createPermissionManagerForCwd()` constructs the `PermissionManager` with project-scoped paths derived from `ctx.cwd`.
|
||||
- `src/logging.ts` — `PermissionSystemLogger.review()` and `.debug()` write structured JSONL lines.
|
||||
|
||||
### Permission surface
|
||||
|
||||
This is a **diagnostic/observability** concern.
|
||||
No permission surface (tools / bash / mcp / skills / special / external_directory) is changed.
|
||||
No policy semantics or merge precedence is affected.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Issue #4 (warn on misplaced permission keys in `config.json`) is already implemented and closed.
|
||||
This plan builds on the same startup path but is independent.
|
||||
|
||||
## Design Overview
|
||||
|
||||
### Data shape
|
||||
|
||||
```typescript
|
||||
interface ResolvedConfigPaths {
|
||||
extensionConfigPath: string;
|
||||
extensionConfigExists: boolean;
|
||||
globalConfigPath: string;
|
||||
globalConfigExists: boolean;
|
||||
projectConfigPath: string | null;
|
||||
projectConfigExists: boolean;
|
||||
agentsDir: string;
|
||||
agentsDirExists: boolean;
|
||||
projectAgentsDir: string | null;
|
||||
projectAgentsDirExists: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### New method on `PermissionManager`
|
||||
|
||||
Add a public `getResolvedConfigPaths(): ResolvedConfigPaths` method that returns the struct above.
|
||||
It checks `existsSync` for each path (cheap, synchronous, already imported in the file).
|
||||
The method is a pure query — no caching side effects.
|
||||
|
||||
`extensionConfigPath` is not owned by `PermissionManager`; it will be passed in by the caller (or injected via a new optional constructor option) since the extension config path lives in `extension-config.ts`.
|
||||
To keep the method self-contained without adding a constructor dependency, the caller in `index.ts` will combine the `PermissionManager` paths with the extension config path when building the log entry.
|
||||
|
||||
Revised approach: add `getResolvedPolicyPaths()` to `PermissionManager` (returns only the policy-related paths it owns), and have the startup reporter in `index.ts` merge in the extension config path to produce the full `config.resolved` log entry.
|
||||
|
||||
```typescript
|
||||
interface ResolvedPolicyPaths {
|
||||
globalConfigPath: string;
|
||||
globalConfigExists: boolean;
|
||||
projectConfigPath: string | null;
|
||||
projectConfigExists: boolean;
|
||||
agentsDir: string;
|
||||
agentsDirExists: boolean;
|
||||
projectAgentsDir: string | null;
|
||||
projectAgentsDirExists: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
### Where the log entry is emitted
|
||||
|
||||
In `index.ts`, after `permissionManager` is (re)created in the `session_start` handler, call a new helper `logResolvedConfigPaths(permissionManager)` that:
|
||||
|
||||
1. Calls `permissionManager.getResolvedPolicyPaths()`.
|
||||
2. Adds `extensionConfigPath` / `extensionConfigExists` from `CONFIG_PATH`.
|
||||
3. Writes a single `config.resolved` entry to both `writeReviewLog` and `writeDebugLog`.
|
||||
|
||||
### Log entry format
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"event": "config.resolved",
|
||||
"extensionConfigPath": "/…/pi-permission-system/config.json",
|
||||
"extensionConfigExists": true,
|
||||
"globalConfigPath": "/…/.pi/agent/pi-permissions.jsonc",
|
||||
"globalConfigExists": false,
|
||||
"projectConfigPath": "/…/my-project/.pi/agent/pi-permissions.jsonc",
|
||||
"projectConfigExists": true,
|
||||
"projectConfigExists": true,
|
||||
"agentsDir": "/…/.pi/agent/agents",
|
||||
"agentsDirExists": true,
|
||||
"projectAgentsDir": "/…/my-project/.pi/agent/agents",
|
||||
"projectAgentsDirExists": false
|
||||
}
|
||||
```
|
||||
|
||||
### Edge cases
|
||||
|
||||
- `projectConfigPath` and `projectAgentsDir` are `null` when no `cwd` is available — logged as `null` with `*Exists: false`.
|
||||
- Extension config file missing (first run, `ensurePermissionSystemConfig` creates it) — `extensionConfigExists` reflects state *after* the ensure step, so it will be `true`.
|
||||
- Multiple `session_start` events (reload) — the entry is emitted each time, which is correct since `cwd` and thus project paths may change.
|
||||
|
||||
## Module-Level Changes
|
||||
|
||||
### `src/permission-manager.ts`
|
||||
|
||||
1. Add `ResolvedPolicyPaths` interface (exported).
|
||||
2. Add public `getResolvedPolicyPaths(): ResolvedPolicyPaths` method that returns the five path pairs using `existsSync`.
|
||||
|
||||
### `src/index.ts`
|
||||
|
||||
1. Import `existsSync` (already imported) and `CONFIG_PATH` (already imported).
|
||||
2. Add `logResolvedConfigPaths(pm: PermissionManager): void` helper that assembles the full entry and writes to both review and debug logs.
|
||||
3. Call `logResolvedConfigPaths(permissionManager)` in the `session_start` handler after `permissionManager` is created.
|
||||
|
||||
### `tests/permission-manager.test.ts` (existing or new)
|
||||
|
||||
1. Add tests for `getResolvedPolicyPaths()` covering: all paths exist, none exist, mixed, null project paths.
|
||||
|
||||
### `tests/index.test.ts` or `tests/config-resolved-log.test.ts` (new)
|
||||
|
||||
1. Add tests for the log-entry assembly helper (if extracted as a pure function) or integration-level tests verifying the review log contains a `config.resolved` entry after startup.
|
||||
|
||||
### No changes needed
|
||||
|
||||
- `schemas/permissions.schema.json` — no policy-schema change.
|
||||
- `config/config.example.json` — no extension-config change.
|
||||
- `README.md` — could mention the diagnostic log entry, but optional and can be a follow-up.
|
||||
|
||||
## TDD Order
|
||||
|
||||
1. **Red:** test that `getResolvedPolicyPaths()` returns correct paths and existence flags when all policy files exist.
|
||||
`test: getResolvedPolicyPaths returns paths and existence when files exist`
|
||||
|
||||
2. **Green:** implement `ResolvedPolicyPaths` interface and `getResolvedPolicyPaths()` on `PermissionManager`.
|
||||
`feat: add getResolvedPolicyPaths to PermissionManager (#6)`
|
||||
|
||||
3. **Red:** test that `getResolvedPolicyPaths()` returns `*Exists: false` when files are missing and `null` for absent project paths.
|
||||
`test: getResolvedPolicyPaths handles missing files and null project paths`
|
||||
|
||||
4. **Green:** already passes from step 2 (or adjust).
|
||||
|
||||
5. **Red:** test the log-entry assembly helper produces the expected `config.resolved` shape combining extension config path with policy paths.
|
||||
`test: config.resolved log entry merges extension and policy paths`
|
||||
|
||||
6. **Green:** implement `buildResolvedConfigLogEntry()` helper in `index.ts` (or a small `src/config-reporter.ts` if warranted) and wire into `session_start`.
|
||||
`feat: emit config.resolved review-log entry at startup (#6)`
|
||||
|
||||
7. **Red:** integration test verifying the review log receives a `config.resolved` entry after a simulated startup sequence.
|
||||
`test: config.resolved entry appears in review log after session_start`
|
||||
|
||||
8. **Green:** already passes from step 6 (or adjust wiring).
|
||||
|
||||
9. **Docs:** mention the `config.resolved` log entry in `README.md` under a debugging/troubleshooting section.
|
||||
`docs: document config.resolved diagnostic log entry (#6)`
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
| Risk | Mitigation |
|
||||
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| `existsSync` on every `session_start` adds latency | Five synchronous `stat` calls are negligible; no mitigation needed. |
|
||||
| Could this silently weaken a permission? | No — this change only *adds* a diagnostic log entry. Permission resolution logic is untouched. |
|
||||
| Log entry leaks sensitive path information | Paths are already visible in the debug log and are user-controlled config locations; no new exposure. |
|
||||
| `getResolvedPolicyPaths` exposes internal paths of `PermissionManager` | The paths are user-configured inputs, not secrets. Exposing them is the explicit goal. |
|
||||
| Multiple `session_start` handlers already exist in `index.ts` | The log call will be added to the existing handler, not a new duplicate. |
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Whether to extract the log-entry builder into a standalone `src/config-reporter.ts` module or keep it inline in `index.ts`.
|
||||
Decision can be made during implementation based on size.
|
||||
Reference in New Issue
Block a user