diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index c509b6e..f788820 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "pm", - "version": "0.24.0", + "version": "0.25.0", "description": "A lightweight project-management layer that sits above OpenSpec and Superpowers. Tracks active proposals as epics, maintains an explicit detour stack, and enforces a reconcile gate so nothing is lost when development pivots or context is compacted.", "author": { "name": "Rob Sherman", diff --git a/.conductor/render-stamp.json b/.conductor/render-stamp.json index 8d438f8..14a0098 100644 --- a/.conductor/render-stamp.json +++ b/.conductor/render-stamp.json @@ -1,4 +1,4 @@ { - "renderedAt": "2026-07-30T15:40:51.660Z", - "stateMtimeMs": 1785426051639.6658 + "renderedAt": "2026-07-30T21:21:55.794Z", + "stateMtimeMs": 1785446515777.2825 } diff --git a/.conductor/state.json b/.conductor/state.json index c151ad0..bb795c8 100644 --- a/.conductor/state.json +++ b/.conductor/state.json @@ -1219,7 +1219,7 @@ "id": "edd-observe-hardcodes-claude-md", "title": "observe.py hardcodes CLAUDE.md -- a second, undeclared platform seam", "priority": "P1", - "status": "queued", + "status": "archived", "role": "epic", "lane": "claude-code", "links": [ @@ -1234,9 +1234,10 @@ "stories": [ { "title": "fold into the rules-block epic: once the engine writes a per-platform rules-block target, update observe() to match and prove both with one eval", - "done": false + "done": true } - ] + ], + "completedAt": "2026-07-30T21:21:55.677Z" }, { "id": "edd-user-memory-confound-control-run", diff --git a/CHANGELOG.md b/CHANGELOG.md index 60ded66..f5a8173 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,23 @@ This project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] +## [0.25.0] — 2026-07-30 + +### Added + +- **`rules-target` — a read-only query printing the absolute path of the file the resolved + platform's rules block belongs in**, walking that platform's first-match-wins chain. It + deliberately does **not** record a platform: a query must not mutate state the way + `write-rules` does. + + It exists so tooling around pm never has to mirror `PLATFORM_RULES_CHAIN`. `evals/observe.py` + hardcoded `CLAUDE.md`, which 0.24.0 quietly turned into a *second* platform seam — once the + engine began writing a per-platform target, the observer reported `rules_block_present: false` + for any platform whose block lands elsewhere. That is a confident wrong answer, and it would + have surfaced on the first Hermes or Codex run as a parity failure that is not real. Copying + the chain into Python was rejected: a second copy of platform knowledge only *moves* the + drift instead of removing it. + ## [0.24.0] — 2026-07-30 ### Added diff --git a/PROJECT.md b/PROJECT.md index e57ef82..613594c 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -3,7 +3,7 @@ > GENERATED by the `pm` plugin — do not hand-edit. Source of truth is > `.conductor/state.json` (ordering, detours, links) + OpenSpec `tasks.md` (stories). > Regenerate: `/pm:status` (or `node scripts/conductor.mjs render`). -> Last rendered: 2026-07-30T15:40:51.653Z +> Last rendered: 2026-07-30T21:21:55.691Z ## Now @@ -23,7 +23,7 @@ _Empty — no work is paused._ | P1 | `df-hierarchy-executor-must-check-readme-for-user-facing-changes` | claude-code | epic | archived | — | - | | P1 | `df-live-infra-epic-session-continuity-warning` | claude-code | epic | archived 🤖 | — | - | | P1 | `df-readme-stale-since-gate-guard` | claude-code | epic | archived | — | - | -| P1 | `edd-observe-hardcodes-claude-md` | claude-code | epic | queued | 0/1 stories | depends-on→rules-block-hardcodes-claude-slash-commands | +| P1 | `edd-observe-hardcodes-claude-md` | claude-code | epic | archived | 1/1 stories | depends-on→rules-block-hardcodes-claude-slash-commands | | P1 | `edd-user-memory-confound-control-run` | claude-code | epic | queued | 0/5 stories | blocks→hermes-platform-support | | P1 | `gh-65-autodetour-false-positives` | claude-code | epic | queued | 2/4 stories | - | | P1 | `pm-fix-link-flag-validation-and-update` | claude-code | epic | archived | — | - | @@ -113,12 +113,12 @@ CONDUCTOR STATE — where we are and what's next NOW: (no active epic set) NEXT UP (by priority, then lane): - • `edd-observe-hardcodes-claude-md` (P1, claude-code, queued) — 0/1 stories • `edd-user-memory-confound-control-run` (P1, claude-code, queued) — 0/5 stories • `gh-65-autodetour-false-positives` (P1, claude-code, queued) — 2/4 stories • `autodetour-parser-misses-am-and-f` (P2, claude-code, queued) — 0/3 stories • `edd-adapter-discards-agent-stdout` (P2, claude-code, queued) — — - (+7 more — see PROJECT.md) + • `edd-measures-installed-plugin-not-worktree` (P2, claude-code, queued) — — + (+6 more — see PROJECT.md) lanes: openspec 2 · superpowers 5 · claude-code 55 · decision 7 planned: 2 — see PROJECT.md @@ -141,7 +141,7 @@ EPIC LINKS: • `portfolio-architecture-consistency-scan` depends-on `epic-hierarchy-orchestration` — C presumes B's hierarchy execution exists to propagate cross-cutting decisions into TRACKER SYNC (github-issues): - ⚠ not yet in github-issues — create issues + record keys (update-epic): `edd-observe-hardcodes-claude-md`, `edd-user-memory-confound-control-run`, `autodetour-parser-misses-am-and-f`, `edd-adapter-discards-agent-stdout`, `edd-measures-installed-plugin-not-worktree`, `platform-switch-orphans-old-rules-block`, `multi-platform-agent-support` + ⚠ not yet in github-issues — create issues + record keys (update-epic): `edd-user-memory-confound-control-run`, `autodetour-parser-misses-am-and-f`, `edd-adapter-discards-agent-stdout`, `edd-measures-installed-plugin-not-worktree`, `platform-switch-orphans-old-rules-block`, `multi-platform-agent-support` 💡 1 tracker configured (github-issues) — consider `/pm:sync` this session to pull in any new issues. diff --git a/README.md b/README.md index 6607b88..9efb709 100644 --- a/README.md +++ b/README.md @@ -46,10 +46,10 @@ Not a benchmark — real numbers pulled straight from this repo's own history, v - Every multi-agent hierarchy dispatch runs **worktree-isolated**, unattended, converging back through sequential merge with **zero data loss** — every conflict seen so far has been mechanical (a shared CHANGELOG header, a usage string), never a real logic collision. -- **38 releases** shipped end-to-end (spec → build → test → changelog → version bump → release) +- **39 releases** shipped end-to-end (spec → build → test → changelog → version bump → release) with the plugin managing its own backlog the entire time. -- **282 tests**, **0 dependencies** — the entire engine is Node 18+ built-ins only, ~2,900 lines - across a 130-line `scripts/conductor.mjs` entry point and 26 `scripts/lib/*.mjs` modules, +- **287 tests**, **0 dependencies** — the entire engine is Node 18+ built-ins only, ~2,900 lines + across a 140-line `scripts/conductor.mjs` entry point and 26 `scripts/lib/*.mjs` modules, nothing to `npm install`. The suite itself is split across 11 files so it runs in parallel. - Caught its own bugs mid-flight, live: a stale-cache silent fallback, an archived-child leak in hierarchy planning, a false-positive auto-detour heuristic — each found by using the tool on @@ -488,6 +488,11 @@ pre-0.24.0 state file that predates the field — re-renders `PROJECT.md`, and s `pmVersion`. Idempotent — safe to run more than once. Requires `/reload-plugins` first if you just updated the plugin (the SessionStart briefing tells you when). +The engine also exposes `rules-target`, a read-only query printing the absolute path of the file +the recorded platform's rules block belongs in (resolving that platform's first-match-wins +chain). It exists so tooling around pm never has to mirror the chain — a second copy of platform +knowledge is drift waiting to happen. Unlike `write-rules`, it records nothing. + After showing the changelog delta ("What's new in pm"), the agent reviews each `Added` headline and recommends adopting any opt-in capability that's relevant to this repo's current `.conductor/state.json` (e.g. secondary trackers, `thorough` review mode) — one line, one diff --git a/evals/adapter.py b/evals/adapter.py index 39164e9..04517c6 100644 --- a/evals/adapter.py +++ b/evals/adapter.py @@ -43,6 +43,7 @@ def _failure(error: str) -> dict: "epic_ids": [], "detours": [], "rules_block_present": False, + "rules_block_file": None, "project_md_present": False, "new_epics": [], "exit_code": INFRA_FAILURE_EXIT_CODE, diff --git a/evals/observe.py b/evals/observe.py index 5c2e9a4..b362840 100644 --- a/evals/observe.py +++ b/evals/observe.py @@ -8,8 +8,15 @@ from __future__ import annotations import json +import os +import subprocess from pathlib import Path +ENGINE = Path(__file__).resolve().parent.parent / "scripts" / "conductor.mjs" + +# Only the stable PREFIX, never the full decorated marker. The engine's own detection keys on +# this same prefix precisely so the parenthetical can change without stranding older blocks -- +# matching the full string here would re-create that bug on the observer side. RULES_BEGIN = "