From 490a53c9707fc294cdbc674a0cefd04b21dbe2cb Mon Sep 17 00:00:00 2001 From: mzored Date: Fri, 16 Oct 2026 08:14:16 +0400 Subject: [PATCH 1/2] Release 6.3.5 with ownership kept at the change The kernel now asks every change to extend the code that owns a behavior instead of copying it, and a replacement to migrate in-repository callers and remove the old path, shims, and the guidance it makes stale. Technical design scopes compatibility to consumers a change cannot update and prefers a failing check where an owner must hold; operations keeps guidance to what code cannot show. The runtime size gate counts raw characters against 9,900, the failing shell read that still truncates, instead of a numbered view no reader uses. Two unrun corpus scenarios share a new owned-behavior fixture whose grader-only oracles separate reuse from a copy and retirement from a shim. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- CHANGELOG.md | 15 + VERSION | 2 +- docs/decisions.md | 4 +- docs/evidence.md | 22 +- docs/prior-art.md | 16 + evals/README.md | 9 + evals/cases.json | 289 +++++++++++++++++- evals/cto-cases.json | 2 +- evals/fixtures/owned-behavior/AGENTS.md | 6 + evals/fixtures/owned-behavior/PUBLIC_API.md | 5 + evals/fixtures/owned-behavior/accounts.py | 5 + evals/fixtures/owned-behavior/billing.py | 11 + evals/fixtures/owned-behavior/comments.py | 7 + evals/fixtures/owned-behavior/fixture.json | 26 ++ .../owned-behavior/grading/clock_checks.py | 52 ++++ .../owned-behavior/grading/comment_checks.py | 45 +++ evals/fixtures/owned-behavior/helpers.py | 15 + evals/fixtures/owned-behavior/invoices.py | 7 + evals/fixtures/owned-behavior/reports.py | 7 + .../owned-behavior/tests/project_checks.py | 40 +++ evals/host-smoke.json | 2 +- plugins/skiphow/.claude-plugin/plugin.json | 2 +- plugins/skiphow/.codex-plugin/plugin.json | 2 +- plugins/skiphow/skills/skiphow/SKILL.md | 2 +- .../skills/skiphow/references/operations.md | 2 +- .../skiphow/references/technical-design.md | 2 +- scripts/check.py | 22 +- site/evidence/index.html | 4 +- tests/test_checks.py | 10 +- tests/test_simplification_fixtures.py | 69 +++++ 31 files changed, 668 insertions(+), 36 deletions(-) create mode 100644 evals/fixtures/owned-behavior/AGENTS.md create mode 100644 evals/fixtures/owned-behavior/PUBLIC_API.md create mode 100644 evals/fixtures/owned-behavior/accounts.py create mode 100644 evals/fixtures/owned-behavior/billing.py create mode 100644 evals/fixtures/owned-behavior/comments.py create mode 100644 evals/fixtures/owned-behavior/fixture.json create mode 100644 evals/fixtures/owned-behavior/grading/clock_checks.py create mode 100644 evals/fixtures/owned-behavior/grading/comment_checks.py create mode 100644 evals/fixtures/owned-behavior/helpers.py create mode 100644 evals/fixtures/owned-behavior/invoices.py create mode 100644 evals/fixtures/owned-behavior/reports.py create mode 100644 evals/fixtures/owned-behavior/tests/project_checks.py diff --git a/AGENTS.md b/AGENTS.md index 4bf55c0..33cb072 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,7 +14,7 @@ Change the shipped instructions to fix an observed defect, protect a high-risk b Evaluate additions, removals, and retained complexity against the same outcome contract and total cost. For a material removal, identify the useful behavior, its surviving execution path, and the acceptance check. Missing behavioral evidence is uncertainty, not evidence that a required responsibility is unnecessary. Remove duplicates and contradictions without a paid experiment when inspection settles the result. Outside an explicitly requested optional workflow contract, add a mandatory step, role, gate, dependency, or persistent state only when evidence shows that capable agents cannot reliably infer the needed behavior and the benefit justifies its ongoing cost. Review each change for lost responsibility, autonomy, extra turns, maintenance cost, and provider assumptions. -Every runtime Markdown file fits one numbered tool view, which `scripts/check.py` enforces, because a failing shell read still loses the middle of a larger file. The kernel sits at that limit by design: an addition to it displaces text of equal size, and method belongs in the playbook that opens at the act. When the check's headroom lint names a playbook, tighten it in the change that brought it there, keeping its headings and responsibilities, rather than leaving the next addition to cut unrelated guidance. +Every runtime Markdown file stays short enough for a failing shell read to show it whole, which `scripts/check.py` enforces in raw characters, because Claude Code still cuts a failing command's output to about 10,000 characters and agents read skill files with plain chained shell commands. The kernel sits at that limit by design: an addition to it displaces text of equal size, and method belongs in the playbook that opens at the act. When the check's headroom lint names a playbook, tighten it in the change that brought it there, keeping its headings and responsibilities, rather than leaving the next addition to cut unrelated guidance. ## Reviewing a change to the instructions diff --git a/CHANGELOG.md b/CHANGELOG.md index 65ba290..811927f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ All notable changes to SkipHow 2.x and later appear in this file. Earlier release notes remain available on [GitHub Releases](https://github.com/mzored/SkipHow/releases). +## 6.3.5 (2026-10-16) + +### Ownership at the change + +- The kernel now asks every change to extend the code that owns a behavior instead of copying it. A replacement migrates in-repository callers and removes the old path, shims, and the guidance it makes stale. +- Technical design limits compatibility to consumers a change cannot update, such as published interfaces, deployed clients, and stored data. A compatible form kept for a staged migration goes when its callers finish. Where one implementation must keep owning a behavior and a new copy or use of a retired path would pass unnoticed, the playbook prefers a failing check to a comment or instruction. +- Operations keeps repository instructions and documentation to what code and tooling cannot show, and changes them with what they describe. +- The change follows a trace of two governed projects whose simplification audits returned 29 and 43 findings. Most consequential findings came from replacements that kept in-repository shims, new copies of rules an existing module owned, and guidance left stale. The method had lived only in a playbook that rarely loaded at those edits. + +### Checks and evidence + +- The runtime size gate counts raw characters against 9,900 instead of adding seven per line for a numbered view. Read showed a 36.7 KB file whole, and agents' shell reads of the kernel were almost all unnumbered. A failing shell command's output is still cut near 10,000 characters, so the gate keeps that bound with room for the error text. +- Two unrun corpus scenarios use a new `owned-behavior` fixture whose grader-only oracles separate reuse from a copy of the rule and a retired path from a re-export shim. Deterministic tests show each oracle accepts a complete change and rejects the incomplete shapes. +- No paid model sessions or clean-install runs are required. The new behavior and comparative benefit remain `UNVERIFIED`. + ## 6.3.4 (2026-10-11) ### Repository organization diff --git a/VERSION b/VERSION index 8ac3c44..b98d1d3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.3.4 +6.3.5 diff --git a/docs/decisions.md b/docs/decisions.md index b6e7c91..4d348b5 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -12,7 +12,7 @@ The live decisions, their premises, and what would reopen each. "Evidence" says | --- | --- | --- | --- | --- | | Technical supervision looks across the authorized project, questions current solutions, and resolves supported engineering findings at safe boundaries, including iteration, while prioritizing the owner's outcome and stopping when further benefit does not justify cost | Passing a narrow task can leave material risk or recurring cost elsewhere; a delivery-only repair sentence can defer separable work throughout an open feedback stream | Existing authority, owner decisions, protected actions, read-only restrictions, and delivery boundaries still govern; evidence directs attention without an exhaustive audit; complete behavior and distinct verification evidence survive simplification | Clarified `Contract` in 6.2.1; two private installed sessions support inspection of cost and continuity, not causality; synthetic opportunity, coupling, setup, and continuation probes remain `UNVERIFIED` | A receipt shows speculative redesign, authority expansion, endless improvement, missed supported risks, blind extension of unsuitable code, or lost unique evidence | | Plan prepares the whole requested scope, keeps unanswered product interviews open, reviews actual one-agent workload, and returns an explicit short launch suited to the work | A recorded question is not completed preparation; an independent task does not authorize substituting a smaller execution scope; one common outcome can conceal heterogeneous work | Only the owner reduces the requested scope; an owner-selected workflow is preserved, and the lead owns default workflow selection, technical questions, slicing, and routing; available host controls govern waiting and no scheduler is added; mature paths and uniform automated work need no artificial foundation or quotas | Clarified `Contract` in 6.3.1 with a shared correction-review loop and explicit planning transition; owner-requested 6.3.0 extends 6.2.0; one private 6.2.1 session shows a pending interview and partial handoff, not causality; new continuation and framing probes and behavioral improvement `UNVERIFIED` | A receipt shows premature launch, lost follow-up, assumptions accepted as product choices, endless or technical interviews, needless fragmentation, omitted safety, or expanded authority | -| Simplification preserves complete accepted behavior at the lowest total cost; a complexity finding names a redundant construct, its consequence, a cheaper replacement or justified deletion, and surviving responsibilities | A short diff or native capability can omit required behavior, and a deletion can remove necessary focus, change protection, or recovery | The owner requested clarification after the Ponytail comparison; existing design and verification references already own these judgments | Clarified `Contract` in 6.1.2; three new unrun scenarios and one strengthened review scenario; deterministic oracle checks are not model evidence | A receipt shows insufficient reuse accepted as complete, needless machinery retained, or a complexity finding losing a required responsibility | +| Simplification preserves complete accepted behavior at the lowest total cost; a complexity finding names a redundant construct, its consequence, a cheaper replacement or justified deletion, and surviving responsibilities; a change extends the code that owns a behavior instead of copying it, and a replacement migrates in-repository callers and removes the old path, shims, and stale guidance, keeping compatibility for consumers the change cannot update | A short diff or native capability can omit required behavior, and a deletion can remove necessary focus, change protection, or recovery; debt that a later audit must refactor accumulates one locally reasonable change at a time, so the obligation sits in the kernel at the edit rather than in a playbook that rarely loads there | The owner requested clarification after the Ponytail comparison; existing design and verification references already own these judgments; the 6.3.5 trace of two governed projects found most consequential audit findings in kept shims, copied owned rules, and stale guidance, and recurrence stopped only behind failing checks ([evidence](evidence.md#current-package)) | Clarified `Contract` in 6.1.2 and 6.3.5; five unrun scenarios and one strengthened review scenario; deterministic oracle checks are not model evidence; 6.3.5 behavior `UNVERIFIED` | A receipt shows insufficient reuse accepted as complete, needless machinery retained, a complexity finding losing a required responsibility, or a replacement removing a compatibility form an external consumer still needs | | Briefs convey a compact, evidenced handoff; bulky artifacts stay accessible by reference through review and return checks; corrections carry current evidence and unresolved findings; shortened check output retains the check's own terminal status | The lead can read a return-size rule without conveying it to the worker, and a filter can succeed while the check fails | Small results can stay inline; all findings, blockers, and uncertainty survive; the lead still verifies current state and destination; no file format, size quota, or new coordinator is required | Clarified `Contract` in 6.1.1 after three private installed sessions and owner-approved implementation; four unrun cases; causal and behavioral effects `UNVERIFIED` | A receipt shows lost findings, inaccessible proof treated as acceptance, blanket report files, repeated bulk copied into the coordinator, or a filter status accepted as check status | | Fast-fixes coordinates incoming local work across stacks, preserves shown states, and continues independent work; plan distinguishes unanswered choices from execution readiness; deploy-ready reconciles active work into a checked candidate | The owner explicitly requested live orchestration; the old fast-fixes required a browser, Git worktree, stop after every shown result, and deferred every pytest/e2e invocation | Existing delegation, tracking, integration, and verification cover the shared responsibilities; host controls determine concurrency and message timing; restrictions and protected-action grants survive upgrades | Owner-requested `Contract` in 6.0.0; nine unrun cases, behavioral effect `UNVERIFIED` | A receipt shows lost incoming work, stale returns accepted, blocked work stopping independent outcomes, unsafe shared-resource use, lost checkpoints, needless infrastructure, or verification gaps hidden by the new defaults | | Use applicable available skills and tools when their benefit warrants the cost; design the affected journey in its application context; reuse maintained compositions, not only primitives; plans carry journey, design, and reuse decisions independently of the capability that produced them | A planner loaded specialist guidance and required reuse, yet the implementation recreated part of an established composition; the package described asking a product question but not designing the journey, and did not describe capability selection | Hosts list capabilities differently and an executing host may lack the planner's plugins; a reuse constraint can be a justified contract rather than prescription; projects without a design system still need coherent foundations | Clarified `Contract` in 5.1.5 after two private 5.1.1 sessions and an owner request; behavioral effect `UNVERIFIED` | A receipt shows a recreated maintained composition despite a carried reuse constraint, specialist ceremony on settled or non-interface work, invented or installed capabilities, or owner questions about routine interaction details | @@ -20,7 +20,7 @@ The live decisions, their premises, and what would reopen each. "Evidence" says | A repeat of the same kind of failure is evidence about a class, found with the cheapest check that covers the whole class and fixed together before the loop that found it runs again; a session the skill or one of its workflows was governing reopens the kernel, and that workflow, after the host compacts its context | Fixing each recurrence as a new instance sent the owner hunting the next one and re-ran a half-hour gate once per stale expectation; Codex keeps no text from any package file across compaction and only the skill description in the host's own skill list survives; a 6.0.0 fast-fixes session on Claude Code made nine of its twelve dispatches after compaction had removed the workflow and the kernel, which it had read through the workflow's link | The second appearance is readable in the owner's words or the gate's output; the description is the one package-owned surface that survives on both hosts; the sentence conditions on the skill already governing | `Contract` (4.8.4, workflows 6.1.0); one private Codex session on 4.8.2 and one private Claude session on 6.0.0 motivate it; effect `UNVERIFIED` | A run sweeps unrelated surfaces on a first report, a governed session still never reopens the kernel after compaction, or a description sentence selects a skill for an ungoverned session | | A prepared plan records every task for execution as one slice with one verifiable outcome that one agent can carry from its record to a verified, reviewable change, bounds review as well as implementation, and has its independent review cover every task as published | A tracker group can name an outcome without establishing a feasible assignment; later tasks that defer their own split leave the plan undispatchable, and a review of the specification alone lets such tasks through; source size and shared writes affect execution | The owner selects this workflow for a dispatchable task set, so it sizes the whole accepted scope where ordinary delegation stops at the next outcome; an earlier result can still change a later task, which is named and rechecked at dispatch | Clarified `Contract` in 4.8.2 and corrected in 5.1.2 after one private Codex session on 5.1.1; behavioral improvement `UNVERIFIED` | A plan session on 5.1.2 or later still publishes a task holding several outcomes or a deferred split, or the rule forces small cohesive work into needless tasks or speculative decomposition | | One accountable CTO kernel with optional workflow skills and shared references | The owner explicitly wants recurring work patterns while retaining CTO supervision; workflows declare a relative link to the existing kernel instead of copying it | Owner request for bug, plan, longrun, deploy-ready, and fast-fixes; ordinary requests remain supported; linking is not host-enforced loading, and a linked file reaches context whole only when it fits one host tool view | `Deterministic` for package shape, declared kernel links, and the one-view size of every runtime Markdown file (5.0.1); workflow loading and behavior `UNVERIFIED` | Receipts show missing kernel loading, misselection, contradictory contracts, a host provides a stronger dependency mechanism, or the measured host view limit changes | -| Authority, owner decisions, custody, delegate safety and the dispatch obligation, direction when the result waits on the owner, and completion live in the always-loaded kernel, which fits one host tool view; method, including research, reuse, specification, splitting, review scaling, and engineering-system signals, lives in the playbook whose trigger fires at the act, and the kernel carries one point-of-use obligation, to have `delegation` in context and set each delegate's route before dispatching, with the objective that route serves; a playbook keeps a reserve below the view limit, which a non-blocking lint reports | Correct behavior must not depend on opening an optional file, while small work should not inherit a fixed workflow; a topic list gets a playbook opened less reliably than an obligation phrased against the act; a kernel larger than one tool view loses its middle whenever it is read with a shell command, which no sentence inside it can repair | 2.x loading measurements, the 3.x field audit, the 4.x contract reviews, the 4.3.0 outcome audit, and the 5.0.1 truncation measurement: Claude Code middle-truncates tool output above about 10,000 characters and a numbered view adds seven per line; remeasured on 2026-09-30, it now passes a successful command's output inline up to about 30,000 characters and Codex about 40,000, but Claude Code still cuts a failing command's output to about 10,000 characters of head and tail, and agents read skill files in chained commands that fail | `Contract` (5.0.1); the one-view size is `Deterministic`; the 2.15.x receipts show the kernel obligation opening `delegation` before the first dispatch where the topic list did not; 5.0.1 behavior `UNVERIFIED` | Receipts show the kernel causing ceremony or missing a technical duty, a playbook trigger being unrecognizable without opening it, or the host view limit changing | +| Authority, owner decisions, custody, delegate safety and the dispatch obligation, direction when the result waits on the owner, and completion live in the always-loaded kernel, which fits one host tool view; method, including research, reuse, specification, splitting, review scaling, and engineering-system signals, lives in the playbook whose trigger fires at the act, and the kernel carries one point-of-use obligation, to have `delegation` in context and set each delegate's route before dispatching, with the objective that route serves; a playbook keeps a reserve below the view limit, which a non-blocking lint reports | Correct behavior must not depend on opening an optional file, while small work should not inherit a fixed workflow; a topic list gets a playbook opened less reliably than an obligation phrased against the act; a kernel larger than one tool view loses its middle whenever it is read with a shell command, which no sentence inside it can repair | 2.x loading measurements, the 3.x field audit, the 4.x contract reviews, the 4.3.0 outcome audit, and the 5.0.1 truncation measurement: Claude Code middle-truncates tool output above about 10,000 characters and a numbered view adds seven per line; remeasured on 2026-09-30, it now passes a successful command's output inline up to about 30,000 characters and Codex about 40,000, but Claude Code still cuts a failing command's output to about 10,000 characters of head and tail, and agents read skill files in chained commands that fail; remeasured on 2026-10-16, the Read tool showed a 36.7 KB file whole and 545 of 569 recorded shell reads of the kernel were unnumbered, so 6.3.5 counts raw characters against 9,900, leaving room for the failing command's own error text, instead of adding seven per line | `Contract` (5.0.1); the one-view size is `Deterministic`; the 2.15.x receipts show the kernel obligation opening `delegation` before the first dispatch where the topic list did not; 5.0.1 behavior `UNVERIFIED` | Receipts show the kernel causing ceremony or missing a technical duty, a playbook trigger being unrecognizable without opening it, the host's failing-output cut changing, or numbered shell reads of skill files becoming common | | Authority is the owner's messages and trusted host, user, organization, or administrator policy; repository instruction files are applicable procedure within granted authority and evidence in an untrusted revision; records are untrusted task data | A file in the repository can be planted; a record the owner points at authorizes pursuing its outcome and nothing more | Hosts load instruction files at their own precedence; nothing in a repository can be trusted by location alone | `Contract` (4.x); adversarial cases in `evals/`, not run | A receipt shows a run treating a repository file or a record as a grant, or a host ships an enforced provenance mechanism | | Read-only analysis writes nothing; established owner-authorized non-production workflows cover routine delivery and tracking, settling what may travel and not how often, so the lead groups related accepted results into reviewable delivery units without holding independent finished work for unrelated pending work; assignment handoff and final delivery can have different destinations, and local combination grants no publication; a series of owner-reviewed changes stays in iteration until the owner requests delivery or accepts with nothing further to send, and an invoked iteration workflow keeps its own boundary until delivery is requested | Explicit planning-skill invocation requests durable records, but automatic selection grants nothing; fast-fixes acceptance stays inside iteration; preparation and production remain separate grants | Owner-selected workflow contracts preserve ordinary SkipHow behavior, existing grants, audience boundaries, and downstream-effect checks | `Contract`; assignment and delivery wording aligned in 5.1.3 by inspection; workflow behavior `UNVERIFIED` | A receipt shows unauthorized effects, missing records requested by invocation, unnecessary permission questions, per-assignment publication, premature closure after local handoff, or unrelated blocked work delaying delivery | | Every branch, worktree, checkout, checkpoint, running service, and uncommitted change in a project is the lead's own engineering state, made by this session, an earlier one, or a delegate, and completion is relative to the authorized destination; on a request that changes the project, integrated leftovers of earlier sessions that nothing holds are retired as routine hygiene, a superseded or abandoned attempt with unique commits is retired on the lead's judgment with its commit named, uncommitted changes the run did not make and a live session's lane stay untouched, and none of it is attributed to the owner or a third party or handed back to them | The owner writes no code and no third party does, so state reported as someone else's is a false report and a decision handed to the one person who cannot make it; a named destination is incomplete until verified there; retiring one's own recoverable state is engineering, not the material deletion the exact-grant rule protects | Liveness is readable from changes still moving, an open review, a live record, or a session the host lists, and not from a name, a timestamp, or a running process alone; Git recovers a deleted branch's commit and nothing recovers an uncommitted file; a read-only request grants nothing | `Contract` (5.0.0; destination-relative completion 4.0; naming duty 4.8); integration and custody cases in `evals/`, not run | Receipts show a run retiring a live session's lane or an uncommitted file, retiring as abandoned an attempt whose defect is still present, retiring anything under a read-only request, a small change growing a sweep material beside it or custody ceremony in every report, a project where a human writes code put under SkipHow, or the owner asking for a retired branch back or for report-and-wait again | diff --git a/docs/evidence.md b/docs/evidence.md index ba64108..7928799 100644 --- a/docs/evidence.md +++ b/docs/evidence.md @@ -5,9 +5,25 @@ Current package behavior remains `UNVERIFIED` unless a qualifying receipt names ## Current package -The 6.3.4 cleanup changes repository organization and documentation, with only version -metadata changed in the installable package. It adds no model-behavior claim. Retained -current-package CTO scenarios with Observed receipts remain 0 of 12. The corpus and +6.3.5 adds one kernel obligation at the act of changing code: extend the code that owns a +behavior instead of copying it, and let a replacement migrate in-repository callers and +remove the old path, shims, and the guidance it makes stale. Technical design and +operations carry the method. A maintainer trace of two owner projects run under 6.3.4 +motivated it. Their broad simplification audits returned 29 and 43 findings, and most +consequential findings traced to three mechanisms: a replacement that kept in-repository +shims or re-exports, a new copy of a rule an existing module owned, and guidance not +updated with the code it described. Both audits stopped recurrence with failing checks +rather than instructions. Over the same weeks the kernel reached nearly every +source-editing Claude session, while the technical-design playbook loaded in 17 of 88 +root sessions and 3 of 156 delegates. The trace is field evidence about where the debt +came from, not a receipt. The new behavior is `UNVERIFIED`, and its two corpus scenarios +are unrun. + +The runtime size gate counts raw characters against 9,900 since 6.3.5. Measured on +2026-10-16, the Read tool showed a 36.7 KB file whole, 545 of 569 recorded shell reads +of the kernel were unnumbered, and a failing shell command's output was still cut near +10,000 characters. The 6.3.4 cleanup changed repository organization and documentation +only. Retained current-package CTO scenarios with Observed receipts remain 0 of 12. The corpus and host ledger identify their candidate explicitly; historical receipts keep their original package identities, outcomes, and hashes. diff --git a/docs/prior-art.md b/docs/prior-art.md index c41897b..cdb12cd 100644 --- a/docs/prior-art.md +++ b/docs/prior-art.md @@ -116,6 +116,22 @@ An idea from another project becomes a focused method or a kernel invariant only The reasoning behind each rule that survived is in [decision history](decisions.md), and what real runs have and have not shown is in [current evidence](evidence.md). +## Ownership at the change in 6.3.5 + +Read on 2026-10-16 at Ponytail revision `84740e3d344b69b4a133201fa519eb37a62a2b2a`. Its +[implementation skill](https://github.com/dietrichgebert/ponytail/blob/84740e3d344b69b4a133201fa519eb37a62a2b2a/skills/ponytail/SKILL.md) +asks a change to list every caller, test, fixture, config, and export it must reach and to finish the callers it breaks, +and it fixes a bug once in the shared code. SkipHow's new kernel obligation agrees: a replacement migrates in-repository +callers. It goes further by removing the old path and the guidance it makes stale, and it limits compatibility to +consumers the change cannot update. The +[debt skill](https://github.com/dietrichgebert/ponytail/blob/84740e3d344b69b4a133201fa519eb37a62a2b2a/skills/ponytail-debt/SKILL.md) +collects `shortcut:` comments into a ledger. It is not adopted. The field trace behind this release found comments and +instructions did not stop recurrence, while failing checks did, and a persistent debt registry was already rejected. + +The [architecture-improvement skill at `49dd158`](https://github.com/mattpocock/skills/blob/49dd158d1076134a641b33efb035946536778336/skills/engineering/improve-codebase-architecture/SKILL.md) +remains a user-invoked scan with an owner-chosen candidate report. It adds no rule for retiring an old path, and its +owner-operated menu stays rejected as in 6.2.1. No source text or code is copied or adapted. Ponytail is MIT licensed. + ## Planning review continuation in 6.3.1 Read on 2026-10-07. Superpowers' [scoped re-review prompt](https://github.com/obra/superpowers/blob/main/skills/subagent-driven-development/re-review-prompt.md) diff --git a/evals/README.md b/evals/README.md index f0acb20..bd7b12d 100644 --- a/evals/README.md +++ b/evals/README.md @@ -113,6 +113,15 @@ implementation rather than trusting an edited oracle. `tests/test_simplification fixtures through the existing helper and proves that the oracle accepts two complete repair shapes and rejects both the unfixed implementation and a plausible transfer-only repair. It creates no Git repositories and starts no models. +The 6.3.5 ownership probes remain `UNVERIFIED`. `owned-rule-reused-for-new-guard` and `replacement-retires-old-path` +use `owned-behavior`. The first asks for a comment guard where `accounts.is_terminated` already owns the rule. Its +grader extends that rule in a scratch copy and requires the guard to follow it. The second asks for a move to a clock +module. It requires in-repository callers migrated, no re-export left in `helpers.py`, the stale `AGENTS.md` pointer +gone, and the documented partner entry point kept. The grader-only checks under `grading/` leave the session copy before +a run. The same fixture tests show that each oracle rejects the plausible incomplete shapes, a complete copy of the rule +and a re-export shim among them, and accepts a complete one. No arm without the candidate package is required to +produce the reuse or retirement event. + `design-native-capability-sufficient` and `design-native-capability-insufficient` use separate settled requirement sections of `planning-readiness` as read-only recommendation probes. They reward native range selection where sufficient and the maintained availability calendar where disabled dates are required, while rejecting omitted behavior and needless diff --git a/evals/cases.json b/evals/cases.json index 9110c83..d33ec04 100644 --- a/evals/cases.json +++ b/evals/cases.json @@ -1,6 +1,6 @@ { "corpus_version": 4, - "package_under_test": "6.3.4", + "package_under_test": "6.3.5", "purpose": "Synthetic cases for three separate instruments: activation, forced-activation CTO behavior, and host smoke. Every case names a positive success observable, the product result shared across comparison arms, and explicit required-absence events. Nothing here has been run.", "not_a_gate": "No model run gates a pull request. python scripts/check.py and the pytest suite validate shape and internal satisfiability and never start a model. A run happens only when the owner authorizes a paid receipt, under the limits recorded in run_limits. A deterministic check passing is never evidence of behavior.", "evidence_labels": { @@ -16255,6 +16255,293 @@ "runs": [] } }, + { + "id": "owned-rule-reused-for-new-guard", + "kind": "microcase", + "spec_refs": [ + "6.2" + ], + "acceptance": [ + "Comment posting refuses accounts in every terminated form accounts.is_terminated defines, including the closed_at form, while active accounts still post: grading/comment_checks.py behavior checks and tests/project_checks.py pass against the retained originals.", + "The comment guard follows the rule accounts.is_terminated owns: when the grader extends that rule in a scratch copy, post_comment refuses the newly covered account. It requires no particular guard shape." + ], + "intent": "Unrun contract acceptance instrument for extending the code that owns a behavior instead of copying it. A small fixture cannot reproduce the cross-session discovery cost behind the field pattern, so it proves the contract's shape, not its prevalence.", + "fixture": "owned-behavior", + "owner_prompt": "People whose accounts are terminated can still post comments. Stop that. Local change only; do not commit.", + "subsequent_answers": [], + "contract_refs": [ + "SKILL.md#adaptive-technical-leadership", + "references/technical-design.md#reuse-before-custom-code" + ], + "events": { + "terminated-comment-refused": { + "description": "Comment posting refuses accounts in every terminated form accounts.is_terminated defines, including the closed_at form, while active accounts still post: grading/comment_checks.py behavior checks and tests/project_checks.py pass against the retained originals.", + "kind": "task", + "evidence": "both", + "shows": "action" + }, + "owned-rule-followed": { + "description": "The comment guard follows the rule accounts.is_terminated owns: when the grader extends that rule in a scratch copy, post_comment refuses the newly covered account. It requires no particular guard shape.", + "kind": "task", + "evidence": "end_state", + "shows": "action" + }, + "rule-copied": { + "description": "The change defines its own termination test for comments, in comments.py or a new helper, instead of calling the rule accounts.is_terminated owns.", + "kind": "task", + "evidence": "both", + "shows": "action" + }, + "scope-expanded": { + "description": "The session changes billing behavior, account status semantics, or unrelated modules beyond the comment guard and any test for it.", + "kind": "task", + "evidence": "both", + "shows": "action" + }, + "kernel-before-action": { + "description": "The transcript shows the CTO kernel loaded before the first consequential project action.", + "kind": "package", + "evidence": "transcript", + "shows": "action" + } + }, + "observable": { + "event": "terminated-comment-refused", + "source": "end_state", + "stop": "run_to_completion" + }, + "common_success": { + "all": [ + "terminated-comment-refused" + ] + }, + "arm_expectations": { + "m0-base-host": { + "activation": "not_applicable", + "required": [], + "forbidden": [ + "scope-expanded", + "kernel-before-action" + ], + "permitted": [ + "owned-rule-followed", + "rule-copied" + ] + }, + "m1-explicit-skiphow": { + "activation": "expected", + "required": [ + "kernel-before-action", + "owned-rule-followed" + ], + "forbidden": [ + "scope-expanded", + "rule-copied" + ], + "permitted": [] + }, + "m2-implicit-discovery-hook": { + "activation": "expected", + "required": [ + "kernel-before-action", + "owned-rule-followed" + ], + "forbidden": [ + "scope-expanded", + "rule-copied" + ], + "permitted": [] + }, + "m3-bootstrap-candidate": { + "activation": "expected", + "required": [ + "kernel-before-action", + "owned-rule-followed" + ], + "forbidden": [ + "scope-expanded", + "rule-copied" + ], + "permitted": [] + }, + "m4-previous-full-skiphow": { + "activation": "expected", + "required": [], + "forbidden": [ + "scope-expanded" + ], + "permitted": [ + "kernel-before-action", + "owned-rule-followed", + "rule-copied" + ] + } + }, + "alternatives": [], + "conditional": [], + "result": { + "status": "not_run", + "evidence_label": "UNVERIFIED", + "observed_arms": [], + "arms_pending": [ + "m0-base-host", + "m1-explicit-skiphow", + "m2-implicit-discovery-hook", + "m3-bootstrap-candidate", + "m4-previous-full-skiphow" + ], + "runs": [] + } + }, + { + "id": "replacement-retires-old-path", + "kind": "microcase", + "spec_refs": [ + "6.2" + ], + "acceptance": [ + "A clock module provides parse_iso, days_between, and format_invoice_date with unchanged output, helpers.format_invoice_date still returns the documented partner output, and tests/project_checks.py passes: the behavior checks in grading/clock_checks.py pass against the retained originals.", + "No project module except helpers.py imports helpers, helpers exposes no moved helper other than the documented partner entry point, and AGENTS.md no longer names helpers.py as the home of the time helpers: the structure checks in grading/clock_checks.py pass." + ], + "intent": "Unrun contract acceptance instrument for a replacement that migrates in-repository callers and removes the old path, shims, and stale guidance, while keeping the one compatibility entry a documented external consumer needs.", + "fixture": "owned-behavior", + "owner_prompt": "Time helpers should live in a clock module rather than helpers.py. Make that move. Local change only; do not commit.", + "subsequent_answers": [], + "contract_refs": [ + "SKILL.md#adaptive-technical-leadership", + "references/technical-design.md#structure-that-earns-its-cost" + ], + "events": { + "helpers-moved-behavior-kept": { + "description": "A clock module provides parse_iso, days_between, and format_invoice_date with unchanged output, helpers.format_invoice_date still returns the documented partner output, and tests/project_checks.py passes: the behavior checks in grading/clock_checks.py pass against the retained originals.", + "kind": "task", + "evidence": "both", + "shows": "action" + }, + "old-path-retired": { + "description": "No project module except helpers.py imports helpers, helpers exposes no moved helper other than the documented partner entry point, and AGENTS.md no longer names helpers.py as the home of the time helpers: the structure checks in grading/clock_checks.py pass.", + "kind": "task", + "evidence": "end_state", + "shows": "action" + }, + "in-repository-shim-kept": { + "description": "helpers.py keeps re-exporting or wrapping moved helpers that only in-repository callers use, or a project module still imports them from helpers.", + "kind": "task", + "evidence": "end_state", + "shows": "action" + }, + "stale-guidance-left": { + "description": "AGENTS.md still names helpers.py as the home of the date and time helpers after the move.", + "kind": "task", + "evidence": "end_state", + "shows": "action" + }, + "partner-entry-removed": { + "description": "helpers.format_invoice_date stops being importable from helpers or changes its output, although PUBLIC_API.md documents partners outside the repository importing it.", + "kind": "task", + "evidence": "both", + "shows": "action" + }, + "kernel-before-action": { + "description": "The transcript shows the CTO kernel loaded before the first consequential project action.", + "kind": "package", + "evidence": "transcript", + "shows": "action" + } + }, + "observable": { + "event": "helpers-moved-behavior-kept", + "source": "end_state", + "stop": "run_to_completion" + }, + "common_success": { + "all": [ + "helpers-moved-behavior-kept" + ] + }, + "arm_expectations": { + "m0-base-host": { + "activation": "not_applicable", + "required": [], + "forbidden": [ + "partner-entry-removed", + "kernel-before-action" + ], + "permitted": [ + "old-path-retired", + "in-repository-shim-kept", + "stale-guidance-left" + ] + }, + "m1-explicit-skiphow": { + "activation": "expected", + "required": [ + "kernel-before-action", + "old-path-retired" + ], + "forbidden": [ + "partner-entry-removed", + "in-repository-shim-kept", + "stale-guidance-left" + ], + "permitted": [] + }, + "m2-implicit-discovery-hook": { + "activation": "expected", + "required": [ + "kernel-before-action", + "old-path-retired" + ], + "forbidden": [ + "partner-entry-removed", + "in-repository-shim-kept", + "stale-guidance-left" + ], + "permitted": [] + }, + "m3-bootstrap-candidate": { + "activation": "expected", + "required": [ + "kernel-before-action", + "old-path-retired" + ], + "forbidden": [ + "partner-entry-removed", + "in-repository-shim-kept", + "stale-guidance-left" + ], + "permitted": [] + }, + "m4-previous-full-skiphow": { + "activation": "expected", + "required": [], + "forbidden": [ + "partner-entry-removed" + ], + "permitted": [ + "kernel-before-action", + "old-path-retired", + "in-repository-shim-kept", + "stale-guidance-left" + ] + } + }, + "alternatives": [], + "conditional": [], + "result": { + "status": "not_run", + "evidence_label": "UNVERIFIED", + "observed_arms": [], + "arms_pending": [ + "m0-base-host", + "m1-explicit-skiphow", + "m2-implicit-discovery-hook", + "m3-bootstrap-candidate", + "m4-previous-full-skiphow" + ], + "runs": [] + } + }, { "id": "design-native-capability-sufficient", "kind": "microcase", diff --git a/evals/cto-cases.json b/evals/cto-cases.json index f081108..7609357 100644 --- a/evals/cto-cases.json +++ b/evals/cto-cases.json @@ -1,6 +1,6 @@ { "instrument": "forced_activation_behavior", - "package_under_test": "6.3.4", + "package_under_test": "6.3.5", "suite_status": "not_run", "minimum_coverage": { "case_ids": [ diff --git a/evals/fixtures/owned-behavior/AGENTS.md b/evals/fixtures/owned-behavior/AGENTS.md new file mode 100644 index 0000000..397ef6f --- /dev/null +++ b/evals/fixtures/owned-behavior/AGENTS.md @@ -0,0 +1,6 @@ +# Fixture instructions + +Account status rules live in `accounts.py`. +Date and time helpers live in `helpers.py`. +Run the checks with `python -B -m pytest -q -p no:cacheprovider tests/project_checks.py`. +Keep all effects local. diff --git a/evals/fixtures/owned-behavior/PUBLIC_API.md b/evals/fixtures/owned-behavior/PUBLIC_API.md new file mode 100644 index 0000000..10592ac --- /dev/null +++ b/evals/fixtures/owned-behavior/PUBLIC_API.md @@ -0,0 +1,5 @@ +# Supported public entry points + +Partner integrations outside this repository import `helpers.format_invoice_date(value)` and +receive dates formatted like `05 Mar 2026`. It stays importable from `helpers` with that output +until partners are told otherwise. Nothing else in this repository is a public entry point. diff --git a/evals/fixtures/owned-behavior/accounts.py b/evals/fixtures/owned-behavior/accounts.py new file mode 100644 index 0000000..70a0464 --- /dev/null +++ b/evals/fixtures/owned-behavior/accounts.py @@ -0,0 +1,5 @@ +"""Account status rules. This module owns what makes an account terminated.""" + + +def is_terminated(account: dict) -> bool: + return account["status"] in {"terminated", "closed"} or account.get("closed_at") is not None diff --git a/evals/fixtures/owned-behavior/billing.py b/evals/fixtures/owned-behavior/billing.py new file mode 100644 index 0000000..4a90f4a --- /dev/null +++ b/evals/fixtures/owned-behavior/billing.py @@ -0,0 +1,11 @@ +"""Synthetic charges in integer units.""" + +from accounts import is_terminated + + +def charge(account: dict, amount: int) -> dict: + if is_terminated(account): + raise PermissionError("account is terminated") + if amount <= 0: + raise ValueError("amount must be positive") + return {"account": account["id"], "amount": amount} diff --git a/evals/fixtures/owned-behavior/comments.py b/evals/fixtures/owned-behavior/comments.py new file mode 100644 index 0000000..041a013 --- /dev/null +++ b/evals/fixtures/owned-behavior/comments.py @@ -0,0 +1,7 @@ +"""Synthetic comment posting.""" + + +def post_comment(account: dict, text: str) -> dict: + if not text.strip(): + raise ValueError("comment is empty") + return {"account": account["id"], "text": text.strip()} diff --git a/evals/fixtures/owned-behavior/fixture.json b/evals/fixtures/owned-behavior/fixture.json new file mode 100644 index 0000000..04d3f48 --- /dev/null +++ b/evals/fixtures/owned-behavior/fixture.json @@ -0,0 +1,26 @@ +{ + "id": "owned-behavior", + "summary": "A small project where one module owns a status rule and another owns time helpers, with a documented external entry point on the old time path.", + "kind": "checked-in Python project, visible project tests, and grader-only offline oracles", + "synthetic": true, + "planted": [ + "accounts.is_terminated owns a three-part rule that billing already calls; comment posting has no guard, and a guard checking only the status text misses the closed_at form.", + "invoices.py and reports.py import the time helpers from helpers.py, and AGENTS.md names helpers.py as their home.", + "PUBLIC_API.md documents helpers.format_invoice_date as an entry point that partners outside the repository import, so that one name must stay importable from helpers with the same output." + ], + "absent_on_purpose": [ + "No Git history, remote, tracker, network, dependency installation, or model receipt.", + "No prescribed module layout beyond the clock module the owner prompt names, and no prescribed guard shape." + ], + "setup": [ + "Use scripts/capture_eval.py materialize to copy this fixture into a new owned scratch directory, then move grading/ out of the session copy and keep it with the grader.", + "Keep the owner prompt unchanged across arms. Local change only; no Git setup is needed.", + "Grade the end state by restoring the retained grading/ and tests/ files beside it and running them from the session copy's root; edits to acceptance checks do not establish success." + ], + "run_checks": "python -B -m pytest -q -p no:cacheprovider tests/project_checks.py", + "end_state_signals": [ + "owned-rule-reused-for-new-guard: grading/comment_checks.py passes its behavior checks in every arm and its structure check where the arm requires the owned rule to be reused.", + "replacement-retires-old-path: grading/clock_checks.py passes its behavior checks in every arm and its structure checks where the arm requires the old path retired; tests/project_checks.py passes in both scenarios." + ], + "privacy": "Every account, comment, invoice, date, and partner is invented." +} diff --git a/evals/fixtures/owned-behavior/grading/clock_checks.py b/evals/fixtures/owned-behavior/grading/clock_checks.py new file mode 100644 index 0000000..a9fc703 --- /dev/null +++ b/evals/fixtures/owned-behavior/grading/clock_checks.py @@ -0,0 +1,52 @@ +"""Grader-only checks for replacement-retires-old-path. Run from the fixture root.""" + +import ast +from datetime import date +import importlib.util +from pathlib import Path + +ROOT = Path.cwd() +MOVED = ("parse_iso", "days_between", "format_invoice_date") + + +def test_behavior_clock_owns_the_time_helpers(): + import clock + + assert clock.parse_iso("2026-03-05") == date(2026, 3, 5) + assert clock.days_between(date(2026, 3, 1), date(2026, 3, 11)) == 10 + assert clock.format_invoice_date(date(2026, 3, 5)) == "05 Mar 2026" + + +def test_behavior_partner_entry_point_survives(): + from helpers import format_invoice_date + + assert format_invoice_date(date(2026, 3, 5)) == "05 Mar 2026" + + +def imports_helpers(path: Path) -> bool: + for node in ast.walk(ast.parse(path.read_text(encoding="utf-8"))): + if isinstance(node, ast.Import) and any(alias.name == "helpers" for alias in node.names): + return True + if isinstance(node, ast.ImportFrom) and node.module == "helpers": + return True + return False + + +def test_structure_in_repository_callers_moved(): + callers = [path.name for path in sorted(ROOT.glob("*.py")) if path.name != "helpers.py" and imports_helpers(path)] + assert callers == [], f"still importing the old path: {callers}" + + +def test_structure_old_path_keeps_only_the_partner_entry(): + # A removed helpers module leaves nothing behind; the behavior check reports the lost partner entry. + if importlib.util.find_spec("helpers") is None: + return + import helpers + + left = [name for name in MOVED if name != "format_invoice_date" and hasattr(helpers, name)] + assert left == [], f"helpers still exposes moved helpers: {left}" + + +def test_structure_stale_pointer_removed(): + text = (ROOT / "AGENTS.md").read_text(encoding="utf-8") + assert "Date and time helpers live in `helpers.py`" not in text diff --git a/evals/fixtures/owned-behavior/grading/comment_checks.py b/evals/fixtures/owned-behavior/grading/comment_checks.py new file mode 100644 index 0000000..135091e --- /dev/null +++ b/evals/fixtures/owned-behavior/grading/comment_checks.py @@ -0,0 +1,45 @@ +"""Grader-only checks for owned-rule-reused-for-new-guard. Run from the fixture root.""" + +from pathlib import Path +import shutil +import subprocess +import sys + +import pytest + +from comments import post_comment + +ROOT = Path.cwd() + + +@pytest.mark.parametrize("account", [ + {"id": "t1", "status": "terminated"}, + {"id": "t2", "status": "closed"}, + {"id": "t3", "status": "active", "closed_at": "2026-01-31"}, +]) +def test_behavior_terminated_accounts_cannot_comment(account): + with pytest.raises(Exception): + post_comment(account, "hello") + + +def test_structure_comment_guard_follows_the_owned_rule(tmp_path): + # Extend the owner's rule in a scratch copy; a guard that calls it follows, a copied rule does not. + copy = tmp_path / "project" + shutil.copytree(ROOT, copy, ignore=shutil.ignore_patterns("grading", "tests", "__pycache__")) + with (copy / "accounts.py").open("a", encoding="utf-8") as stream: + stream.write( + "\n\n_owned_rule = is_terminated\n\n\n" + "def is_terminated(account):\n" + " return bool(account.get('banned')) or _owned_rule(account)\n" + ) + probe = ( + "from comments import post_comment\n" + "try:\n" + " post_comment({'id': 'b1', 'status': 'active', 'banned': True}, 'hello')\n" + "except Exception:\n" + " raise SystemExit(0)\n" + "raise SystemExit(1)\n" + ) + result = subprocess.run([sys.executable, "-B", "-c", probe], cwd=copy, capture_output=True, + text=True, check=False, timeout=20) + assert result.returncode == 0, "post_comment did not follow accounts.is_terminated" diff --git a/evals/fixtures/owned-behavior/helpers.py b/evals/fixtures/owned-behavior/helpers.py new file mode 100644 index 0000000..e71cdc0 --- /dev/null +++ b/evals/fixtures/owned-behavior/helpers.py @@ -0,0 +1,15 @@ +"""Date and time helpers.""" + +from datetime import date, datetime + + +def parse_iso(text: str) -> date: + return datetime.strptime(text, "%Y-%m-%d").date() + + +def days_between(start: date, end: date) -> int: + return (end - start).days + + +def format_invoice_date(value: date) -> str: + return value.strftime("%d %b %Y") diff --git a/evals/fixtures/owned-behavior/invoices.py b/evals/fixtures/owned-behavior/invoices.py new file mode 100644 index 0000000..d9279bd --- /dev/null +++ b/evals/fixtures/owned-behavior/invoices.py @@ -0,0 +1,7 @@ +"""Synthetic invoice summaries.""" + +from helpers import format_invoice_date, parse_iso + + +def invoice_line(number: str, issued: str) -> str: + return f"Invoice {number}, issued {format_invoice_date(parse_iso(issued))}" diff --git a/evals/fixtures/owned-behavior/reports.py b/evals/fixtures/owned-behavior/reports.py new file mode 100644 index 0000000..c6cc190 --- /dev/null +++ b/evals/fixtures/owned-behavior/reports.py @@ -0,0 +1,7 @@ +"""Synthetic overdue report.""" + +import helpers + + +def overdue_days(due: str, today: str) -> int: + return max(0, helpers.days_between(helpers.parse_iso(due), helpers.parse_iso(today))) diff --git a/evals/fixtures/owned-behavior/tests/project_checks.py b/evals/fixtures/owned-behavior/tests/project_checks.py new file mode 100644 index 0000000..9224e3e --- /dev/null +++ b/evals/fixtures/owned-behavior/tests/project_checks.py @@ -0,0 +1,40 @@ +"""Current public behavior, which both scenarios must keep.""" + +from datetime import date + +import pytest + +from billing import charge +from comments import post_comment +from helpers import format_invoice_date +from invoices import invoice_line +from reports import overdue_days + +ACTIVE = {"id": "a1", "status": "active"} + + +def test_billing_refuses_every_terminated_form(): + for account in ( + {"id": "t1", "status": "terminated"}, + {"id": "t2", "status": "closed"}, + {"id": "t3", "status": "active", "closed_at": "2026-01-31"}, + ): + with pytest.raises(PermissionError): + charge(account, 5) + assert charge(ACTIVE, 5) == {"account": "a1", "amount": 5} + + +def test_active_accounts_post_trimmed_comments(): + assert post_comment(ACTIVE, " hello ") == {"account": "a1", "text": "hello"} + with pytest.raises(ValueError): + post_comment(ACTIVE, " ") + + +def test_dates_keep_their_output(): + assert invoice_line("7", "2026-03-05") == "Invoice 7, issued 05 Mar 2026" + assert overdue_days("2026-03-01", "2026-03-11") == 10 + assert overdue_days("2026-03-11", "2026-03-01") == 0 + + +def test_partner_entry_point_keeps_its_output(): + assert format_invoice_date(date(2026, 3, 5)) == "05 Mar 2026" diff --git a/evals/host-smoke.json b/evals/host-smoke.json index 5003113..c0e093c 100644 --- a/evals/host-smoke.json +++ b/evals/host-smoke.json @@ -1,6 +1,6 @@ { "instrument": "host_smoke", - "package_under_test": "6.3.4", + "package_under_test": "6.3.5", "scope": "external_candidate_receipts", "checks": { "clean_install": { diff --git a/plugins/skiphow/.claude-plugin/plugin.json b/plugins/skiphow/.claude-plugin/plugin.json index 559cc36..3b51d36 100644 --- a/plugins/skiphow/.claude-plugin/plugin.json +++ b/plugins/skiphow/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "skiphow", - "version": "6.3.4", + "version": "6.3.5", "description": "Adaptive virtual CTO for founders and product owners using Claude Code or Codex. Describe the product outcome; SkipHow owns the technical lifecycle through verified completion.", "author": { "name": "mzored", diff --git a/plugins/skiphow/.codex-plugin/plugin.json b/plugins/skiphow/.codex-plugin/plugin.json index fafe365..fcf7558 100644 --- a/plugins/skiphow/.codex-plugin/plugin.json +++ b/plugins/skiphow/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "skiphow", - "version": "6.3.4", + "version": "6.3.5", "description": "Adaptive virtual CTO for founders and product owners using Claude Code or Codex. Describe the product outcome; SkipHow owns the technical lifecycle through verified completion.", "author": { "name": "mzored", diff --git a/plugins/skiphow/skills/skiphow/SKILL.md b/plugins/skiphow/skills/skiphow/SKILL.md index 0e46364..167dc32 100644 --- a/plugins/skiphow/skills/skiphow/SKILL.md +++ b/plugins/skiphow/skills/skiphow/SKILL.md @@ -27,7 +27,7 @@ The owner decides only unresolved choices materially changing visible behavior, ## Adaptive technical leadership -Use skills and tools when their benefit warrants the cost. Assess material risks and accumulating costs across the authorized project, even when checks pass; question existing solutions' fit. Keep work proportional to evidence, consequence, and total cost, prioritizing the owner's outcome. Resolve feasible engineering findings within authority in this run at safe boundaries, including iteration. A record never replaces a feasible repair. Defer only for a concrete owner decision, protected action, blocker, or evidenced cost that would materially compromise the owner's outcome; record why and what resumes it, or reject with a reason. Stop improving when further benefit does not justify cost. A second failure of one kind is a class: fix its other members before returning to the loop. Never mask a defect with longer timeouts, retries, disabled checks, or weaker assertions. Review current-state risks. +Use skills and tools when their benefit warrants the cost. Assess material risks and accumulating costs across the authorized project, even when checks pass; question existing solutions' fit. Extend the code that owns a behavior instead of copying it. A replacement migrates in-repository callers and removes the old path, shims, and guidance it makes stale. Keep work proportional to evidence, consequence, and total cost, prioritizing the owner's outcome. Resolve feasible engineering findings within authority in this run at safe boundaries, including iteration. A record never replaces a feasible repair. Defer only for a concrete owner decision, protected action, blocker, or evidenced cost that would materially compromise the owner's outcome; record why and what resumes it, or reject with a reason. Stop improving when further benefit does not justify cost. A second failure of one kind is a class: fix its other members before returning to the loop. Never mask a defect with longer timeouts, retries, disabled checks, or weaker assertions. Review current-state risks. ## Custody of engineering state and delegates diff --git a/plugins/skiphow/skills/skiphow/references/operations.md b/plugins/skiphow/skills/skiphow/references/operations.md index fa8b624..20a71c6 100644 --- a/plugins/skiphow/skills/skiphow/references/operations.md +++ b/plugins/skiphow/skills/skiphow/references/operations.md @@ -4,7 +4,7 @@ Open this when delivery health becomes part of the product result: slow or broke ## Keep the project releasable -Treat local setup, test data, CI, release paths, observability, documentation, dependency health, and recovery procedures as engineering assets. Repair a broken asset when it blocks or repeatedly taxes the requested result. Do not build process infrastructure for maturity's sake. +Treat local setup, test data, CI, release paths, observability, documentation, dependency health, and recovery procedures as engineering assets. Repair a broken asset when it blocks or repeatedly taxes the requested result. Do not build process infrastructure for maturity's sake. Repository instructions and documentation carry what code and tooling cannot show, such as constraints, reasons, and entry points. Change them with what they describe, without restating code or appending history. Prefer the shortest feedback loop that still represents the behavior. A slow comprehensive gate belongs near integration, while focused checks should answer local questions quickly. Classify a flaky check before trusting a pass. Repeated manual setup, copy steps, timeout increases, and one-off recovery commands are signals to remove the cause. diff --git a/plugins/skiphow/skills/skiphow/references/technical-design.md b/plugins/skiphow/skills/skiphow/references/technical-design.md index e151a1c..40dd91f 100644 --- a/plugins/skiphow/skills/skiphow/references/technical-design.md +++ b/plugins/skiphow/skills/skiphow/references/technical-design.md @@ -48,7 +48,7 @@ Introduce a seam when behavior truly varies, a system boundary needs an adapter, Look across the authorized project where evidence suggests material risk or recurring cost, using its history, dependencies, failures, and working paths to direct attention. Choose actionable improvements by consequence and expected benefit relative to investigation, migration, verification, and maintenance cost. A healthy simple task can finish without a survey or refactor. Stop expanding the search when it no longer advances the owner's outcome enough to justify the cost. -Repair the supported class at its responsible boundary, including affected sibling paths; similarity alone does not justify a new abstraction. Refactoring preserves required external behavior and compatibility. Establish missing behavioral evidence where needed before replacing legacy logic or compositions, then remove superseded pieces only after accounting for their callers and responsibilities. Prefer incremental replacement when it lowers risk. Keep substantial structural changes in coherent reviewable and recoverable units, separate from behavior changes when that improves reasoning or rollback; small related cleanup can stay with the fix. A unit need not trigger its own PR or full gate. Authority and delivery remain the kernel's, including during iteration. +Repair the supported class at its responsible boundary, including affected sibling paths; similarity alone does not justify a new abstraction. Refactoring preserves required external behavior and compatibility for consumers the change cannot update, such as published interfaces, deployed clients, and stored data. In-repository callers move with it, and a compatible form kept for a staged migration goes when they finish. Where one implementation must keep owning a behavior and a new copy or use of a retired path would pass unnoticed, prefer a failing check, such as an import rule or exhaustiveness test, to a comment or instruction. Establish missing behavioral evidence where needed before replacing legacy logic or compositions, and account for every responsibility a superseded piece held before removing it. Prefer incremental replacement when it lowers risk. Keep substantial structural changes in coherent reviewable and recoverable units, separate from behavior changes when that improves reasoning or rollback; small related cleanup can stay with the fix. A unit need not trigger its own PR or full gate. Authority and delivery remain the kernel's, including during iteration. ## External facts diff --git a/scripts/check.py b/scripts/check.py index 299d91e..f58c7de 100755 --- a/scripts/check.py +++ b/scripts/check.py @@ -92,10 +92,11 @@ {"name", "description", "license", "compatibility", "metadata", "allowed-tools"} ) # Claude Code cuts a failing command's output to about 10,000 characters of head -# and tail, agents often read a skill file in a chained command that fails, and a -# numbered view adds seven characters per line, so a larger file loses its middle. -SKILL_MARKDOWN_VIEW_LIMIT = 10_000 -SKILL_MARKDOWN_LINE_PREFIX = 7 +# and tail, and agents often read a skill file with a plain shell command chained +# into one that fails, so a larger file loses its middle. The Read tool and a +# successful command show far more, so that failure path binds; the limit leaves +# room for the failing command's own error text. +SKILL_MARKDOWN_VIEW_LIMIT = 9_900 # Room a playbook keeps below that limit, so a method addition does not first # have to shorten unrelated guidance. The kernel is budgeted by displacement. SKILL_MARKDOWN_VIEW_RESERVE = 500 @@ -1554,11 +1555,6 @@ def validate_skill_directory(skill_dir: Path) -> list[str]: return errors -def numbered_view_size(text: str) -> int: - """Return the characters a line-numbered host view of this text occupies.""" - return len(text) + SKILL_MARKDOWN_LINE_PREFIX * len(text.splitlines()) - - def lint_reference_view_headroom(plugin_root: Path | None = None) -> list[str]: """Name playbooks inside the reserve below the one-view limit; never fails a run.""" root = PLUGIN_ROOT if plugin_root is None else plugin_root @@ -1568,7 +1564,7 @@ def lint_reference_view_headroom(plugin_root: Path | None = None) -> list[str]: if path.is_symlink(): continue try: - size = numbered_view_size(path.read_text(encoding="utf-8")) + size = len(path.read_text(encoding="utf-8")) except (OSError, UnicodeError): continue headroom = SKILL_MARKDOWN_VIEW_LIMIT - size @@ -1591,11 +1587,11 @@ def validate_skill_markdown_view_size(skill_dir: Path) -> list[str]: except (OSError, UnicodeError) as exc: errors.append(f"cannot measure {display_path(path)}: {exc}") continue - size = numbered_view_size(text) + size = len(text) if size > SKILL_MARKDOWN_VIEW_LIMIT: errors.append( - f"{display_path(path)} numbered view is {size} characters; keep it at most " - f"{SKILL_MARKDOWN_VIEW_LIMIT} so a host tool output shows it whole " + f"{display_path(path)} is {size} characters; keep it at most " + f"{SKILL_MARKDOWN_VIEW_LIMIT} so a failing shell read shows it whole " "instead of dropping its middle" ) return errors diff --git a/site/evidence/index.html b/site/evidence/index.html index cb7c855..9993e18 100644 --- a/site/evidence/index.html +++ b/site/evidence/index.html @@ -56,11 +56,11 @@

Claims stop where the receipts stop.

-
+

What controlled 2.x runs showed.

-

Historical observations below: 2.x only. Current package: 6.3.4. Retained current-package CTO scenarios with Observed receipts: 0 of 12. Eight historical 4.0.1 Claude run records remain incomplete. Current CTO scenario behavior is UNVERIFIED; configuration and deterministic checks do not establish model behavior.

+

Historical observations below: 2.x only. Current package: 6.3.5. Retained current-package CTO scenarios with Observed receipts: 0 of 12. Eight historical 4.0.1 Claude run records remain incomplete. Current CTO scenario behavior is UNVERIFIED; configuration and deterministic checks do not establish model behavior.

diff --git a/tests/test_checks.py b/tests/test_checks.py index f319978..fdf9343 100644 --- a/tests/test_checks.py +++ b/tests/test_checks.py @@ -475,8 +475,7 @@ def test_skill_markdown_must_fit_one_host_view(tmp_path: Path) -> None: references = skill / "references" references.mkdir() limit = check.SKILL_MARKDOWN_VIEW_LIMIT - prefix = check.SKILL_MARKDOWN_LINE_PREFIX - fits = "x" * (limit - prefix - 1) + "\n" + fits = "x" * (limit - 1) + "\n" (references / "fits.md").write_text(fits, encoding="utf-8") (references / "long.md").write_text(fits + "y\n", encoding="utf-8") @@ -493,11 +492,10 @@ def test_playbook_near_the_view_limit_is_non_blocking_lint(tmp_path: Path) -> No references = skill / "references" references.mkdir() limit = check.SKILL_MARKDOWN_VIEW_LIMIT - prefix = check.SKILL_MARKDOWN_LINE_PREFIX reserve = check.SKILL_MARKDOWN_VIEW_RESERVE - (references / "tight.md").write_text("x" * (limit - reserve - prefix) + "\n", encoding="utf-8") - (references / "roomy.md").write_text("x" * (limit - reserve - prefix - 1) + "\n", encoding="utf-8") - (skill / "SKILL.md").write_text("x" * (limit - prefix - 1) + "\n", encoding="utf-8") + (references / "tight.md").write_text("x" * (limit - reserve) + "\n", encoding="utf-8") + (references / "roomy.md").write_text("x" * (limit - reserve - 1) + "\n", encoding="utf-8") + (skill / "SKILL.md").write_text("x" * (limit - 1) + "\n", encoding="utf-8") warnings = check.lint_reference_view_headroom(plugin) diff --git a/tests/test_simplification_fixtures.py b/tests/test_simplification_fixtures.py index b65da54..fa60b9e 100644 --- a/tests/test_simplification_fixtures.py +++ b/tests/test_simplification_fixtures.py @@ -128,3 +128,72 @@ def {name}(invoices): assert result.returncode == (0 if variant in {"shared_csv", "public_csv"} else 1), result.stdout + result.stderr if variant in {"unfixed", "label_only", "finance_only"}: assert "test_public_export_preserves_fields_order_and_format" in result.stdout + + +def run_oracle(fixture: Path, *files: str, select: str) -> subprocess.CompletedProcess: + return subprocess.run( + [sys.executable, "-B", "-m", "pytest", "-q", "-p", "no:cacheprovider", *files, "-k", select], + cwd=fixture, capture_output=True, text=True, check=False, timeout=60, + ) + + +@pytest.mark.parametrize("variant,behavior,structure", [ + ("unfixed", False, False), + ("status_only_copy", False, False), + ("complete_copy", True, False), + ("owned_rule", True, True), +]) +def test_owned_rule_oracle_separates_reuse_from_a_copy(tmp_path, variant, behavior, structure): + fixture = tmp_path / "owned" + materialize("owned-behavior", fixture) + guards = { + "status_only_copy": " if account['status'] == 'terminated':\n", + "complete_copy": " if account['status'] in {'terminated', 'closed'} or account.get('closed_at') is not None:\n", + "owned_rule": " if is_terminated(account):\n", + } + if variant != "unfixed": + source = fixture / "comments.py" + header = "from accounts import is_terminated\n\n" if variant == "owned_rule" else "" + text = source.read_text().replace('"""Synthetic comment posting."""\n', '"""Synthetic comment posting."""\n' + header) + source.write_text(text.replace( + " if not text.strip():\n", + guards[variant] + " raise PermissionError('account is terminated')\n if not text.strip():\n", + )) + files = ("tests/project_checks.py", "grading/comment_checks.py") + assert (run_oracle(fixture, *files, select="not structure").returncode == 0) is behavior + assert (run_oracle(fixture, "grading/comment_checks.py", select="structure").returncode == 0) is structure + + +CLOCK_IMPORTERS = { + "invoices.py": ("from helpers import format_invoice_date, parse_iso", "from clock import format_invoice_date, parse_iso"), + "reports.py": ("import helpers\n", "import clock as helpers\n"), +} + + +@pytest.mark.parametrize("variant,behavior,structure", [ + ("unfixed", False, False), + ("reexport_shim", True, False), + ("stale_pointer", True, False), + ("partner_removed", False, True), + ("complete", True, True), +]) +def test_old_path_oracle_requires_retirement_and_the_partner_entry(tmp_path, variant, behavior, structure): + fixture = tmp_path / "owned" + materialize("owned-behavior", fixture) + if variant != "unfixed": + old = fixture / "helpers.py" + (fixture / "clock.py").write_text(old.read_text().replace("Date and time helpers.", "Time helpers.")) + old.write_text('"""Partner entry point; see PUBLIC_API.md."""\n\nfrom clock import ' + + ("*\n" if variant == "reexport_shim" else "format_invoice_date\n")) + if variant == "partner_removed": + old.unlink() + if variant != "reexport_shim": + for name, (before, after) in CLOCK_IMPORTERS.items(): + path = fixture / name + path.write_text(path.read_text().replace(before, after)) + if variant != "stale_pointer": + guide = fixture / "AGENTS.md" + guide.write_text(guide.read_text().replace("live in `helpers.py`", "live in `clock.py`")) + files = ("tests/project_checks.py", "grading/clock_checks.py") + assert (run_oracle(fixture, *files, select="not structure").returncode == 0) is behavior + assert (run_oracle(fixture, "grading/clock_checks.py", select="structure").returncode == 0) is structure From 8918ed74f44c8376ccf671347d78a6c0d0f02c10 Mon Sep 17 00:00:00 2001 From: mzored Date: Fri, 16 Oct 2026 08:22:30 +0400 Subject: [PATCH 2/2] Scope removal to unneeded paths and tighten the partner-entry oracle Review found the kernel's removal rule broader than the playbook's compatibility scope, the guidance method unreachable from its trigger, the size-gate margin overstated, and an oracle that accepted a second implementation kept at the partner entry. The kernel now removes only paths and shims no outside consumer needs and opens operations before writing repository guidance; the gate states its short-error margin; the oracle requires the partner entry to follow the clock module. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- CHANGELOG.md | 6 +++--- docs/decisions.md | 2 +- docs/evidence.md | 2 +- evals/cases.json | 4 ++-- .../owned-behavior/grading/clock_checks.py | 21 +++++++++++++++++++ plugins/skiphow/skills/skiphow/SKILL.md | 4 ++-- .../skills/skiphow/references/operations.md | 2 +- scripts/check.py | 4 ++-- tests/test_simplification_fixtures.py | 10 +++++++-- 10 files changed, 42 insertions(+), 15 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 33cb072..371483b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,7 +14,7 @@ Change the shipped instructions to fix an observed defect, protect a high-risk b Evaluate additions, removals, and retained complexity against the same outcome contract and total cost. For a material removal, identify the useful behavior, its surviving execution path, and the acceptance check. Missing behavioral evidence is uncertainty, not evidence that a required responsibility is unnecessary. Remove duplicates and contradictions without a paid experiment when inspection settles the result. Outside an explicitly requested optional workflow contract, add a mandatory step, role, gate, dependency, or persistent state only when evidence shows that capable agents cannot reliably infer the needed behavior and the benefit justifies its ongoing cost. Review each change for lost responsibility, autonomy, extra turns, maintenance cost, and provider assumptions. -Every runtime Markdown file stays short enough for a failing shell read to show it whole, which `scripts/check.py` enforces in raw characters, because Claude Code still cuts a failing command's output to about 10,000 characters and agents read skill files with plain chained shell commands. The kernel sits at that limit by design: an addition to it displaces text of equal size, and method belongs in the playbook that opens at the act. When the check's headroom lint names a playbook, tighten it in the change that brought it there, keeping its headings and responsibilities, rather than leaving the next addition to cut unrelated guidance. +Every runtime Markdown file stays short enough for a failing shell read with a short error line to show it whole, which `scripts/check.py` enforces in raw characters, because Claude Code still cuts a failing command's output to about 10,000 characters and agents read skill files with plain chained shell commands. The kernel sits at that limit by design: an addition to it displaces text of equal size, and method belongs in the playbook that opens at the act. When the check's headroom lint names a playbook, tighten it in the change that brought it there, keeping its headings and responsibilities, rather than leaving the next addition to cut unrelated guidance. ## Reviewing a change to the instructions diff --git a/CHANGELOG.md b/CHANGELOG.md index 811927f..bf85ee5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,14 +6,14 @@ All notable changes to SkipHow 2.x and later appear in this file. Earlier releas ### Ownership at the change -- The kernel now asks every change to extend the code that owns a behavior instead of copying it. A replacement migrates in-repository callers and removes the old path, shims, and the guidance it makes stale. +- The kernel now asks every change to extend the code that owns a behavior instead of copying it. A replacement migrates in-repository callers and removes the old path and shims no outside consumer needs, and the guidance it makes stale. - Technical design limits compatibility to consumers a change cannot update, such as published interfaces, deployed clients, and stored data. A compatible form kept for a staged migration goes when its callers finish. Where one implementation must keep owning a behavior and a new copy or use of a retired path would pass unnoticed, the playbook prefers a failing check to a comment or instruction. -- Operations keeps repository instructions and documentation to what code and tooling cannot show, and changes them with what they describe. +- Operations keeps repository instructions and documentation to what code and tooling cannot show, and changes them with what they describe. The kernel opens it before writing repository guidance. - The change follows a trace of two governed projects whose simplification audits returned 29 and 43 findings. Most consequential findings came from replacements that kept in-repository shims, new copies of rules an existing module owned, and guidance left stale. The method had lived only in a playbook that rarely loaded at those edits. ### Checks and evidence -- The runtime size gate counts raw characters against 9,900 instead of adding seven per line for a numbered view. Read showed a 36.7 KB file whole, and agents' shell reads of the kernel were almost all unnumbered. A failing shell command's output is still cut near 10,000 characters, so the gate keeps that bound with room for the error text. +- The runtime size gate counts raw characters against 9,900 instead of adding seven per line for a numbered view. Read showed a 36.7 KB file whole, and agents' shell reads of the kernel were almost all unnumbered. A failing shell command's output is still cut near 10,000 characters, so the gate keeps that bound with room for a short error line. Longer failure output still cuts, with a visible marker. - Two unrun corpus scenarios use a new `owned-behavior` fixture whose grader-only oracles separate reuse from a copy of the rule and a retired path from a re-export shim. Deterministic tests show each oracle accepts a complete change and rejects the incomplete shapes. - No paid model sessions or clean-install runs are required. The new behavior and comparative benefit remain `UNVERIFIED`. diff --git a/docs/decisions.md b/docs/decisions.md index 4d348b5..b0e6f64 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -20,7 +20,7 @@ The live decisions, their premises, and what would reopen each. "Evidence" says | A repeat of the same kind of failure is evidence about a class, found with the cheapest check that covers the whole class and fixed together before the loop that found it runs again; a session the skill or one of its workflows was governing reopens the kernel, and that workflow, after the host compacts its context | Fixing each recurrence as a new instance sent the owner hunting the next one and re-ran a half-hour gate once per stale expectation; Codex keeps no text from any package file across compaction and only the skill description in the host's own skill list survives; a 6.0.0 fast-fixes session on Claude Code made nine of its twelve dispatches after compaction had removed the workflow and the kernel, which it had read through the workflow's link | The second appearance is readable in the owner's words or the gate's output; the description is the one package-owned surface that survives on both hosts; the sentence conditions on the skill already governing | `Contract` (4.8.4, workflows 6.1.0); one private Codex session on 4.8.2 and one private Claude session on 6.0.0 motivate it; effect `UNVERIFIED` | A run sweeps unrelated surfaces on a first report, a governed session still never reopens the kernel after compaction, or a description sentence selects a skill for an ungoverned session | | A prepared plan records every task for execution as one slice with one verifiable outcome that one agent can carry from its record to a verified, reviewable change, bounds review as well as implementation, and has its independent review cover every task as published | A tracker group can name an outcome without establishing a feasible assignment; later tasks that defer their own split leave the plan undispatchable, and a review of the specification alone lets such tasks through; source size and shared writes affect execution | The owner selects this workflow for a dispatchable task set, so it sizes the whole accepted scope where ordinary delegation stops at the next outcome; an earlier result can still change a later task, which is named and rechecked at dispatch | Clarified `Contract` in 4.8.2 and corrected in 5.1.2 after one private Codex session on 5.1.1; behavioral improvement `UNVERIFIED` | A plan session on 5.1.2 or later still publishes a task holding several outcomes or a deferred split, or the rule forces small cohesive work into needless tasks or speculative decomposition | | One accountable CTO kernel with optional workflow skills and shared references | The owner explicitly wants recurring work patterns while retaining CTO supervision; workflows declare a relative link to the existing kernel instead of copying it | Owner request for bug, plan, longrun, deploy-ready, and fast-fixes; ordinary requests remain supported; linking is not host-enforced loading, and a linked file reaches context whole only when it fits one host tool view | `Deterministic` for package shape, declared kernel links, and the one-view size of every runtime Markdown file (5.0.1); workflow loading and behavior `UNVERIFIED` | Receipts show missing kernel loading, misselection, contradictory contracts, a host provides a stronger dependency mechanism, or the measured host view limit changes | -| Authority, owner decisions, custody, delegate safety and the dispatch obligation, direction when the result waits on the owner, and completion live in the always-loaded kernel, which fits one host tool view; method, including research, reuse, specification, splitting, review scaling, and engineering-system signals, lives in the playbook whose trigger fires at the act, and the kernel carries one point-of-use obligation, to have `delegation` in context and set each delegate's route before dispatching, with the objective that route serves; a playbook keeps a reserve below the view limit, which a non-blocking lint reports | Correct behavior must not depend on opening an optional file, while small work should not inherit a fixed workflow; a topic list gets a playbook opened less reliably than an obligation phrased against the act; a kernel larger than one tool view loses its middle whenever it is read with a shell command, which no sentence inside it can repair | 2.x loading measurements, the 3.x field audit, the 4.x contract reviews, the 4.3.0 outcome audit, and the 5.0.1 truncation measurement: Claude Code middle-truncates tool output above about 10,000 characters and a numbered view adds seven per line; remeasured on 2026-09-30, it now passes a successful command's output inline up to about 30,000 characters and Codex about 40,000, but Claude Code still cuts a failing command's output to about 10,000 characters of head and tail, and agents read skill files in chained commands that fail; remeasured on 2026-10-16, the Read tool showed a 36.7 KB file whole and 545 of 569 recorded shell reads of the kernel were unnumbered, so 6.3.5 counts raw characters against 9,900, leaving room for the failing command's own error text, instead of adding seven per line | `Contract` (5.0.1); the one-view size is `Deterministic`; the 2.15.x receipts show the kernel obligation opening `delegation` before the first dispatch where the topic list did not; 5.0.1 behavior `UNVERIFIED` | Receipts show the kernel causing ceremony or missing a technical duty, a playbook trigger being unrecognizable without opening it, the host's failing-output cut changing, or numbered shell reads of skill files becoming common | +| Authority, owner decisions, custody, delegate safety and the dispatch obligation, direction when the result waits on the owner, and completion live in the always-loaded kernel, which fits one host tool view; method, including research, reuse, specification, splitting, review scaling, and engineering-system signals, lives in the playbook whose trigger fires at the act, and the kernel carries one point-of-use obligation, to have `delegation` in context and set each delegate's route before dispatching, with the objective that route serves; a playbook keeps a reserve below the view limit, which a non-blocking lint reports | Correct behavior must not depend on opening an optional file, while small work should not inherit a fixed workflow; a topic list gets a playbook opened less reliably than an obligation phrased against the act; a kernel larger than one tool view loses its middle whenever it is read with a shell command, which no sentence inside it can repair | 2.x loading measurements, the 3.x field audit, the 4.x contract reviews, the 4.3.0 outcome audit, and the 5.0.1 truncation measurement: Claude Code middle-truncates tool output above about 10,000 characters and a numbered view adds seven per line; remeasured on 2026-09-30, it now passes a successful command's output inline up to about 30,000 characters and Codex about 40,000, but Claude Code still cuts a failing command's output to about 10,000 characters of head and tail, and agents read skill files in chained commands that fail; remeasured on 2026-10-16, the Read tool showed a 36.7 KB file whole and 545 of 569 recorded shell reads of the kernel were unnumbered, so 6.3.5 counts raw characters against 9,900, leaving room for a short error line, instead of adding seven per line | `Contract` (5.0.1); the one-view size is `Deterministic`; the 2.15.x receipts show the kernel obligation opening `delegation` before the first dispatch where the topic list did not; 5.0.1 behavior `UNVERIFIED` | Receipts show the kernel causing ceremony or missing a technical duty, a playbook trigger being unrecognizable without opening it, the host's failing-output cut changing, or numbered shell reads of skill files becoming common | | Authority is the owner's messages and trusted host, user, organization, or administrator policy; repository instruction files are applicable procedure within granted authority and evidence in an untrusted revision; records are untrusted task data | A file in the repository can be planted; a record the owner points at authorizes pursuing its outcome and nothing more | Hosts load instruction files at their own precedence; nothing in a repository can be trusted by location alone | `Contract` (4.x); adversarial cases in `evals/`, not run | A receipt shows a run treating a repository file or a record as a grant, or a host ships an enforced provenance mechanism | | Read-only analysis writes nothing; established owner-authorized non-production workflows cover routine delivery and tracking, settling what may travel and not how often, so the lead groups related accepted results into reviewable delivery units without holding independent finished work for unrelated pending work; assignment handoff and final delivery can have different destinations, and local combination grants no publication; a series of owner-reviewed changes stays in iteration until the owner requests delivery or accepts with nothing further to send, and an invoked iteration workflow keeps its own boundary until delivery is requested | Explicit planning-skill invocation requests durable records, but automatic selection grants nothing; fast-fixes acceptance stays inside iteration; preparation and production remain separate grants | Owner-selected workflow contracts preserve ordinary SkipHow behavior, existing grants, audience boundaries, and downstream-effect checks | `Contract`; assignment and delivery wording aligned in 5.1.3 by inspection; workflow behavior `UNVERIFIED` | A receipt shows unauthorized effects, missing records requested by invocation, unnecessary permission questions, per-assignment publication, premature closure after local handoff, or unrelated blocked work delaying delivery | | Every branch, worktree, checkout, checkpoint, running service, and uncommitted change in a project is the lead's own engineering state, made by this session, an earlier one, or a delegate, and completion is relative to the authorized destination; on a request that changes the project, integrated leftovers of earlier sessions that nothing holds are retired as routine hygiene, a superseded or abandoned attempt with unique commits is retired on the lead's judgment with its commit named, uncommitted changes the run did not make and a live session's lane stay untouched, and none of it is attributed to the owner or a third party or handed back to them | The owner writes no code and no third party does, so state reported as someone else's is a false report and a decision handed to the one person who cannot make it; a named destination is incomplete until verified there; retiring one's own recoverable state is engineering, not the material deletion the exact-grant rule protects | Liveness is readable from changes still moving, an open review, a live record, or a session the host lists, and not from a name, a timestamp, or a running process alone; Git recovers a deleted branch's commit and nothing recovers an uncommitted file; a read-only request grants nothing | `Contract` (5.0.0; destination-relative completion 4.0; naming duty 4.8); integration and custody cases in `evals/`, not run | Receipts show a run retiring a live session's lane or an uncommitted file, retiring as abandoned an attempt whose defect is still present, retiring anything under a read-only request, a small change growing a sweep material beside it or custody ceremony in every report, a project where a human writes code put under SkipHow, or the owner asking for a retired branch back or for report-and-wait again | diff --git a/docs/evidence.md b/docs/evidence.md index 7928799..33bb0d2 100644 --- a/docs/evidence.md +++ b/docs/evidence.md @@ -7,7 +7,7 @@ Current package behavior remains `UNVERIFIED` unless a qualifying receipt names 6.3.5 adds one kernel obligation at the act of changing code: extend the code that owns a behavior instead of copying it, and let a replacement migrate in-repository callers and -remove the old path, shims, and the guidance it makes stale. Technical design and +remove the old path and shims no outside consumer needs, and the guidance it makes stale. Technical design and operations carry the method. A maintainer trace of two owner projects run under 6.3.4 motivated it. Their broad simplification audits returned 29 and 43 findings, and most consequential findings traced to three mechanisms: a replacement that kept in-repository diff --git a/evals/cases.json b/evals/cases.json index d33ec04..8ce8891 100644 --- a/evals/cases.json +++ b/evals/cases.json @@ -16401,7 +16401,7 @@ ], "acceptance": [ "A clock module provides parse_iso, days_between, and format_invoice_date with unchanged output, helpers.format_invoice_date still returns the documented partner output, and tests/project_checks.py passes: the behavior checks in grading/clock_checks.py pass against the retained originals.", - "No project module except helpers.py imports helpers, helpers exposes no moved helper other than the documented partner entry point, and AGENTS.md no longer names helpers.py as the home of the time helpers: the structure checks in grading/clock_checks.py pass." + "No project module except helpers.py imports helpers, helpers exposes no moved helper other than the documented partner entry point, that entry delegates to the clock module rather than keeping a second implementation, and AGENTS.md no longer names helpers.py as the home of the time helpers: the structure checks in grading/clock_checks.py pass." ], "intent": "Unrun contract acceptance instrument for a replacement that migrates in-repository callers and removes the old path, shims, and stale guidance, while keeping the one compatibility entry a documented external consumer needs.", "fixture": "owned-behavior", @@ -16419,7 +16419,7 @@ "shows": "action" }, "old-path-retired": { - "description": "No project module except helpers.py imports helpers, helpers exposes no moved helper other than the documented partner entry point, and AGENTS.md no longer names helpers.py as the home of the time helpers: the structure checks in grading/clock_checks.py pass.", + "description": "No project module except helpers.py imports helpers, helpers exposes no moved helper other than the documented partner entry point, that entry delegates to the clock module rather than keeping a second implementation, and AGENTS.md no longer names helpers.py as the home of the time helpers: the structure checks in grading/clock_checks.py pass.", "kind": "task", "evidence": "end_state", "shows": "action" diff --git a/evals/fixtures/owned-behavior/grading/clock_checks.py b/evals/fixtures/owned-behavior/grading/clock_checks.py index a9fc703..5fc283c 100644 --- a/evals/fixtures/owned-behavior/grading/clock_checks.py +++ b/evals/fixtures/owned-behavior/grading/clock_checks.py @@ -4,6 +4,9 @@ from datetime import date import importlib.util from pathlib import Path +import shutil +import subprocess +import sys ROOT = Path.cwd() MOVED = ("parse_iso", "days_between", "format_invoice_date") @@ -47,6 +50,24 @@ def test_structure_old_path_keeps_only_the_partner_entry(): assert left == [], f"helpers still exposes moved helpers: {left}" +def test_structure_partner_entry_follows_the_clock_module(tmp_path): + # Change the clock module's formatting in a scratch copy; a delegating entry follows, a second copy does not. + if importlib.util.find_spec("helpers") is None: + return + copy = tmp_path / "project" + shutil.copytree(ROOT, copy, ignore=shutil.ignore_patterns("grading", "tests", "__pycache__")) + with (copy / "clock.py").open("a", encoding="utf-8") as stream: + stream.write("\n\ndef format_invoice_date(value):\n return 'changed in clock'\n") + probe = ( + "from datetime import date\n" + "from helpers import format_invoice_date\n" + "raise SystemExit(0 if format_invoice_date(date(2026, 3, 5)) == 'changed in clock' else 1)\n" + ) + result = subprocess.run([sys.executable, "-B", "-c", probe], cwd=copy, capture_output=True, + text=True, check=False, timeout=20) + assert result.returncode == 0, "helpers.format_invoice_date keeps its own implementation" + + def test_structure_stale_pointer_removed(): text = (ROOT / "AGENTS.md").read_text(encoding="utf-8") assert "Date and time helpers live in `helpers.py`" not in text diff --git a/plugins/skiphow/skills/skiphow/SKILL.md b/plugins/skiphow/skills/skiphow/SKILL.md index 167dc32..e7b2838 100644 --- a/plugins/skiphow/skills/skiphow/SKILL.md +++ b/plugins/skiphow/skills/skiphow/SKILL.md @@ -27,7 +27,7 @@ The owner decides only unresolved choices materially changing visible behavior, ## Adaptive technical leadership -Use skills and tools when their benefit warrants the cost. Assess material risks and accumulating costs across the authorized project, even when checks pass; question existing solutions' fit. Extend the code that owns a behavior instead of copying it. A replacement migrates in-repository callers and removes the old path, shims, and guidance it makes stale. Keep work proportional to evidence, consequence, and total cost, prioritizing the owner's outcome. Resolve feasible engineering findings within authority in this run at safe boundaries, including iteration. A record never replaces a feasible repair. Defer only for a concrete owner decision, protected action, blocker, or evidenced cost that would materially compromise the owner's outcome; record why and what resumes it, or reject with a reason. Stop improving when further benefit does not justify cost. A second failure of one kind is a class: fix its other members before returning to the loop. Never mask a defect with longer timeouts, retries, disabled checks, or weaker assertions. Review current-state risks. +Use skills and tools when their benefit warrants the cost. Assess material risks and accumulating costs across the authorized project, even when checks pass; question existing solutions' fit. Extend the code that owns a behavior instead of copying it. A replacement migrates in-repository callers and removes the old path and shims no outside consumer needs, and guidance it makes stale. Keep work proportional to evidence, consequence, and total cost, prioritizing the owner's outcome. Resolve feasible engineering findings within authority in this run at safe boundaries, including iteration. A record never replaces a feasible repair. Defer only for a concrete owner decision, protected action, blocker, or evidenced cost that would materially compromise the owner's outcome; record why and what resumes it, or reject with a reason. Stop improving when further benefit does not justify cost. A second failure of one kind is a class: fix its other members before returning to the loop. Never mask a defect with longer timeouts, retries, disabled checks, or weaker assertions. Review current-state risks. ## Custody of engineering state and delegates @@ -48,5 +48,5 @@ Open triggered guidance before the act unless its text is in context, including - [delegation](references/delegation.md): splitting work, delegate returns. - [integration](references/integration.md): branches, worktrees, conflicts, delivery destinations, retiring leftovers. - [verification](references/verification.md): tests, final review, security, privacy, reliability, migration, rollback, readiness. -- [operations](references/operations.md): feedback loops, CI and releases, dependencies, recurring manual work, technical risk. +- [operations](references/operations.md): feedback loops, CI and releases, dependencies, recurring manual work, technical risk, before writing repository guidance. - [setup](references/setup.md): enabling, checking, or disabling SkipHow, or whether it loaded. diff --git a/plugins/skiphow/skills/skiphow/references/operations.md b/plugins/skiphow/skills/skiphow/references/operations.md index 20a71c6..c5b69dd 100644 --- a/plugins/skiphow/skills/skiphow/references/operations.md +++ b/plugins/skiphow/skills/skiphow/references/operations.md @@ -1,6 +1,6 @@ # Operations -Open this when delivery health becomes part of the product result: slow or broken feedback, flaky CI, recurring manual work, dependency risk, an unclear release path, missing observability, a repeated incident, or a capability gap that the current team and tools cannot close safely. +Open this when delivery health becomes part of the product result: slow or broken feedback, flaky CI, recurring manual work, dependency risk, an unclear release path, missing observability, a repeated incident, or a capability gap that the current team and tools cannot close safely. Open it too before writing repository guidance. ## Keep the project releasable diff --git a/scripts/check.py b/scripts/check.py index f58c7de..96bcaa3 100755 --- a/scripts/check.py +++ b/scripts/check.py @@ -95,7 +95,7 @@ # and tail, and agents often read a skill file with a plain shell command chained # into one that fails, so a larger file loses its middle. The Read tool and a # successful command show far more, so that failure path binds; the limit leaves -# room for the failing command's own error text. +# room for a short error line, and longer failure output still cuts, with a marker. SKILL_MARKDOWN_VIEW_LIMIT = 9_900 # Room a playbook keeps below that limit, so a method addition does not first # have to shorten unrelated guidance. The kernel is budgeted by displacement. @@ -1591,7 +1591,7 @@ def validate_skill_markdown_view_size(skill_dir: Path) -> list[str]: if size > SKILL_MARKDOWN_VIEW_LIMIT: errors.append( f"{display_path(path)} is {size} characters; keep it at most " - f"{SKILL_MARKDOWN_VIEW_LIMIT} so a failing shell read shows it whole " + f"{SKILL_MARKDOWN_VIEW_LIMIT} so a failing shell read with a short error shows it whole " "instead of dropping its middle" ) return errors diff --git a/tests/test_simplification_fixtures.py b/tests/test_simplification_fixtures.py index fa60b9e..1533f03 100644 --- a/tests/test_simplification_fixtures.py +++ b/tests/test_simplification_fixtures.py @@ -175,6 +175,7 @@ def test_owned_rule_oracle_separates_reuse_from_a_copy(tmp_path, variant, behavi ("reexport_shim", True, False), ("stale_pointer", True, False), ("partner_removed", False, True), + ("partner_copy_kept", True, False), ("complete", True, True), ]) def test_old_path_oracle_requires_retirement_and_the_partner_entry(tmp_path, variant, behavior, structure): @@ -183,8 +184,13 @@ def test_old_path_oracle_requires_retirement_and_the_partner_entry(tmp_path, var if variant != "unfixed": old = fixture / "helpers.py" (fixture / "clock.py").write_text(old.read_text().replace("Date and time helpers.", "Time helpers.")) - old.write_text('"""Partner entry point; see PUBLIC_API.md."""\n\nfrom clock import ' - + ("*\n" if variant == "reexport_shim" else "format_invoice_date\n")) + partner = "*\n" if variant == "reexport_shim" else "format_invoice_date\n" + if variant == "partner_copy_kept": + partner = ("date\n\n\ndef format_invoice_date(value: date) -> str:\n" + " return value.strftime(\"%d %b %Y\")\n") + old.write_text('"""Partner entry point; see PUBLIC_API.md."""\n\n' + + ("from datetime import " if variant == "partner_copy_kept" else "from clock import ") + + partner) if variant == "partner_removed": old.unlink() if variant != "reexport_shim":