Skip to content

release: pm 0.52.0 — safe to leave running - #244

Merged
cfdude merged 66 commits into
mainfrom
dev
Oct 14, 2026
Merged

cfdude merged 66 commits into
mainfrom
dev

Conversation

@cfdude

@cfdude cfdude commented Oct 14, 2026

Copy link
Copy Markdown
Owner

Cuts 0.52.0: spend tracking (opt-in, off by default), six-role execution profile, spend guard, brief and hook context caps, silent-wrong-record fixes. 14 member epics, all Gate-reviewed and archived. Resolves #172, #176, #196, #210, #213. See CHANGELOG.md.

https://claude.ai/code/session_01D9yCR4qSsVzzoaHepAeTUW

cfdude added 30 commits October 6, 2026 18:12
Bumps the shared workflow SHA to d5d0608 (TruffleHog 3.96.0 -> 3.97.0, which
fixes the Lob detector false positive on test_-prefixed names) and picks up
the Lob exclude commit b5f9e50 that the old pin predated. Refs #172.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
…cannot EPIPE the writer

The SessionStart and PreCompact payloads were never read; a payload past the pipe buffer left the writer holding an unread pipe. Both verbs drain stdin first (also in a repository without pm); commit-nudge drains on its dormant and detached early returns. Process-boundary guard in functional/verb-surface plus a unit spy in its twin.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
…isposition can be corrected

gh#176. --help renders the index base from the flag registry, a --story write echoes the story
it landed on, and --story <n> --correct-disposition reopens a story and keeps the original under
stories[n-1].corrections[]. The replacement guard is unchanged; its refusal names the new path.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
…eports UNKNOWN, not no drift

gh#196. The installed version is read from the package.json beside the resolved binary (no spawn);
the --version fallback gets a 15 s timeout and one answer per process; present-but-unreadable
renders one fixed OpenSpec drift UNKNOWN line, so the committed PROJECT.md stops flapping. An
absent CLI stays silent.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
…flog lines, name every epic in a retraction

Closes the 0.45.0 Gate 2 minors: at most 50 commits per run with the surplus left for the next (anchor stops at the last reported); a partial last reflog line is never the anchor; a RETRACTED row names every epic it covers; retract-detour says when prefix-related pruned rows were retracted together; the detached-tree finish() path is now tested.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
…s detours (#213)

A commit is left out of the AUTO-DETOUR trail when the record ties it to the active epic: already attributed to an epic, subject names the epic id, or every changed path was already changed by a live commit attributed to it. A commit another epic holds is not offered for attribution again (its holder is named), and an auto-logged commit also offered for attribution is called one-or-the-other.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
…repository without pm

The EPIPE fix of f7dcbda missed two sibling hooks that return before reading stdin when no conductor record exists. Both now call drainHookPayload() first; allow/deny behaviour is unchanged. Unit spy covers all five hook verbs without pm; the 1 MiB spawned functional guard now includes gate-guard and lesson-advice. Sweep: hooks.json invokes only brief, snapshot, commit-nudge, gate-guard, lesson-advice, so no other entry point remains.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
)

The rule "every changed path was already changed by a live commit attributed to the epic" also matched a real one-file fix to a file the epic already touched, hiding it from the detour trail. Option chosen: apply it only when the commit changes two or more paths (the reviewer's recommendation); the other two rules (already attributed, subject names the epic id) are unchanged. A false row is visible and retractable, a false suppression is silent.

Adds unit-rung tests for all three suppression rules, the new floor, live-commit and no-evidence cases, and for a commit another epic holds not being re-offered. isRecordedEpicWork and attributionNudge are exported with injectable git lookups so the rung runs no git. Functional 9.1 now uses two files and asserts the one-file floor. Changeset notes the DETOUR-COMMIT branch deliberately has no suppression.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
Twin-Unchanged: commit-observation — the unit twin holds no suppression test; the suppression rules' unit coverage is new in unit/attribution-nudge.test.mjs, staged in this commit
…egistry (#210)

upgrade reviewed only the changelog delta, so an opt-in skipped at an earlier
release was invisible forever. It now prints an opt-ins summary and one NOT SET
line per unadopted opt-in, each with its adopting command, read from OPT_INS in
scripts/lib/opt-ins.mjs. Read-only; enables nothing.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
…nStart brief at 16384 bytes

render (/pm:status) and upgrade print two lines: the managed rules block on disk and the SessionStart
brief, in bytes and estimated tokens. The brief hook now sends at most 16384 bytes: head, a visible
truncation marker naming PROJECT.md, and the closing reminders. rules --sections prints the ranked
per-section sizes. docs/rules-block-session-cost.md ranks the block's sections; no block content moved.

Twin-Unchanged: conductor-31 — adds one row to the valueless-flag closed list asserted only in the functional file; the unit twin asserts nothing about that list
Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
… on disk and no passing Gate 1

set-active, update-epic --status active, and add-epic/add-many creating at
active now refuse; push/pop-detour warn. New --gate1-waived "<why>" (inverse:
--clear gate1-waived). Pre-propose, already-active and stale-pass are not refused.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
Twin-Unchanged: conductor-13 — adds one flag row to the documented-flag exercise table and one ctt fixture status; the assertion twin's subject is untouched
Twin-Unchanged: output-text-integrity — adds one poison recipe for the new flag; the twin's subject is untouched
New gate-1-verdict-stale check reuses artifactStaleness; count line prints at
zero. Layer B builder and day-one record entry added.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
Twin-Unchanged: emitted-invocations — adds one Layer B builder for the new check; the assertion twin's subject is untouched
# Conflicts:
#	scripts/test/sweeps/output-interpolations.judged.mjs
set-review-mode needs --mode and set-tracker needs --system; the registry
printed bare positionals. Every OPT_INS row now carries a runnable example
that a test runs against the engine. A claim is an epic's temporary hold,
not a repo setting, so its NOT SET row (true on every upgrade) is removed.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
Spec-only: opt-in per-epic token ledger read from local Claude Code
transcripts (spend-ledger), plus the conductor-record detached-HEAD table
delta for the new .conductor/spend/ write sites.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
Cross-spec review of release 0.52.0 (spend-ledger x conductor-record):
- spend-ledger said "HEAD is detached" where conductor-record binds the
  MAIN working tree's HEAD; a detached linked worktree read two ways.
  New requirement pins detachment to the main tree.
- set-spend-tracking's own timeline entry was not an enumerated write
  site; suppressing it while state.json flips would credit off-period
  spend to an epic. The switch is now refused in a detached main tree.
- Timeline SHALL-append now names the conductor-record suppression.
- The derived-data row names --rebuild and --clear as writers too.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
… block

The tail-cut ate UNGATED/WITHDRAWN ARCHIVES, the spec-sync block, HANDOFFS and
TRACKER SYNC because RELEASES alone is ~14 KB, and its marker pointed at a
PROJECT.md that never holds the spec-sync block and reported the total as
bytes over. fitBrief() shortens only RELEASES, DISPOSITIONS, GATE REVIEWS,
HANDOFFS and EPIC LINKS (each ends in a '+N more - see ...' line), places them
after every obligation block, trims further before ever cutting one, and sends
obligations over the cap with a marker saying so. The PreCompact snapshot is
unchanged.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
…link, not its id alone

An epic whose change directory has a different name (gh#200) was read as
pre-propose and never refused. gate1ActivationGap now resolves the change
through changeNamesClaimedByArtifacts, and add-many and update-epic pass the
artifact paths they are setting.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
… the lane the call sets

--lane openspec --status active (and --lane openspec --gate1-waived) on an
epic in another lane was judged against the lane being left.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
…he call-site sweep

Tasks 1.1-1.4 and 7.1/7.3 of token-spend-tracking-per-epic. RED/GREEN in one commit:
the failing run is openspec/changes/token-spend-tracking-per-epic/red-1.1-1.3.txt.

- KNOWN_SPEND_PHASES declared once in constants.mjs.
- set-spend-tracking on|off (state.spendTracking, enabledAt never moves), its OPT_INS
  row, USAGE/positional/FLAGLESS/VERB_EFFECTS rows, commands/spend.md and its parity
  ledger claim.
- .conductor/spend/ added to ENGINE_IGNORED (#106) and to this repo's .gitignore.
- call-site-sweep-1.4.txt; judged interpolations for the new verb.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
Twin-Unchanged: verb-surface — registry rows only (a DISPATCH_BASELINE entry for the new verb); no assertion of the twin changes
cfdude added 25 commits October 9, 2026 15:44
…job roles with built-in default tiers

Adds plan, mechanical and rudimentary to KNOWN_JOB_ROLES with per-role default
pairs (plan opus:high, implement sonnet:medium, test sonnet:low, review opus:high,
mechanical sonnet:low, rudimentary haiku), resolved at read time and never stored.
One-shot Fable build is an epic-layer implement override. Gate 1 (one opus
reviewer) returned 3 Critical / 4 Important; all fixed in this commit.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
…lly, stuck-loop signal, token ceiling and escalation

New capability spend-guard. Gate 1 (one opus reviewer) returned 1 Critical and 5 Important; all fixed:
guard state read from the invoking tree, ledger rounds counted since the last pass, a pass spends a
rounds override, overrides must rank at or above the escalation target (user after one model
override), testable brief/hook scenarios, a degraded not-read path instead of exit 1.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
…ew (lastPassAt anchor, default-sourced escalation, stale-ledger reads)

- gateRounds gains lastPassAt (single writer: record-gate-review); the pass anchor is the latest
  across gates and drives the ledger-side reset and override liveness. A withdrawn pass still anchors.
- Escalation climbs from the resolved model whatever its source (default included); user only at
  fable or outside the ladder.
- Held ingest lock / detached HEAD / --no-ingest read the existing ledger, flagged possibly stale;
  not-read only when the ledger truly cannot be read.
- mainState() split into a non-refusing reader for spend-guard; tracking reads unknown, never exit 1.
- Minor: distinct-value wording, `:` deferral format, ## Spend guard placement after ## Spend tracking.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
…air per role

KNOWN_JOB_ROLES gains plan, mechanical and rudimentary; DEFAULT_ROLE_MODELS
holds one default pair per role, applied at read time (source `default`) and
never stored. The clear-model flag help, the --clear model note and the rules
block (dispatch line, resolution sentence with every default, one-shot
sentence) render from the constants. Fixtures regenerated deliberately.
RED run: openspec/changes/execution-profile-planning-and-light-roles/red-1.txt
(tasks 1.1-1.4).

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
…s-never-stored and doc-drift tests

Command docs (profile, init, epic, spend), README, conductor skill and the
changeset fragment name the six roles, the default table, and the one-shot
implement=fable override; /pm:init recommends accepting the defaults. New
assert tests: no verb writes a default (2.1), docs name every role and
default pair derived from the constants (3.2), init wording (4.5 rewrite).
Call-site sweep saved as call-site-sweep-5.1.txt. Rules-block growth: the
pinned Execution profile section 2020 -> 2488 bytes (+468) with two roles
overridden; the brief prints no line for a default (unit-tested), so its
size is unchanged.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
… plan is a role (execution-profile-planning-and-light-roles 1.1)
…sts able to fail

The unset scenario never ran an unset; the every-layer scenario only parsed; the doc-drift test
matched common words. Now: unset is run at project, lane and epic (--unset model:<role>,
--unset model, --clear-model, --clear model) and asserts source default + built-in value; a new
role is written at all three layers and round-trips the resolver; the doc check matches generated
role lists and per-role default rows exactly, in every doc that states them, with mutation
proofs; --clear model asserts every role resolves default. A file-rung twin runs the real verbs.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
…override-spend-guard (spend-budget-and-stuck-loop-guard 1.1-5.5)

Every accepted record-gate-review now keeps epic.gateRounds (fails, passes, failStreak, lastVerdictAt, lastPassAt) in
the same write. set-spend-guard configures maxFailedRounds / tokenCeiling / warnAt at the project, a lane or an epic
(epic > lane > project, every field with an --unset). spend-guard <epic> compares the rounds count (recorded fail streak,
or the distinct phase=review ledger rounds since the pass anchor, whichever is larger) and the attributed token total with
those limits and exits 2 after a STOP block that names the escalation target. override-spend-guard records the escalation
(only while tripped, at or above the target; --revoke keeps the record). The brief carries one STOP line, record-gate-review
prints the STOP on stderr after the success line, and the rules block gains a ## Spend guard section only while a limit is
configured. No limit is on by default; no hook reads the ledger.

red-1.1.txt is the saved RED run for the tally (task 1.1). The later groups were built as one unit and their tests were
written after the implementation draft, so no RED runs were saved for them.

Twin-Unchanged: verb-surface — DISPATCH_BASELINE and POSITIONAL_COUNTS rows for three new verbs; the twin asserts the same rules over its own corpus
Twin-Unchanged: conductor-31 — VERB_BASELINE, BASELINE_PRE and the closed valueless list gain the new verbs' rows; the twin asserts the same rules
Twin-Unchanged: emitted-invocations — one rules-block render, one PRINTER_FIXTURES entry; the printed-invocation rule itself is unchanged
Twin-Unchanged: output-text-integrity — nine POISON_RECIPES rows for the new flags; the poison rule itself is unchanged
Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
…exits 11, the engine-wide code (Gate 2 minor)
… tasks; route lessons

Tick the tasks the record and git evidence support (Gate 1/2, cross-spec, attribution, README,
dogfood runs), save commit-verification-7.2.txt for spend-budget-and-stuck-loop-guard, and carry
the Mintlify, re-record and disposition tasks to the release cut. Two process lessons (gates
recorded back to back; a spec edit staling a recorded Gate 1 until the record is committed) and
one practice sentence in the release-candidate skill (commit conductor records before dispatching
worktree agents).

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
…file-planning-and-light-roles, spend-budget-and-stuck-loop-guard

Specs synced: spend-ledger (new), spend-guard (new), conductor-record (1 modified),
execution-profile (3 added, 4 modified). Archive move only; not attributed to any epic.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
…he ten non-openspec members as delivered

Mintlify, README and Changelog carried to release-0-52-0-cut. Deferrals: record-spend verb and
rows.jsonl rotation declined; per-epic failing-certify detection declined; the archive-time
freeze, the tier-mismatch flag and the phase=build fix-round signal held by
token-spend-analysis-and-recommendations.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
Three assertion-half tests wrote ingest.lock with a real-time mtime; under the CI +400d clock
shift the engine's Date.now() made it 400 days old, so it was broken as stale instead of
reporting busy. The tests now utimes the lock from Date.now(). Engine unchanged.

Claude-Session: https://claude.ai/code/session_01D9yCR4qSsVzzoaHepAeTUW
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CI's TruffleHog (3.96.0) reports a pytest function name as a VERIFIED Lob API key

1 participant