mirror of
https://github.com/lllin000/PaperForge.git
synced 2026-07-22 06:50:53 +00:00
chore: enforce Ask Matt workflow guardrails
This commit is contained in:
parent
4ef9e9834a
commit
6dfb209de4
4 changed files with 329 additions and 8 deletions
11
.omp/RULES.md
Normal file
11
.omp/RULES.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Matt Workflow Guardrails
|
||||
|
||||
- Ask Matt owns the engineering lifecycle: route with `/ask-matt`, implement one agent-ready issue with `/implement`, then run `/review`. Do not introduce a second orchestration workflow.
|
||||
- One implementation session handles one issue in one authoritative worktree. Start a fresh session before changing issues; use `/handoff` only when context must cross sessions.
|
||||
- Before editing, pin the issue, parent PRD, issue `updated_at`, base SHA, worktree root, scope, acceptance criteria, and verification commands. If the issue changes, stop and refresh this contract.
|
||||
- Use one writer. Parallel `task` batches may contain only read-only `scout`, `reviewer`, or `librarian` agents. Never let parallel agents edit shared files or the same worktree.
|
||||
- Never copy implementation files between the main checkout and a worktree. All reads, writes, tests, reviews, and commits for an issue use its authoritative worktree.
|
||||
- Follow Matt `/tdd` at pre-agreed seams. Run focused checks while implementing and the issue-specific final gate once after the last mutation; UI work also needs a real-browser smoke test.
|
||||
- Review exactly once on Matt's two axes. Consolidate findings into one repair pass and one re-review. If an important defect remains, return to the acceptance contract or split the issue; do not create repeated Final/Definitive review loops.
|
||||
- Commit, merge, push, create/merge a PR, or close an issue only after verification succeeds after the last mutation. Update `PROJECT-MANAGEMENT.md` and the active queue according to project rules before closeout.
|
||||
- The removed Superpowers workflows are not part of this project. Do not reinstall, invoke, emulate, or delegate through them.
|
||||
90
.omp/hooks/pre/matt-guard.test.ts
Normal file
90
.omp/hooks/pre/matt-guard.test.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import { execFileSync } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
import register from "./matt-guard";
|
||||
|
||||
type Handler = (
|
||||
event: Record<string, unknown>,
|
||||
context?: { cwd: string },
|
||||
) => unknown | Promise<unknown>;
|
||||
|
||||
const root = path.resolve(import.meta.dir, "../../..");
|
||||
|
||||
function handlers() {
|
||||
const registered: Record<string, Handler> = {};
|
||||
register({
|
||||
on: (event: string, handler: Handler) => {
|
||||
registered[event] = handler;
|
||||
},
|
||||
} as unknown as Parameters<typeof register>[0]);
|
||||
return registered;
|
||||
}
|
||||
|
||||
function foreignWorktree(): string | undefined {
|
||||
const current = path.normalize(root).toLowerCase();
|
||||
return execFileSync("git", ["worktree", "list", "--porcelain"], {
|
||||
cwd: root,
|
||||
encoding: "utf8",
|
||||
})
|
||||
.split(/\r?\n/)
|
||||
.filter((line) => line.startsWith("worktree "))
|
||||
.map((line) => path.normalize(line.slice("worktree ".length)))
|
||||
.find((worktree) => worktree.toLowerCase() !== current);
|
||||
}
|
||||
|
||||
describe("Matt workflow guard", () => {
|
||||
test("allows parallel read-only agents and blocks a writer", async () => {
|
||||
const hook = handlers();
|
||||
const context = { cwd: root };
|
||||
|
||||
expect(
|
||||
await hook.tool_call(
|
||||
{
|
||||
toolName: "task",
|
||||
input: { tasks: [{ agent: "reviewer" }, { agent: "scout" }] },
|
||||
},
|
||||
context,
|
||||
),
|
||||
).toBeUndefined();
|
||||
expect(
|
||||
await hook.tool_call(
|
||||
{
|
||||
toolName: "task",
|
||||
input: { tasks: [{ agent: "reviewer" }, {}] },
|
||||
},
|
||||
context,
|
||||
),
|
||||
).toMatchObject({ block: true });
|
||||
});
|
||||
|
||||
test("requires verification after the latest mutation", async () => {
|
||||
const hook = handlers();
|
||||
const context = { cwd: root };
|
||||
const commit = { toolName: "bash", input: { command: "git commit --dry-run" } };
|
||||
|
||||
expect(await hook.tool_call(commit, context)).toMatchObject({ block: true });
|
||||
await hook.tool_result({
|
||||
toolName: "bash",
|
||||
input: { command: "npm test" },
|
||||
isError: false,
|
||||
details: { exitCode: 0 },
|
||||
});
|
||||
expect(await hook.tool_call(commit, context)).toBeUndefined();
|
||||
|
||||
await hook.tool_call({ toolName: "edit", input: "mutation" }, context);
|
||||
expect(await hook.tool_call(commit, context)).toMatchObject({ block: true });
|
||||
});
|
||||
|
||||
test.skipIf(!foreignWorktree())("blocks writes into another linked worktree", async () => {
|
||||
const hook = handlers();
|
||||
const target = path.join(foreignWorktree()!, "CONTEXT.md");
|
||||
|
||||
expect(
|
||||
await hook.tool_call(
|
||||
{ toolName: "write", input: { path: target } },
|
||||
{ cwd: root },
|
||||
),
|
||||
).toMatchObject({ block: true });
|
||||
});
|
||||
});
|
||||
211
.omp/hooks/pre/matt-guard.ts
Normal file
211
.omp/hooks/pre/matt-guard.ts
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
import { execFileSync } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
|
||||
|
||||
const READ_ONLY_AGENTS: Record<string, true> = {
|
||||
scout: true,
|
||||
reviewer: true,
|
||||
librarian: true,
|
||||
};
|
||||
const RELEASE_COMMAND =
|
||||
/\bgit(?:\.exe)?\b[^\r\n;&|]*\b(?:commit|merge|push)\b|\bgh(?:\.exe)?\s+(?:issue\s+close|pr\s+(?:create|merge))\b/i;
|
||||
const VERIFICATION_COMMAND =
|
||||
/\b(?:pytest|unittest|vitest|jest|ruff|mypy|pyright|tsc)\b|\b(?:npm|pnpm|yarn|bun)\s+(?:test|run\s+(?:test|typecheck|build|lint))\b|\bcargo\s+(?:test|check)\b|\bgo\s+test\b|\bdotnet\s+test\b/i;
|
||||
const URI = /^[a-z][a-z0-9+.-]*:\/\//i;
|
||||
|
||||
interface GuardState {
|
||||
verifiedAfterMutation: boolean;
|
||||
}
|
||||
|
||||
function record(value: unknown): Record<string, unknown> {
|
||||
return value && typeof value === "object"
|
||||
? (value as Record<string, unknown>)
|
||||
: {};
|
||||
}
|
||||
|
||||
function json(value: unknown): Record<string, unknown> {
|
||||
try {
|
||||
return record(JSON.parse(String(value ?? "")));
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function runGit(cwd: string, args: string[]): string {
|
||||
try {
|
||||
return execFileSync("git", args, {
|
||||
cwd,
|
||||
encoding: "utf8",
|
||||
windowsHide: true,
|
||||
stdio: ["ignore", "pipe", "ignore"],
|
||||
}).trim();
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function normalized(value: string): string {
|
||||
const resolved = path.resolve(value);
|
||||
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
||||
}
|
||||
|
||||
function isWithin(root: string, candidate: string): boolean {
|
||||
const relative = path.relative(root, candidate);
|
||||
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
||||
}
|
||||
|
||||
function crossesWorktrees(candidate: string, cwd: string): boolean {
|
||||
if (!candidate || URI.test(candidate)) return false;
|
||||
|
||||
const current = runGit(cwd, ["rev-parse", "--show-toplevel"]);
|
||||
if (!current) return false;
|
||||
|
||||
const target = normalized(path.resolve(cwd, candidate));
|
||||
const currentRoot = normalized(current);
|
||||
const worktrees = runGit(cwd, ["worktree", "list", "--porcelain"])
|
||||
.split(/\r?\n/)
|
||||
.filter((line) => line.startsWith("worktree "))
|
||||
.map((line) => normalized(line.slice("worktree ".length)));
|
||||
return worktrees.some(
|
||||
(root) => root !== currentRoot && isWithin(root, target),
|
||||
);
|
||||
}
|
||||
|
||||
function pathsFromCall(toolName: string, input: unknown): string[] {
|
||||
const fields = record(input);
|
||||
|
||||
if (toolName === "edit") {
|
||||
const patch = typeof input === "string" ? input : String(fields.input ?? "");
|
||||
return [...patch.matchAll(/^\[([^#\r\n]+)#[0-9A-F]{4}\]$/gm)].map(
|
||||
(match) => match[1],
|
||||
);
|
||||
}
|
||||
|
||||
if (toolName === "ast_edit") {
|
||||
return Array.isArray(fields.paths) ? fields.paths.map(String) : [];
|
||||
}
|
||||
|
||||
if (toolName === "lsp") {
|
||||
return typeof fields.file === "string" ? [fields.file] : [];
|
||||
}
|
||||
|
||||
if (toolName !== "write") return [];
|
||||
const target = String(fields.path ?? "");
|
||||
if (target === "xd://ast_edit") {
|
||||
const payload = json(fields.content);
|
||||
return Array.isArray(payload.paths) ? payload.paths.map(String) : [];
|
||||
}
|
||||
if (target === "xd://lsp") {
|
||||
const payload = json(fields.content);
|
||||
return typeof payload.file === "string" ? [payload.file] : [];
|
||||
}
|
||||
return URI.test(target) ? [] : [target];
|
||||
}
|
||||
|
||||
function tasksFromCall(input: unknown): Record<string, unknown>[] {
|
||||
const tasks = record(input).tasks;
|
||||
return Array.isArray(tasks) ? tasks.map(record) : [];
|
||||
}
|
||||
|
||||
function hasWriterTask(input: unknown): boolean {
|
||||
return tasksFromCall(input).some(
|
||||
(task) => READ_ONLY_AGENTS[String(task.agent ?? "")] !== true,
|
||||
);
|
||||
}
|
||||
|
||||
function isMutatingCall(toolName: string, input: unknown): boolean {
|
||||
if (toolName === "edit" || toolName === "ast_edit") return true;
|
||||
if (toolName === "task") return hasWriterTask(input);
|
||||
if (toolName === "lsp") {
|
||||
const fields = record(input);
|
||||
return (
|
||||
fields.action === "rename" ||
|
||||
fields.action === "rename_file" ||
|
||||
(fields.action === "code_actions" && fields.apply === true)
|
||||
);
|
||||
}
|
||||
if (toolName !== "write") return false;
|
||||
|
||||
const fields = record(input);
|
||||
const target = String(fields.path ?? "");
|
||||
if (!URI.test(target)) return true;
|
||||
if (target === "xd://ast_edit") return true;
|
||||
if (target !== "xd://lsp") return false;
|
||||
|
||||
const payload = json(fields.content);
|
||||
return (
|
||||
payload.action === "rename" ||
|
||||
payload.action === "rename_file" ||
|
||||
(payload.action === "code_actions" && payload.apply === true)
|
||||
);
|
||||
}
|
||||
|
||||
function releaseAction(toolName: string, input: unknown): boolean {
|
||||
const fields = record(input);
|
||||
if (toolName === "bash") return RELEASE_COMMAND.test(String(fields.command ?? ""));
|
||||
if (toolName === "github") {
|
||||
return ["pr_create", "pr_push"].includes(String(fields.op ?? ""));
|
||||
}
|
||||
if (toolName !== "write" || fields.path !== "xd://github") return false;
|
||||
return ["pr_create", "pr_push"].includes(String(json(fields.content).op ?? ""));
|
||||
}
|
||||
|
||||
function successfulVerification(event: Record<string, unknown>): boolean {
|
||||
if (event.toolName !== "bash" || event.isError === true) return false;
|
||||
const input = record(event.input);
|
||||
if (!VERIFICATION_COMMAND.test(String(input.command ?? ""))) return false;
|
||||
|
||||
const details = record(event.details);
|
||||
const exitCode = details.exitCode ?? details.exit_code ?? details.code;
|
||||
if (typeof exitCode === "number") return exitCode === 0;
|
||||
|
||||
const content = Array.isArray(event.content) ? event.content : [];
|
||||
const text = content.map((chunk) => String(record(chunk).text ?? "")).join("\n");
|
||||
return !/command exited with code\s+[1-9]\d*/i.test(text);
|
||||
}
|
||||
|
||||
export default function mattGuard(pi: ExtensionAPI): void {
|
||||
const state: GuardState = { verifiedAfterMutation: false };
|
||||
|
||||
pi.on("tool_call", async (event, ctx) => {
|
||||
const toolName = String(event.toolName);
|
||||
const input = event.input;
|
||||
|
||||
if (toolName === "task") {
|
||||
const tasks = tasksFromCall(input);
|
||||
if (tasks.length > 1 && hasWriterTask(input)) {
|
||||
return {
|
||||
block: true,
|
||||
reason:
|
||||
"Matt guard: parallel task batches may contain only scout, reviewer, or librarian agents. Use one writer.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const crossWorktree = pathsFromCall(toolName, input).find((candidate) =>
|
||||
crossesWorktrees(candidate, ctx.cwd),
|
||||
);
|
||||
if (crossWorktree) {
|
||||
return {
|
||||
block: true,
|
||||
reason: `Matt guard: refusing write into another Git worktree: ${crossWorktree}`,
|
||||
};
|
||||
}
|
||||
|
||||
if (releaseAction(toolName, input) && !state.verifiedAfterMutation) {
|
||||
return {
|
||||
block: true,
|
||||
reason:
|
||||
"Matt guard: run the issue-specific verification gate after the last mutation before commit, merge, push, PR creation, or issue close.",
|
||||
};
|
||||
}
|
||||
|
||||
if (isMutatingCall(toolName, input)) state.verifiedAfterMutation = false;
|
||||
});
|
||||
|
||||
pi.on("tool_result", async (event) => {
|
||||
if (successfulVerification(event as unknown as Record<string, unknown>)) {
|
||||
state.verifiedAfterMutation = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
> **Branch:** `feat/ocr-rebuild-ux` | **Last Updated:** 2026-07-15
|
||||
> **Active work:** [Control-center PRD #74](https://github.com/LLLin000/PaperForge/issues/74) is split into eight dependency-linked implementation issues (#75–#82). Canonical setup/config migration (#75) and the Installation/Help capability tracer (#76) are implemented; Managed Runtime lifecycle (#77) is the next production slice.
|
||||
> **Branch:** `master` | **Last Updated:** 2026-07-18
|
||||
> **Active work:** [Control-center PRD #74](https://github.com/LLLin000/PaperForge/issues/74) is split into eight dependency-linked implementation issues (#75–#82). Setup/config (#75), capability envelopes (#76), and Managed Runtime/navigation (#77) are implemented; Library/OCR/Memory capability tracers (#78) are next.
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> **Current state:** Retrieval recovery is merged and live. OCR multi-key rebuild/redo streams progress and refreshes canonical maintenance state. The control plane now has schema-v1 capability envelopes from Python through the Obsidian settings surface: Installation and Help use real probes, stale/malformed cached evidence fails closed, and Library/OCR/Memory/Maintenance remain explicit placeholders for later tracers.
|
||||
> **Current state:** Retrieval recovery is merged and live. OCR multi-key rebuild/redo streams progress and refreshes canonical maintenance state. The control plane now includes schema-v1 capability envelopes plus a machine-local Managed Runtime with immutable slots, atomic activation, rollback, cancellation, and managed-first command dispatch.
|
||||
>
|
||||
> #71/#72 prototypes and #73 migration contract are complete. PRD #74 defines the six-module production program; #75 and #76 are implemented. The approved navigation refinement preserves the Wayfinder overview and stages `概览 / 模块详情 / 维护 / 帮助` across #77/#78/#80 instead of creating dead placeholder detail pages.
|
||||
> #71/#72 prototypes and #73 migration contract are complete. PRD #74 defines the six-module production program; #75–#77 are implemented. The Wayfinder overview and `概览 / 模块详情 / 维护 / 帮助` shell are live; #78 adds real Library/OCR/Memory detail tracers without placeholder pages.
|
||||
>
|
||||
> Next: implement [#77](https://github.com/LLLin000/PaperForge/issues/77) in a fresh Matt `/implement` session.
|
||||
> Next: implement [#78](https://github.com/LLLin000/PaperForge/issues/78) in a fresh Matt `/implement` session.
|
||||
## 1. Architecture
|
||||
|
||||
### 1.1 The problem (pre-v2)
|
||||
|
|
@ -60,6 +60,8 @@ raw observations → structural signatures → stable anchors/families → zone
|
|||
| Maintenance regression action model | **19/19 passed** ✅ (per-row canonical action routing, redo confirmation gate, cache manifest preservation) |
|
||||
| Canonical setup/config migration (#75) | **61 passed, 0 failed** ✅ (fresh/v1/v2 config, CLI routing, path forwarding, failure exit, idempotent rerun) |
|
||||
| Installation/Help capability tracer (#76) | **21 backend tests + 169 plugin tests passed; typecheck/build clean; independent review PASS** ✅ |
|
||||
| Managed Runtime lifecycle + navigation (#77) | **192 focused + 289 full passed; typecheck/build clean** ✅ |
|
||||
| OMP Matt workflow guard | **Fresh OMP auto-discovery confirmed; 7/7 deterministic guard cases passed** ✅ |
|
||||
|
||||
</br>
|
||||
</br>
|
||||
|
|
@ -85,6 +87,8 @@ raw observations → structural signatures → stable anchors/families → zone
|
|||
| **Control-plane contracts** | ✅ Orthogonal capability/activity/attention model and managed-runtime immutable-slot architecture chosen; documented in `docs/research/2026-07-14-capability-state-action-contract.md` and `docs/research/2026-07-14-managed-runtime-architecture.md` |
|
||||
| **Setup/config migration** | ✅ Bare, headless, and modular setup share `SetupPlan`; schema-v2 `vault_config` is authoritative with warned v1 read fallback |
|
||||
| **Capability tracer** | ✅ Installation/Help backend envelopes feed the six-module Overview; malformed/stale persistence fails closed; setup/update actions route to the setup flow |
|
||||
| **Managed Runtime** | ✅ Immutable machine-local slots, atomic pointer activation, rollback/cancel/retention, managed-first command dispatch |
|
||||
| **Agent workflow controls** | ✅ `.omp/RULES.md` + `matt-guard.ts` enforce one-writer Matt flow, worktree isolation, and post-mutation verification before release operations |
|
||||
|
||||
### 2.3 Fix Status
|
||||
|
||||
|
|
@ -121,6 +125,7 @@ raw observations → structural signatures → stable anchors/families → zone
|
|||
| 32 | — | OCR rebuild progress + maintenance selection contract | Feature | Added flushed, prefix-separated rebuild/redo streams; full keyed redo; cooperative stop; canonical `needs_derived_rebuild`; All/Recommended filters; selected batch progress UI | `d7b0a527`, `3a516add`, `e556c8ba` |
|
||||
| 33 | — | OCR maintenance canonical per-row action model | Fix | Added `maintenanceActionForRow()` for `display_action`→verb routing, `maintenanceActionRequiresConfirmation()` redo confirmation gate, batch-action filtering by canonical verb, and cache-manifest preservation. Batch actions now follow the canonical backend action instead of raw `can_rebuild`/`can_redo` booleans; destructive redo requires user confirmation; cache refresh preserves the backend manifest. | `d7b0a527` |
|
||||
| 34 | — | Setup/config paths had conflicting precedence, duplicate engines, dropped user paths, and false-success exits | Migration/prefactor | Unified CLI dispatch on `SetupPlan`; v2-only writes with v1 read fallback; complete path forwarding; visible deprecation; non-zero required-step failures | `af849699`, `7b747423`, `906b3caf` |
|
||||
| 35 | — | Agent sessions could bypass the Matt issue lifecycle, fan out writers, cross worktree boundaries, or release after stale verification | Workflow guard | Added sticky Matt rules plus a project hook that blocks parallel writer batches, cross-worktree mutations, and commit/merge/push/PR/issue-close operations until a recognized verification command succeeds after the latest mutation. | — |
|
||||
|
||||
|
||||
## 3. Remaining Issues — Release-Readiness Layers
|
||||
|
|
@ -150,7 +155,7 @@ raw observations → structural signatures → stable anchors/families → zone
|
|||
[Wayfinder: Restore PaperForge retrieval end to end](https://github.com/LLLin000/PaperForge/issues/45) completed and the resulting retrieval fixes are merged to `master`. The live Literature-hub vault has a healthy 2560-dimensional vec0 index; M and @ search paths are operational.
|
||||
|
||||
### Layer 3: Plugin UI
|
||||
The OCR maintenance slice has a canonical All/Recommended state model, selected batch actions, streaming progress, cooperative stop, and canonical per-row action routing with confirmation gates for destructive operations. Issue #76 now provides the first production capability-envelope tracer: real Installation/Help probes, a six-module Overview, strict persisted-envelope validation, stale fail-closed behavior, and backend-owned actions. Library/OCR/Memory/Maintenance remain explicit placeholders until #78/#80. The approved Wayfinder navigation refinement is recorded on PRD #74 and staged across #77/#78/#80.
|
||||
The OCR maintenance slice has a canonical All/Recommended state model, selected batch actions, streaming progress, cooperative stop, and canonical per-row action routing with confirmation gates for destructive operations. Issues #76–#77 now provide production capability envelopes, the four-destination navigation shell, and the machine-local Managed Runtime lifecycle. Installation/Help are real probes; Library/OCR/Memory/Maintenance remain explicit placeholders pending #78/#80. Stale or malformed persisted evidence fails closed.
|
||||
|
||||
### Layer 4: Downstream Tools
|
||||
`chunker.py` uses hardcoded section regex + fixed 3-paragraph groups. OCR has rich structured output (sections, headings, figures, tables with captions) — chunker should consume this structure directly. Figures/tables should support separate embedding (text + future vision).
|
||||
|
|
@ -158,7 +163,7 @@ The OCR maintenance slice has a canonical All/Recommended state model, selected
|
|||
Remaining legacy OCR issues (carried forward):
|
||||
## 4. Active Queue
|
||||
|
||||
1. 🔴 **[Control-center PRD #74](https://github.com/LLLin000/PaperForge/issues/74)** — published and split into eight native dependency-linked issues (#75–#82). #75 and #76 are implemented; #77 Managed Runtime lifecycle is the next unblocked production tracer.
|
||||
1. 🔴 **[Control-center PRD #74](https://github.com/LLLin000/PaperForge/issues/74)** — published and split into eight native dependency-linked issues (#75–#82). #75–#77 are implemented; #78 Library/OCR/Memory capability tracers are the next unblocked slice.
|
||||
2. ✅ **[Capability-state vocabulary](https://github.com/LLLin000/PaperForge/issues/69)** — resolved at `issuecomment-4971161072`. Orthogonal availability/activity/attention axes, 6-state capability ordinal, 12 canonical verbs, backend-owned severity and primary actions, maintenance projection.
|
||||
3. ✅ **[Managed runtime](https://github.com/LLLin000/PaperForge/issues/70)** — resolved at `issuecomment-4971239398`. Plugin-managed immutable runtime slots, system-Python bootstrap with validated-triplet fallback, single `active-runtime.json` pointer, `ManagedRuntime` class with `current()`/`status()`/`ensure()`, fail-closed command resolution.
|
||||
4. ✅ **[Control-center prototype](https://github.com/LLLin000/PaperForge/issues/71)** — resolved with independent Critical/Important PASS review and browser verification at 768px. Six-module control-center HTML prototype covers 5 scenarios with plain-button switcher, primary attention zone, responsive layout, and capability-gated actions. Design decisions recorded in `docs/prototypes/2026-07-14-six-module-control-center.html/.md`. No production implementation before #73.
|
||||
|
|
@ -184,7 +189,8 @@ Remaining legacy OCR issues (carried forward):
|
|||
- [x] Publish PRD #74 and eight agent-ready issues (#75–#82) with native dependencies
|
||||
- [x] Canonicalize setup/config migration ([#75](https://github.com/LLLin000/PaperForge/issues/75)) — 61 focused tests; spec PASS; quality APPROVED
|
||||
- [x] Implement Installation/Help capability tracer ([#76](https://github.com/LLLin000/PaperForge/issues/76)) — 21 backend tests; 169 plugin tests; typecheck/build clean; independent review PASS
|
||||
- [ ] Implement Managed Runtime lifecycle and the approved Installation-detail navigation shell ([#77](https://github.com/LLLin000/PaperForge/issues/77))
|
||||
- [x] Implement Managed Runtime lifecycle and the approved Installation-detail navigation shell ([#77](https://github.com/LLLin000/PaperForge/issues/77)) — 192 focused + 289 full tests; typecheck/build clean; merged to `master`
|
||||
- [ ] Expose Library, OCR, and Memory capabilities end to end ([#78](https://github.com/LLLin000/PaperForge/issues/78))
|
||||
---
|
||||
|
||||
## 5. Key File Map
|
||||
|
|
@ -341,6 +347,7 @@ Remaining legacy OCR issues (carried forward):
|
|||
| 2026-07-15 | Canonical setup is one `SetupPlan`; configuration writes converge to schema v2 | Duplicate headless/modular/bare engines dropped path inputs and disagreed on success. One engine plus `vault_config`-first reads makes migration observable, idempotent, and reversible through the warned v1 read fallback. |
|
||||
| 2026-07-15 | Capability integration advances by real tracer, never frontend optimism | #76 exposes only Installation and Help as real probes; Library/OCR/Memory/Maintenance remain explicit placeholders until their backend envelopes ship. Persisted malformed or stale evidence becomes unknown/invalid rather than ready. |
|
||||
| 2026-07-15 | Module detail navigation extends Wayfinder instead of replacing it | Preserve the primary-attention zone and concrete backend action. Stage `概览 / 模块详情 / 维护 / 帮助` across #77/#78/#80; use explicit module-title navigation, top ordinary buttons, and no dead placeholder details. |
|
||||
| 2026-07-18 | Enforce Matt with sticky rules plus a minimal deterministic hook | Prose alone cannot prevent orchestration drift. Keep lifecycle guidance in `.omp/RULES.md`; use the hook only for mechanically provable boundaries: one writer, worktree isolation, and verification-after-last-mutation before release operations. |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -444,6 +451,8 @@ python -m ruff check paperforge/worker/ocr_*.py
|
|||
| 2026-07-15 | Six-module control-center + maintenance inbox prototypes completed | Closed #71 (six-module HTML prototype, 5 scenarios, plain-button switcher, responsive 768px) with independent Critical/Important PASS review and browser verification. Closed #72 (actionable-only inbox, inline issue-draft review, confirmation-first report) with same review gate. Both prototype pairs passed all review dimensions. No production code changed. | `docs/prototypes/2026-07-14-six-module-control-center.{html,md}`, `docs/prototypes/2026-07-14-maintenance-issue-reporting.{html,md}` |
|
||||
| 2026-07-15 | Control-center PRD split + first production slice | Published PRD #74 and eight native dependency-linked issues (#75–#82). Implemented #75: one SetupPlan for all setup entry points, schema-v2 `vault_config`, warned v1 read fallback, complete path forwarding, visible deprecation, and non-zero required-step failures. Verification: 61/61 focused tests; independent Spec PASS / Quality APPROVED. | [PRD #74](https://github.com/LLLin000/PaperForge/issues/74), [Issue #75](https://github.com/LLLin000/PaperForge/issues/75) |
|
||||
| 2026-07-15 | Installation/Help capability tracer + navigation refinement | Implemented schema-v1 probe envelopes, six-module Overview, setup-complete migration, strict persistence/TTL validation, backend-owned action labels and dispatch, responsive/focus-visible UI, and generated bundle. Verification: 21 backend tests, 169 plugin tests, typecheck/build, live Obsidian stale-cache/action-label smoke test, independent review PASS. Matt flow refined PRD #74 and existing #77/#78/#80 without duplicating issues. | [Issue #76](https://github.com/LLLin000/PaperForge/issues/76), [PRD refinement](https://github.com/LLLin000/PaperForge/issues/74#issuecomment-4980322098) |
|
||||
| 2026-07-15 | Managed Runtime lifecycle + final navigation shell | Completed #77 with immutable runtime slots, synchronous fail-closed `current`, probed `status`, install/repair/update/rollback/cancel/retention, managed-first dispatch, Release-N fallback, four-destination navigation, Installation detail, Agent integration, and Help focus restoration. Verification: 192 focused + 289 full tests; typecheck/build clean. Merged to `master` in `173a4e8..4ef9e98`. | [Issue #77](https://github.com/LLLin000/PaperForge/issues/77) |
|
||||
| 2026-07-18 | OMP Matt workflow enforcement | Removed all discoverable Superpowers installations, retained canonical Ask Matt skills, added sticky project rules, and installed an auto-discovered hook for one-writer batches, worktree isolation, and fresh verification gates. Fresh OMP smoke reached the hook; 7/7 deterministic guard cases passed. | `.omp/RULES.md`, `.omp/hooks/pre/matt-guard.ts` |
|
||||
|
||||
## 9. Historical Detail Archive
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue