From 488ff615f1e07951d4391b2fd42e50ebeea7a8ee Mon Sep 17 00:00:00 2001 From: axiom-marbell Date: Tue, 18 Aug 2026 11:28:53 +0200 Subject: [PATCH 1/3] process: keep viable harvest work active Advances #81 by separating PR lifecycle from claim promotion and by making validation scope impact-based. --- .agents/skills/physics-erdos-loop/SKILL.md | 11 ++-- .agents/skills/research-pr-harvest/SKILL.md | 34 ++++++++-- .github/pull_request_template.md | 13 +++- AGENTS.md | 23 ++++--- AGENTS_START_HERE.md | 35 +++++++--- memory-templates/campaign-proposal.md | 6 +- memory-templates/claim-review.md | 4 +- .../delegated-continuation-pr-template.md | 12 ++-- memory-templates/effort-contract.md | 7 +- memory-templates/research-arc.md | 13 ++-- .../efforts/pr-lifecycle-active-harvest.md | 65 +++++++++++++++++++ tests/test_public_contribution_surfaces.py | 21 ++++++ 12 files changed, 197 insertions(+), 47 deletions(-) create mode 100644 memory/codex/efforts/pr-lifecycle-active-harvest.md diff --git a/.agents/skills/physics-erdos-loop/SKILL.md b/.agents/skills/physics-erdos-loop/SKILL.md index e98c9722..ce7daec9 100644 --- a/.agents/skills/physics-erdos-loop/SKILL.md +++ b/.agents/skills/physics-erdos-loop/SKILL.md @@ -29,6 +29,7 @@ Read [governance.md](references/governance.md) before changing a claim, conventi - A campaign is an immutable research event, not canonical truth. - A proposal can challenge a claim; only an accepted claim can supersede one. +- Accepted canon governs release and promotion decisions, but remains falsifiable and reviewable; a conflict is a diagnosis to investigate, not a reason to erase evidence or halt conditional artifact work. - A verifier passing is necessary, but proves only its asserted predicate. - A campaign completion decision, scientific claim decision, and PR merge decision are independent. Use `research-pr-harvest` for the merge decision. - Numeric agreement is a comparator, never a concept-selection mechanism or hidden derivation input. @@ -107,7 +108,7 @@ After failure, choose the next action from the diagnosis: - implementation defect → repair and rerun; - unstable numerics → change discretization, solver, precision, or oracle; - bad representation → change variables, gauge, basis, coordinates, or formalism; -- concept conflicts with the framework → reject or reformulate it and try another candidate; +- concept conflicts with the framework → determine whether the defect belongs to the candidate or is independent evidence against accepted structure; reject or reformulate a defective candidate, otherwise open a `challenges` or foundational-revision proposal and keep the frontier active; - target was misstated → correct the claim while preserving the user's objective; - accepted foundation appears inconsistent → open a separate foundational-revision proposal. @@ -142,9 +143,9 @@ A large pass tally with insensitive predicates does not promote a claim. ## Phase 6 — assess framework fit before data fit -Compare candidates using the preregistered criteria. Structural fit precedes empirical closeness. If the favored candidate requires reinterpretation of unrelated claims, convention mixing, compensating imports, or narrative edits, reject it and continue the search. +Compare candidates using the preregistered criteria. Structural fit precedes empirical closeness. If the favored candidate requires reinterpretation of unrelated claims, convention mixing, compensating imports, or narrative edits merely to preserve it, reject it and continue the search. If the mismatch is reproduced independently of that candidate, treat it as evidence about canon and route it through a separate challenge rather than assuming acceptance status resolves the science. -Do not revise foundations merely to save a candidate. A foundational revision must demonstrate an independent pre-existing inconsistency, compare at least two repairs, select the minimum coherent change, enumerate the migration, and pass global replay. +Do not revise foundations merely to save a candidate. A foundational revision is nevertheless a legitimate advancement route when it demonstrates an independent pre-existing inconsistency, compares at least two repairs, selects the minimum coherent change, enumerates the migration, and passes global replay. Until adjudication, the conflict blocks promotion and accepted downstream use, not truthful conditional APIs or continued investigation. Only after the structural choice is frozen should you open the comparator gate and report predictive agreement or disagreement. @@ -176,11 +177,11 @@ For accepted claims: 3. Move the adjudicated campaign record into the immutable `campaigns/` log. 4. Run `scripts/render_docs.py`; never hand-edit `docs/generated/`. 5. Generate or synchronize accepted claim/release memory. Keep proposal and attempt memory separate. -6. Run targeted scientific checks, `scripts/validate.sh` (which includes the full test suite), and `git diff --check`; do not repeat the unchanged full suite separately. Run validation and commit in separate process invocations so an unguarded shell cannot continue past a failed gate and mask it with a later successful command. +6. Run targeted scientific checks, `scripts/validate.sh --full`, and `git diff --check`; promotion is a full-validation boundary, so do not repeat that unchanged suite separately. Run validation and commit in separate process invocations so an unguarded shell cannot continue past a failed gate and mask it with a later successful command. ## Phase 10 — done gate -Declare the campaign objective complete only when every item in the success contract passes and the debt ledger is empty. An honest failure leaves the objective active with a new candidate or repair queued. It does not prevent an individual PR or agent run from ending at a clean harvest checkpoint: use `research-pr-harvest` to merge independently correct, novel, reusable units, name and keep open the canonical goal issue, update that issue with the merged/refactor/history disposition and unit-level rationale, and hand the exact frontier to a fresh run. Missing work toward the larger goal is frontier rather than debt unless a merged or promoted unit promises it. A pause caused by user authority or an external dependency preserves the active contract and exact next executable action; it is not scientific completion. When a repeated workflow defect or tooling gotcha is discovered, correct and consolidate the relevant instruction in `AGENTS.md`, this skill, and the applicable memory template; do not merely append another overlapping rule. +Declare the campaign objective complete only when every item in the success contract passes and the debt ledger is empty. An honest failure leaves the objective active with a new candidate or repair queued. It does not prevent an individual PR or agent run from ending at a clean harvest checkpoint: use `research-pr-harvest` to merge independently correct, novel, reusable units, name and keep open the canonical goal issue, update that issue with the merged/refactor/history disposition and unit-level rationale, and hand the exact frontier to a fresh run. Keep a source PR open while a finite repair or harvest is live; close it unmerged only after the harvest skill's terminal-close test. Missing work toward the larger goal is frontier rather than debt unless a merged or promoted unit promises it. A pause caused by user authority or an external dependency preserves the active contract and exact next executable action; it is not scientific completion. When a repeated workflow defect or tooling gotcha is discovered, correct and consolidate the relevant instruction in `AGENTS.md`, this skill, and the applicable memory template; do not merely append another overlapping rule. ## Working with delegated agents diff --git a/.agents/skills/research-pr-harvest/SKILL.md b/.agents/skills/research-pr-harvest/SKILL.md index 39a80b4b..9ea2d897 100644 --- a/.agents/skills/research-pr-harvest/SKILL.md +++ b/.agents/skills/research-pr-harvest/SKILL.md @@ -19,7 +19,7 @@ Read the PR, linked goal, base release, accepted claim boundary, diff, tests, an Identify one canonical goal issue and confirm that it existed before the PR was submitted, including before a draft PR. The PR must mention that issue explicitly: use `Advances #N` while the goal remains incomplete and reserve `Fixes #N` for full completion. If the source PR has no pre-existing issue, do not merge it. Create the canonical issue, preserve the source PR as provenance, and place any selected units in a new compliant harvest PR opened after the issue. -In this repository, treat the user's act of supplying a PR URL or number to an agent that did not open, commit to, or materially implement that PR as standing authorization to complete the normal PR lifecycle without further operator prompts: review and comment, edit PR metadata, create a focused harvest branch or follow-up PR, merge or close according to the evidence, and update the linked issue. An agent must not merge any PR it opened, committed to, or materially implemented. If the reviewing agent performs substantive repairs or creates a follow-up harvest PR, a distinct agent or repository owner must execute that merge. Leave a validated handoff when no distinct merger is available. Do not force-push a contributor's branch, delete unrelated branches, broaden the issue objective, or promote unsupported claims. If an external permission or branch rule blocks an action, preserve the exact next action and report the actual blocker. +In this repository, treat the user's act of supplying a PR URL or number to an agent that did not open, commit to, or materially implement that PR as standing authorization to complete the normal PR lifecycle without further operator prompts: review and comment, edit PR metadata, request changes, create a focused harvest branch or follow-up PR, merge when eligible, close only after the terminal-close test below, and update the linked issue. An agent must not merge any PR it opened, committed to, or materially implemented. If the reviewing agent performs substantive repairs or creates a follow-up harvest PR, a distinct agent or repository owner must execute that merge. Leave a validated handoff when no distinct merger is available. Do not force-push a contributor's branch, delete unrelated branches, broaden the issue objective, or promote unsupported claims. If an external permission or branch rule blocks an action, preserve the exact next action and report the actual blocker. ## Slice the PR into harvest atoms @@ -50,11 +50,23 @@ Classify every unit as one of: - **refactor then merge** — valuable core exists but must be decoupled, renamed, narrowed, or convention-corrected; - **leave in PR history** — speculative capstone, duplicated machinery, unsupported interpretation, failed route, or maintenance cost with no durable reusable unit. +`Refactor then merge` is an active lifecycle state, not a polite rejection. Name +the repair owner or handoff, live source or harvest PR, exact change, and landing +test. Keep the source PR open in `request changes`, `active refactor`, or +`active harvest` state until that unit lands or becomes terminal. + +Close an unmerged PR only when every reusable atom has landed elsewhere, every +remaining atom has unit-level evidence that it is incorrect, non-novel, or +unmaintainable, the author or owner explicitly withdraws it, or a superseding +landed implementation makes it redundant. Missing accepted dependency closure, +a conflict with current canon, lack of a distinct merger, or a pending finite +repair does not pass this terminal-close test. + ## Build a harvest merge Prefer a focused harvest commit or follow-up PR over merging an inseparable campaign dump. Include only the selected implementation, tests, and minimal API documentation. -- Keep accepted-claim authority in the registry; do not promote a headline because related code merges. +- Keep accepted-claim authority in the registry; do not promote a headline because related code merges. Treat accepted canon as release authority, not an irrevisable premise: a correct conditional API may merge with explicit assumptions while contrary evidence proceeds through `challenges` or a separately governed foundational revision. - Create or confirm the canonical goal issue before opening the focused PR. Name it using `Advances #N` for partial progress and use `Fixes #N` only after the full goal passes its completion gate. - Record the authoring or implementing agent and a distinct intended merger. The authoring agent may prepare, validate, and hand off the PR but may not merge it. - Keep the goal issue open when the campaign remains incomplete. @@ -73,7 +85,7 @@ After the final merge boundary is known, update the goal issue—not only the PR - — Rationale: . Evidence: . ### Requires refactor -- — Rationale: . Required change: . Source: . +- — Rationale: . Required change: . Owner/handoff: . Live PR: . Landing test: . Source: . ### Left in PR history - — Rationale: . Source: . @@ -81,8 +93,10 @@ After the final merge boundary is known, update the goal issue—not only the PR ### Continuation - Claims promoted: - Goal state: +- PR lifecycle: - Landed interfaces: - Next decisive action: +- Terminal-close evidence: ### What closes the goal - Objective-level closure: @@ -111,6 +125,14 @@ For the current route, list necessary proof obligations and the evidence that wo Treat review conclusions as reasoned and rebuttable, not as accepted scientific authority. For every blocking conclusion, state what counterexample, derivation, constraint count, controlled limit, or other evidence would change the assessment. Invite focused disagreement without weakening the completion gate. +When a route conflicts with accepted canon, separate its artifact claim from its +authority claim. A canon conflict blocks promotion and accepted downstream use; +it does not by itself block a correct conditional artifact, erase contrary +evidence, or close the PR. Test whether the mismatch is a candidate defect or a +pre-existing canonical inconsistency. If it survives independently of the +favored route, open a `challenges` or foundational-revision proposal with +competing repairs and keep the scientific frontier active. + ## Preserve novelty without creating debt Missing pieces of the parent goal are the **campaign frontier**, not debt. Debt means an unresolved defect, unsupported promise, broken consumer, or hidden assumption inside the scope being merged or promoted. @@ -134,12 +156,13 @@ Treat the goal as long-lived and each agent run or PR as bounded. - Harvest a unit as soon as it becomes locally complete; do not hold all value hostage to the capstone. - Maintain a short PR frontier with `landed`, `current hypothesis`, and `next decisive test` rather than expanding narrative state. - If later work becomes repetitive, weakens claims, substitutes ceremony for new evidence, or only restates earlier results, stop that run at the last strong milestone. Merge the harvest, leave the goal open, and hand the frontier to a fresh agent. +- Keep the source PR open while a declared refactor or harvest remains live; reassess closure only after the unit lands or the terminal-close test passes. An individual run may end at a clean handoff without lowering or closing the campaign objective. ## Validate proportionately -Run targeted tests and claim-appropriate oracles for each harvested unit, impact analysis for changed public symbols, affected consumers, and one repository validation at the final unchanged merge boundary. Do not rerun unrelated full validation for each discarded campaign artifact. +Run targeted tests and claim-appropriate oracles for each harvested unit, impact analysis for changed public symbols, affected consumers, and one repository validation at the final unchanged merge boundary. An additive public export may use scoped validation when impact is bounded, no existing contract changes, consumers are known, and targeted API coverage passes. Reserve full validation for promotion or release, shared numerics or verification machinery, claim/release governance semantics, changed existing public contracts with consumers, dependency or cross-cutting convention changes, multi-sector changes, or uncertain impact. Do not rerun unrelated full validation for each discarded campaign artifact. ## Report the disposition @@ -164,7 +187,8 @@ Use this compact structure: - Authoring or implementing agent: - Distinct merger: - Issue handoff: -- PR disposition: +- PR lifecycle: +- Terminal-close evidence: - Campaign frontier: ``` diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index de820d8e..f2ebb6e1 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -89,7 +89,10 @@ Campaign frontier outside this merge unit: ## Validation boundary Mark a nonapplicable scientific row `N/A` and explain why in the verification -section; do not claim it passed. +section; do not claim it passed. An additive public export may stay scoped when +impact analysis shows a bounded sector, no changed existing contract, known +consumers, and targeted API coverage. Record that rationale rather than treating +the export alone as a full-suite trigger. - [ ] Targeted tests and named scientific verifiers pass. - [ ] Load-bearing mutations, counterexamples, or wrong-convention probes fail as expected. @@ -119,10 +122,12 @@ fields in a formal review. - Claim promotion: - Goal completion: - Merge as written: -- Refactor or harvest: +- Refactor or harvest: - Leave in PR history: +- Source PR lifecycle: +- Terminal-close evidence: - Next decisive action: -- Head branch disposition: +- Head branch disposition: ### Reviewer checks @@ -134,5 +139,7 @@ fields in a formal review. - [ ] Every new public symbol has an explicit authority status and owning issue. - [ ] Verification sensitivity and applicable numerical/formal limits audited. - [ ] Merge, claim-promotion, and goal-completion decisions kept independent. +- [ ] A finite refactor or harvest has an owner/handoff, live PR, and landing test; its source PR remains open. +- [ ] Any proposed unmerged closure passes the terminal-close test and records landed replacements. - [ ] Canonical issue handoff is posted or preserved ready to post. - [ ] The exact same-repository head will be deleted after merge; open or closed-unmerged/failed heads are preserved unless an owner explicitly retires them. diff --git a/AGENTS.md b/AGENTS.md index e9284c12..3422187e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,15 +26,17 @@ An effort succeeds only when all of the following are true: Only the user may change the objective or accept a reduced scope. Runtime interruption, missing authority, or an external dependency may pause execution, but it does not turn incomplete work into success. -These gates govern declaring the objective complete and promoting its headline claims. They are not a universal pull-request merge gate. Use `.agents/skills/research-pr-harvest/SKILL.md` to extract locally correct, novel, reusable units from an incomplete campaign while leaving its goal open. A merge creates provenance and reusable code, not accepted scientific authority. Missing parts of the larger goal are campaign frontier, not debt; debt is an unresolved defect, hidden assumption, broken consumer, or unsupported promise inside the scope being merged or promoted. Every harvest PR must name its canonical goal issue, and the final disposition must update that issue with unit-level lists and rationales for what merged, what requires refactor, and what remains only in PR history, plus landed links and the next decisive action. +These gates govern declaring the objective complete and promoting its headline claims. They are not a universal pull-request merge gate. Use `.agents/skills/research-pr-harvest/SKILL.md` to extract locally correct, novel, reusable units from an incomplete campaign while leaving its goal open. A merge creates provenance and reusable code, not accepted scientific authority. Missing parts of the larger goal are campaign frontier, not debt; debt is an unresolved defect, hidden assumption, broken consumer, or unsupported promise inside the scope being merged or promoted. Every harvest PR must name its canonical goal issue, and the final disposition must update that issue with unit-level lists and rationales for what merged, what requires refactor, and what remains only in PR history, plus landed links and the next decisive action. A `refactor then merge` disposition is active work: name its owner or handoff, live source or harvest PR, exact repair, and landing test, and keep the source PR open until that unit lands or satisfies the terminal-close test below. + +Request changes or create a focused harvest when a PR contains valuable work with a finite repair path. Close an unmerged PR only when every reusable atom has landed elsewhere, every remaining atom has been shown incorrect, non-novel, or unmaintainable with unit-level rationale, the author or owner explicitly withdraws it, or a superseding landed implementation makes it redundant. Incomplete accepted dependency closure, a conflict with current canon, absence of a distinct merger, or a pending finite repair is not terminal. Record the qualifying reason and landed links before closure; otherwise keep the PR open in `request changes`, `active refactor`, or `active harvest` state. Every pull request, including documentation, tooling, compatibility, harvest, and scientific work, must name exactly one canonical issue that existed before the PR was submitted. A contributing agent may create that issue. There are no standalone-PR exceptions. The issue must state the positive objective, scope, success gate, dependencies, and coordination boundary; use `Advances #N` while work remains and `Fixes #N` only when the full objective is complete. An agent must never merge a PR that it opened, authored a commit for, or materially implemented. The PR must be reviewed and merged by a distinct agent or repository owner. If the authoring agent also performs substantive corrective work during review, merge authority remains with another actor. When no distinct merger is available, leave the validated PR ready for handoff rather than self-merging it. -Merged same-repository PR head branches are transient and must not accumulate as a parallel discovery surface. Repository GitHub settings delete them automatically after merge; the distinct merger must verify that cleanup and may delete only the exact merged head if automation did not. Durable provenance lives in the merge commit, PR, canonical issue handoff, and landed `main` history. Preserve `main`, protected branches, open PR heads, and closed-unmerged or failed branches by default; deleting or retiring any of those requires an explicit owner decision. Branch cleanup never permits force-pushing, deleting an unverified or unrelated branch, or treating branch deletion as scientific adjudication. +Merged same-repository PR head branches are transient and must not accumulate as a parallel discovery surface. Repository GitHub settings delete them automatically after merge; the distinct merger must verify that cleanup and may delete only the exact merged head if automation did not. Durable provenance lives in the merge commit, PR, canonical issue handoff, and landed `main` history. Preserve `main`, protected branches, open PR heads, and closed-unmerged or failed branches by default; retiring a closed-unmerged head requires both an explicit owner decision and its recorded terminal-close rationale. Branch cleanup never permits force-pushing, deleting an unverified or unrelated branch, or treating branch deletion as scientific adjudication. -When the user supplies a pull-request URL or number to an agent that did not author or materially implement that PR, treat it as standing authorization to process the PR autonomously through the normal repository lifecycle: inspect, review, comment, correct PR metadata, create a focused harvest branch or follow-up PR, merge or close according to the evidence, and update the linked issue. Do not pause for routine operator confirmation. This authorization never overrides the non-self-merge rule and does not permit force-pushing a contributor branch, deleting unrelated branches, changing the user's objective, or promoting a claim that has not passed governance. +When the user supplies a pull-request URL or number to an agent that did not author or materially implement that PR, treat it as standing authorization to process the PR autonomously through the normal repository lifecycle: inspect, review, comment, correct PR metadata, request changes, create a focused harvest branch or follow-up PR, merge when eligible, close only after the terminal-close test, and update the linked issue. Do not pause for routine operator confirmation. This authorization never overrides the non-self-merge rule and does not permit force-pushing a contributor branch, deleting unrelated branches, changing the user's objective, or promoting a claim that has not passed governance. ## Authority and provenance @@ -46,7 +48,7 @@ Use this authority order: 4. Active proposals. 5. Append-only attempts and exploratory memory. -Chronology, commit status, prose confidence, check count, and empirical agreement do not create authority. A commit establishes provenance, not truth. A later campaign may challenge an earlier claim but cannot supersede it until review promotes the replacement claim. +Chronology, commit status, prose confidence, check count, and empirical agreement do not create authority. A commit establishes provenance, not truth. Accepted canon controls releases, promotion, and downstream dependency claims; it is reviewable scientific state, not an irrevisable premise or a reason to discard conflicting evidence. A later campaign may challenge an earlier claim but cannot supersede it until review promotes the replacement claim. Correct conditional APIs and evidence may merge without that promotion when their assumptions and exclusions are explicit. Never silently edit an earlier campaign. Never edit files under `docs/generated/`. Generate canonical documentation from the registry with `scripts/render_docs.py`. @@ -72,9 +74,9 @@ Before implementation: - State selection criteria before inspecting comparison values: structural fit, assumption cost, parameter economy, symmetry, dimensional consistency, limiting behavior, compatibility with accepted sectors, and predictive reach. - Separate derivation inputs from empirical comparators. When practical, keep comparator values blinded until equations, conventions, tests, and selection criteria are frozen. -When a candidate conflicts with accepted structure, diagnose the mismatch and reject or reformulate that candidate first. Try another concept. Do not rewrite unrelated earlier claims, rename quantities, mix conventions, or add compensating assumptions merely to preserve the chosen candidate. +When a candidate conflicts with accepted structure, diagnose whether the mismatch is a candidate defect or evidence of a pre-existing inconsistency in canon. Reject or reformulate a defective candidate and try another concept. If the mismatch survives independently of the favored candidate, open a `challenges` or foundational-revision proposal and compare repairs. Do not rewrite unrelated earlier claims, rename quantities, mix conventions, or add compensating assumptions merely to preserve the chosen candidate; equally, do not use current acceptance status as a substitute for investigating credible contrary evidence. -A foundational revision is exceptional. Open it as a separate proposal and require: +A foundational revision is separately governed and evidence-heavy, but it is an active advancement path rather than a shutdown condition. Open it as a separate proposal and require: - evidence that the inconsistency exists without assuming the new candidate; - at least two repair alternatives; @@ -195,11 +197,14 @@ git diff --check The non-pytest repository, generated-state, memory, skill, import, and compile checks run in both modes. A scoped pass is evidence only for the declared -pytest scope; record the exact selectors in the PR. Use +pytest scope; record the exact selectors in the PR. An additive public export +may remain scoped when impact analysis shows a bounded sector, no changed +existing contract, known consumers, and targeted coverage of the new API. Use `scripts/validate.sh --full` for a claim promotion or release and whenever the change reaches shared numerics, verification machinery, claim or release -governance semantics, public exports, dependencies, conventions, multiple -framework sectors, or has an uncertain dependency boundary. Calling +governance semantics, changes an existing public contract with consumers, +changes dependencies or cross-cutting conventions, spans multiple framework +sectors, or has an uncertain dependency boundary. Calling `scripts/validate.sh` without arguments remains a backward-compatible alias for `--full`. diff --git a/AGENTS_START_HERE.md b/AGENTS_START_HERE.md index 0a82e037..24338d58 100644 --- a/AGENTS_START_HERE.md +++ b/AGENTS_START_HERE.md @@ -19,7 +19,10 @@ Use this read order at the start of a task: A pinned accepted release and accepted claim registry outrank a newer commit, confident prose, a passing script, or an attractive numerical match. Never edit -`docs/generated/` or `migration/source-claims.yaml` by hand. +`docs/generated/` or `migration/source-claims.yaml` by hand. This authority +controls release and promotion decisions; canon remains scientifically +challengeable, and a truthful conditional artifact may merge without claim +promotion while a challenge is reviewed. ## 2. Coordinate before editing @@ -63,7 +66,8 @@ after merge; the merge commit, PR, issue handoff, and landed `main` history are the durable discovery paths. If automatic cleanup does not occur, the merger deletes that exact merged head after verifying the PR state and target. Preserve open heads and closed-unmerged or failed heads by default so unresolved work is -not erased; retire those only through an explicit owner decision. Never delete +not erased; retire those only through an explicit owner decision after the +terminal-close reason and any landed replacement are recorded. Never delete `main`, a protected branch, another open contributor branch, or an unverified head merely because it appears old. @@ -197,11 +201,14 @@ Before requesting review: scripts/validate.sh --pytest-scope tests/test_affected_module.py [more selectors ...] ``` - Use `scripts/validate.sh --full` instead when the change reaches shared - numerics, verification machinery, claim or release governance semantics, - public exports, dependencies, conventions, multiple framework sectors, or - has an uncertain dependency boundary. Claim promotions and releases always - require `--full`. + An additive public export may remain scoped when impact analysis shows a + bounded sector, no changed existing contract, known consumers, and targeted + API coverage. Use `scripts/validate.sh --full` instead when the change + reaches shared numerics, verification machinery, claim or release governance + semantics, changes an existing public contract with consumers, changes + dependencies or cross-cutting conventions, spans multiple framework + sectors, or has an uncertain dependency boundary. Claim promotions and + releases always require `--full`. 5. In a separate invocation, run: ```bash @@ -268,6 +275,15 @@ broken consumers, and unresolved debt within the proposed unit are blocking. Incomplete future work is campaign frontier and may remain open when a smaller unit is independently useful. +Treat `request changes`, `active refactor`, and `active harvest` as live review +states. A required-refactor finding must name an owner or handoff, a live source +or harvest PR, the exact repair, and the landing test. Keep the source PR open +while that finite path is active. Close unmerged only when all reusable atoms +land elsewhere, all remaining atoms have unit-level evidence of being +incorrect, non-novel, or unmaintainable, the owner explicitly withdraws the +work, or a landed replacement makes it redundant. A canon conflict, incomplete +dependency closure, or unavailable distinct merger is not by itself terminal. + End the review with an explicit disposition: ```text @@ -294,7 +310,10 @@ After the final disposition: - after a successful merge, confirm the exact same-repository PR head was automatically deleted and delete it explicitly if the repository setting did not do so; retain closed-unmerged or failed heads unless their owner explicitly - retires them; + retires them after recording the terminal-close rationale; +- keep a source PR open while a promised refactor or harvest is live; close it + as superseded only after the reusable unit lands, or close it unmerged only + after the terminal-close test in the review section passes; - leave the issue open with the next decisive action when the PR only advances it; - synchronize durable effort or decision memory with the landed commit and diff --git a/memory-templates/campaign-proposal.md b/memory-templates/campaign-proposal.md index 4e7f0ba4..ac3d06e2 100644 --- a/memory-templates/campaign-proposal.md +++ b/memory-templates/campaign-proposal.md @@ -32,7 +32,7 @@ For campaigns whose objective is external literature (a paper, a theory, a datas | --- | --- | --- | ## Invariants, Conventions, and Allowed Imports -Freeze what the campaign must preserve and every input it may use. Anything added later becomes explicit debt and requires proposal revision. +Freeze what the campaign must preserve and every input it may use. Accepted canon governs the base release but remains challengeable. Record evidence that would distinguish a candidate defect from an independent canonical inconsistency; anything added later becomes explicit debt and requires proposal revision. ## Candidate Preregistration Register at least two plausible concepts unless uniqueness is proved. Do not retrofit the framework after selecting one. @@ -52,7 +52,7 @@ List claims proposed or challenged, their dependencies, evidence plan, and consu Name importable APIs, claim-appropriate exact/numeric/formal oracles, mutations, counterexamples, refinements, independent routes, and global replay commands. Campaign verifiers run directly with `PYTHONPATH=src`; import reusable package APIs rather than repository scripts, which remain CLI adapters. Pin the campaign's own source, claim, and release evidence, but never make future valid work fail by asserting unrelated queue units stay pending or mutable `current` remains the historical release; replay old campaigns through durable snapshots or their canonical modules/tests. For each replay inventory, record lexical check-call sites, runtime check executions, and assertion nodes separately; loops and dynamic dispatch can make the runtime tally differ legitimately, so equality is not an oracle. Predeclare a compatibility preflight: canonical integration uses `trapezoid_integral`, mutable current-environment scripts use `np.trapezoid`, and executable syntax is checked for direct, imported, and dynamic legacy access. An eager fallback such as `getattr(np, "trapezoid", getattr(np, "trapz"))` is legacy access because the default is evaluated first. Repair mutable code to the current name or a safe two-step fallback; give immutable source an alias-only recorded replay before scientific adjudication. Do not count that native compatibility abort as candidate rejection. State why SymPy, Lean, or a particular SciPy method fits each obligation. Do not plan a numerical rerun as independent evidence when an exact result already fixes its right-hand side or output; classify it as regression coverage and prefer exact sensitivity or Taylor separation for tractable counterexamples. Before labeling a downstream tail, dispersion, normalization, or consistency route independent, eliminate shared intermediate variables and compare the resulting equations or positive solution sets. For cross-sector matches, freeze distinct field types, kinetic metrics, action measures, and coefficient conversions; equal symbols, shapes, or dimensions are not maps. Structural oracles must evaluate the claimed object rather than a literal boolean, stand-in constant, copied period, or unrelated bounded sample. For differential forms, predeclare the full graded Leibniz/cyclic expansion and keep nonvanishing, closedness, global non-exactness, period normalization, filling dependence, and gauge descent as separate gates. For genuinely unresolved ODE/BVP/PDE or quadrature work, specify precision, equations, domain, initial/boundary data, discretization, mesh/time/sample refinement, tolerances, error norm, invariants or controlled dissipation, solver-status gate, and method cross-check. For FFT differentiation or spectral line claims, freeze the active frequencies and window, require commensurability or measured endpoint closure, distinguish an identity on one FFT coefficient from independent evidence, and predeclare the claimed line's minimum norm or power fraction. Express near-zero and agreement thresholds in a declared dimensional or scale-relative error model, and keep exact analytic nulls separate from numerical roundoff regressions. ## Attempts and Continuation -Append failed routes with diagnoses and next candidates. An ill-fitting concept is rejected or reformulated; unrelated earlier work is not rewritten to save it. Execution runs in declared waves whose inputs are explicit; research/grounding is wave 0 and a dependent wave (implementation, verification, report) opens only after every input its wave declares has settled — a research subagent's output is a hard prerequisite, never a race (AP-15). +Append failed routes with diagnoses and next candidates. An ill-fitting concept is rejected or reformulated; unrelated earlier work is not rewritten to save it. If a conflict survives independently of the candidate, open a separate `challenges` or foundational-revision proposal rather than treating canon as irrevisable or silently changing it. Execution runs in declared waves whose inputs are explicit; research/grounding is wave 0 and a dependent wave (implementation, verification, report) opens only after every input its wave declares has settled — a research subagent's output is a hard prerequisite, never a race (AP-15). ## Debt Ledger Describe what this campaign's ledger tracks, then list new assumptions, @@ -60,7 +60,7 @@ imports, parameters, residuals, convention conflicts, and broken consumers. The campaign remains active until the ledger is empty. ## Review and Promotion Plan -Name claim-level reviewers, acceptance gates, package extraction, release update, generated documentation, and accepted-memory synchronization. State the resulting source-unit disposition; a partial migration must preserve its exact remaining subclaims. Terminal `qualified`, `refuted`, `duplicate_evidence`, and `out_of_scope` decisions must name their disposition-specific reason and durable evidence paths; use `qualified` for mixed units that also map accepted claims. Materialize each evidence path before registering it. For predecessor migration, edit `migration/dispositions.yaml` and regenerate `migration/source-claims.yaml` with `scripts/inventory_claims.py`; never maintain the generated queue by hand. Run validation and commit in separate process invocations; never let an unguarded later command mask an earlier failed gate. If a final attempt summarizes the promotion gate, create it with an explicit in-progress status, finalize it after the gate, and rerun only record-sensitive repository/generation checks rather than the unchanged full suite. +Name claim-level reviewers, acceptance gates, package extraction, release update, generated documentation, and accepted-memory synchronization. State the resulting source-unit disposition; a partial migration must preserve its exact remaining subclaims. For each `refactor then merge` atom, name its owner or handoff, live PR, exact repair, landing test, and source-PR lifecycle. Close unmerged only after the terminal-close test in `AGENTS.md`. Terminal `qualified`, `refuted`, `duplicate_evidence`, and `out_of_scope` decisions must name their disposition-specific reason and durable evidence paths; use `qualified` for mixed units that also map accepted claims. Materialize each evidence path before registering it. For predecessor migration, edit `migration/dispositions.yaml` and regenerate `migration/source-claims.yaml` with `scripts/inventory_claims.py`; never maintain the generated queue by hand. Record the impact-based scoped/full validation rationale and run validation and commit in separate process invocations; never let an unguarded later command mask an earlier failed gate. If a final attempt summarizes the promotion gate, create it with an explicit in-progress status, finalize it after the gate, and rerun only record-sensitive repository/generation checks rather than the unchanged full suite. ## Done Gate The campaign closes only on the complete positive success contract in `AGENTS.md`. If any gate fails, state the next attempt and continue. diff --git a/memory-templates/claim-review.md b/memory-templates/claim-review.md index 382f0af2..b1a37ef9 100644 --- a/memory-templates/claim-review.md +++ b/memory-templates/claim-review.md @@ -35,7 +35,7 @@ Assign one status and justify the maximum verdict earned. Confirm the oracle fit Record input mutations, wrong conventions, counterexamples, convergence/limit tests, and whether each relevant check failed when it should. For numerical claims independently inspect solver status, precision, initial/boundary data, mesh/domain/time/tolerance or quadrature refinement, residual or error norm, conservation/stability behavior, and an independent method or soluble limit. Confirm that near-zero and agreement bounds have a dimensional or scale-relative error model; preserve any failed absolute threshold and require refinement or roundoff evidence before accepting a repaired numerical oracle, without weakening a separately exact statement. ## Framework Compatibility -Assess invariants, assumptions, imports, parameters, units, conventions, limits, and cross-sector composition. If the concept does not fit, reject or return it for a different candidate; do not retrofit unrelated claims. +Assess invariants, assumptions, imports, parameters, units, conventions, limits, and cross-sector composition. If the concept does not fit, determine whether the mismatch is a candidate defect or independent evidence against accepted canon. Reject or return a defective candidate; route a surviving inconsistency through `challenges` or a separate foundational revision. State separately whether a correct conditional artifact can merge without promoting the disputed claim. ## Dependency and Consumer Replay List direct/indirect consumers and exact replay results. Preflight direct, imported, and dynamic legacy access, including eagerly evaluated nested `getattr` defaults. Repair mutable code to `np.trapezoid` or a safe two-step fallback, or record an alias-only replay for immutable source, before calling the consumer scientifically broken. Record any debt created by the claim. @@ -56,7 +56,7 @@ Record verification, review, compatibility, and epistemic status separately. Bef List registry edit, importable implementation/tests, immutable campaign record, release manifest, generated docs, accepted-memory synchronization, editable `migration/dispositions.yaml` update plus regenerated `migration/source-claims.yaml`, and validation commands. Check release closure against accepted registry membership, not only epistemically active claims; accepted qualified claims remain in the release. Use the governance validator rather than a review-local status subset. Never hand-edit the generated queue. For terminal qualification, refutation, duplicate, or scope decisions, record the structured reason and durable evidence path; an unsupported label is not an adjudication. ## Continuation if Not Accepted -Non-acceptance is not campaign success. Name the repair or next candidate and leave the parent effort active. If a foundation issue is independently demonstrated, link the separate revision proposal. +Non-acceptance is not campaign success and is not automatically PR closure. Name the repair or next candidate and leave the parent effort active. Record the source PR lifecycle, repair owner or handoff, live harvest/refactor PR, exact landing test, and terminal-close evidence if closure is proposed. If a foundation issue is independently demonstrated, link the separate revision proposal. ## Done Gate Accept only when the positive claim and every success gate in `AGENTS.md` pass with an empty debt ledger. diff --git a/memory-templates/delegated-continuation-pr-template.md b/memory-templates/delegated-continuation-pr-template.md index f07d4cb2..b0257f92 100644 --- a/memory-templates/delegated-continuation-pr-template.md +++ b/memory-templates/delegated-continuation-pr-template.md @@ -20,7 +20,7 @@ status: active State the user objective and all applicable success gates from `AGENTS.md`. Failure, no-go, residual, split, or partial progress cannot be checked as complete. ## Accepted Frontier -Record the pinned release, accepted claim boundary, live PR state, working-tree provenance, and exact unresolved positive objective. Correct stale status before selecting work. +Record the pinned release, accepted claim boundary, live PR lifecycle (`request changes`, `active refactor`, `active harvest`, or ready for review), working-tree provenance, and exact unresolved positive objective. Treat canon as release authority that may be challenged, not as an automatic terminal verdict. Correct stale status before selecting work. ## Proposal and Candidate Set Link the proposal manifest, invariants, allowed imports, at least two candidates, frozen selection criteria, and comparator-blinding gate. @@ -36,13 +36,16 @@ Use `$research-pr-harvest` whenever a local unit becomes complete. Keep utilitie - PR issue reference: <`Advances #N` or `Fixes #N`> - Authoring or implementing agent: - Distinct intended merger: +- Source PR lifecycle: +- Refactor owner/handoff and live PR: +- Terminal-close evidence: - Final issue handoff: | Unit | Local claim | Headline-independent? | Evidence | Merge disposition | | --- | --- | --- | --- | --- | | | | | | | -After the merge boundary is known, update the canonical issue with three unit-level lists: merged and rationale, requires refactor and rationale plus exact required change, and left in PR history and rationale. Link landed paths/commits, the reviewed PR, and the next decisive action. If GitHub mutation is not authorized, preserve the exact ready-to-post comment and mark the handoff pending. +After the merge boundary is known, update the canonical issue with three unit-level lists: merged and rationale, requires refactor and rationale plus exact required change, owner, live PR, and landing test, and left in PR history and rationale. Link landed paths/commits, the reviewed PR, and the next decisive action. Keep the source PR open while a finite refactor or harvest is live. Close unmerged only after the terminal-close test in `AGENTS.md`, and record the qualifying reason. If GitHub mutation is not authorized, preserve the exact ready-to-post comment and mark the handoff pending. ## Decomposition and Ownership List dependency-ordered local work and one child contract per authorized worker. Give workers disjoint write surfaces and raw sourced inputs. The agent that opens, commits to, or materially implements the PR may not merge it; reserve merge authority for the named distinct reviewer or repository owner. @@ -61,7 +64,7 @@ After every result, failed test, review, or apparent success, rerun this cycle. Record the result, whether it advances the positive success contract, the failed/remaining gate, diagnosed layer, next candidate or repair, changed artifact, retest, sensitivity audit, dependency replay, and canonical-state check. The decision is `continue` until every success gate passes. ## Framework-Fit and Foundation Gate -Record why the selected concept fits naturally. If it does not, reject it and try another. Any foundation change must live in a separate proposal with independent inconsistency evidence, alternate repairs, minimum-change rationale, migration map, and global replay. +Record why the selected concept fits naturally. If it does not, determine whether the mismatch is a candidate defect or independent evidence against canon. Reject a defective candidate and try another; route independent canonical inconsistency through a separate `challenges` or foundational-revision proposal with alternate repairs, minimum-change rationale, migration map, and global replay. A conflict blocks promotion, not a correct conditional artifact. ## Validation Ledger Every failed row creates the next task. @@ -74,6 +77,7 @@ Every failed row creates the next task. | Solver status, refinement, conservation/stability, and independent route where numerical | | | | | Global dependency replay | | | | | Importable API tests | | | | +| Impact analysis and scoped/full validation rationale | | | | | Registry/release validation | | | | | Generated docs and memory agree | | | | | Debt ledger empty | | | | @@ -88,7 +92,7 @@ List individually accepted claims, source API/tests, immutable campaign record, Record the verified positive result and every harvested unit. If the parent objective is incomplete, use `Advances #N`, preserve the exact next decisive action, and leave the goal active. A clean harvest handoff may end this PR or agent run without pretending the campaign is complete. ## Done Gate -For full campaign completion, check all success conditions from `AGENTS.md`, targeted checks, downstream replay, generated-state consistency, one `scripts/validate.sh` run at the unchanged promotion boundary, and empty debt. For a progress merge, require that the canonical issue predates the PR, a distinct merger is identified, and each harvested unit passes its local claim, dependency, convention, consumer, and validation gates; keep the campaign open and do not promote unsupported headline claims. +For full campaign completion, check all success conditions from `AGENTS.md`, targeted checks, downstream replay, generated-state consistency, one `scripts/validate.sh --full` run at the unchanged promotion boundary, and empty debt. For a progress merge, require that the canonical issue predates the PR, a distinct merger is identified, and each harvested unit passes its local claim, dependency, convention, consumer, impact, and proportionate scoped/full validation gates; keep the campaign and any live source-refactor PR open and do not promote unsupported headline claims. ## Cross-References Link parent/child contracts, proposal, claims, campaigns, source, tests, release, generated docs, and PR. diff --git a/memory-templates/effort-contract.md b/memory-templates/effort-contract.md index b8c49f92..a4b2227c 100644 --- a/memory-templates/effort-contract.md +++ b/memory-templates/effort-contract.md @@ -23,7 +23,7 @@ This effort delivers . It is complete only when the objec The work starts from release . Record accepted claims and source artifacts actually read; chronology and memory prose are not authority. ## Constraints and Invariants -State the user constraints, accepted invariants, conventions, units, permitted imports, and write boundaries. A new concept may not silently redefine them. +State the user constraints, accepted invariants, conventions, units, permitted imports, and write boundaries. A new concept may not silently redefine them. Accepted canon governs release and promotion but remains challengeable; record what independent evidence would trigger a separate revision path. ## Decomposition Work proceeds through these dependency-ordered steps and continues after failed attempts. @@ -55,7 +55,8 @@ Validation covers the actual objective, verifier sensitivity, limits, convention - Numerical solver-status, refinement, conservation/stability, and independent-route command (when applicable): - Dependency replay, after AST preflight for direct/imported/dynamic legacy trapezoid access and repair of mutable consumers to `np.trapezoid` or a safe two-step fallback, or an alias-only replay for immutable source: - Targeted tests during implementation: -- `scripts/validate.sh` (includes the full pytest suite; run once at the unchanged promotion boundary): +- Impact analysis and validation-scope rationale (an additive bounded API may stay scoped; promotion/release, shared machinery, changed existing contracts, cross-cutting dependencies/conventions, multi-sector, or uncertain impact requires full): +- `scripts/validate.sh --pytest-scope ` or `scripts/validate.sh --full` (run the justified workflow once at the unchanged boundary): - `git diff --check` (run separately from commit so a later command cannot mask failure): ## Debt Ledger @@ -65,7 +66,7 @@ Every new assumption, import, parameter, residual, broken consumer, or narrative | --- | --- | --- | --- | --- | ## Results -Record positive verified outcomes and exact reproduction commands. Failures belong in Attempts and keep this contract active. +Record positive verified outcomes and exact reproduction commands. Failures belong in Attempts and keep this contract active. For every `refactor then merge` unit, record the owner or handoff, live PR, landing test, and source-PR lifecycle; do not close it merely because dependency promotion remains incomplete. ## Canonicalization List claim-registry changes, release manifest, extracted APIs, immutable campaign record, generated docs, and accepted-memory synchronization. Confirm no proposal prose was merged into canonical memory. diff --git a/memory-templates/research-arc.md b/memory-templates/research-arc.md index bd59317e..41f5ffd8 100644 --- a/memory-templates/research-arc.md +++ b/memory-templates/research-arc.md @@ -21,7 +21,7 @@ status: active State the requested result precisely. Completion requires the result itself, accepted dependency closure, natural framework fit, a sensitive verifier, candidate comparison, global replay, importable implementation, accepted claim promotion, synchronized generated records, and an empty debt ledger. Honest failure and honest no-go reports are attempt evidence, never the deliverable. ## Authority and Prior Work -Record the accepted release, source commit, claim ids, importable modules, campaigns, memory searches, and graph/source queries actually checked. Distinguish accepted state, proposal state, and attempt state. +Record the accepted release, source commit, claim ids, importable modules, campaigns, memory searches, and graph/source queries actually checked. Distinguish accepted state, proposal state, and attempt state. Accepted canon controls release and promotion; it remains falsifiable, so record credible independent challenges rather than treating conflict as automatic rejection. - Accepted release: - Accepted claims reused: @@ -69,13 +69,16 @@ Record each locally complete unit as soon as it becomes independently correct an - Canonical goal issue: - PR issue reference: <`Advances #N` or `Fixes #N`> +- Source PR lifecycle: +- Refactor owner/handoff, live PR, and landing test:
+- Terminal-close evidence: - Final issue handoff: | Unit | Local claim | Independent of headline? | Evidence | Commit/PR | Disposition | | --- | --- | --- | --- | --- | --- | | | | | | | | -At the final harvest boundary, update the canonical issue with unit-level lists and rationales for what merged, what requires refactor and the exact required change, and what remains only in PR history. Include landed paths/commits and the next decisive action so a fresh agent can resume from the issue alone. +At the final harvest boundary, update the canonical issue with unit-level lists and rationales for what merged, what requires refactor plus its owner, live PR, exact change, and landing test, and what remains only in PR history. Include landed paths/commits and the next decisive action so a fresh agent can resume from the issue alone. Keep a source PR open while finite refactor or harvest work is active; close only after the terminal-close test in `AGENTS.md`. ## Attempts Append one row per scientific attempt. Preserve source, stdout/stderr, elapsed time, and exact command. A native immutable-source abort caused only by missing direct, imported, or dynamic `np.trapz`—including an eagerly evaluated nested fallback—is compatibility provenance: run an alias-only compatibility replay and use that replay for scientific adjudication rather than consuming or rejecting a candidate. Failure of the repaired scientific route triggers the next route; it never closes the arc. @@ -85,7 +88,7 @@ Append one row per scientific attempt. Preserve source, stdout/stderr, elapsed t | 0001 | | | | | | ## Framework-Fit Audit -Assess each candidate before empirical fit: invariant preservation, imports, free parameters, conventions, known limits, cross-sector consumers, and whether it demands unrelated narrative changes. Reject and replace an ill-fitting candidate before considering foundational revision. +Assess each candidate before empirical fit: invariant preservation, imports, free parameters, conventions, known limits, cross-sector consumers, and whether it demands unrelated narrative changes. Determine whether a mismatch is a candidate defect or independent evidence against canon. Reject and replace a defective candidate; route a surviving canonical inconsistency through the separate revision gate without blocking correct conditional artifacts. ## Verifier Audit Record clean exit and tally, derivation-vs-literal inspection, mutations, counterexamples, wrong conventions, refinement, conservation/limit tests, independent rederivation, and exact formal theorem/axioms where applicable. For SciPy ODE/BVP/PDE evidence, record routine/algorithm, precision, solver status, domain, mesh, time policy, tolerances, residual/error norm, convergence order, and a soluble limit or independent method. @@ -98,7 +101,7 @@ List direct and indirect consumers and every replay command. Preflight direct/im | | | | | | ## Foundational Revision Gate -Leave empty unless independent evidence shows accepted foundations are inconsistent. If opened, link a separate proposal containing the pre-existing inconsistency, two or more repair candidates, minimum-change decision, migration map, independent review, and full replay. Never use this section to rescue a favored concept. +Leave empty unless independent evidence shows accepted foundations are inconsistent. If opened, link a separate proposal containing the pre-existing inconsistency, two or more repair candidates, minimum-change decision, migration map, independent review, and full replay. This is an active advancement route when the evidence qualifies; never use it merely to rescue a favored concept. ## Debt Ledger Track every assumption, import, parameter, residual, unresolved convention, broken consumer, or documentation mismatch inside the scope proposed for promotion. Empty it before completion. Do not list the still-open parent objective or an unselected future route as debt. @@ -116,7 +119,7 @@ Record accepted positive results and reproduction commands. Also record harveste Record extracted APIs/tests, accepted registry entries, immutable campaign location, release id, generated docs command, accepted-memory synchronization, and proposal/attempt memory separation. ## Done Gate -Check every success condition from `AGENTS.md`, confirm targeted science checks, downstream replay, `scripts/validate.sh` (including its full test suite) once at the unchanged promotion boundary, and an empty debt ledger. Otherwise continue. +Check every success condition from `AGENTS.md`, confirm targeted science checks, downstream replay, `scripts/validate.sh --full` once at the unchanged promotion boundary, and an empty debt ledger. A progress harvest uses the impact-justified scoped/full workflow instead and leaves the arc active. Otherwise continue. ## Cross-References Link the proposal, source artifacts, claims, reviewers, campaign, release, generated outputs, and related memory entries. diff --git a/memory/codex/efforts/pr-lifecycle-active-harvest.md b/memory/codex/efforts/pr-lifecycle-active-harvest.md new file mode 100644 index 00000000..c2b8ec43 --- /dev/null +++ b/memory/codex/efforts/pr-lifecycle-active-harvest.md @@ -0,0 +1,65 @@ +--- +description: Keep viable scientific pull requests active through repair or harvest and scope validation by impact +author: codex +created: '2026-08-18T09:27:59Z' +updated: '2026-08-18T09:27:59Z' +tags: +- substrate-framework +- effort +- pull-request-policy +- research-harvest +category: efforts +confidence: working +status: active +--- + +## Goal and Success Contract +This effort corrects the PR process exposed by the premature closure of PR #77. It succeeds when viable work with a finite repair or harvest path remains active, terminal closure requires unit-level evidence, canon conflicts can open governed challenges instead of ending inquiry, validation scope follows measured impact, and the root contract, onboarding, PR template, research skills, and task templates agree. Issue #81 is canonical, and a distinct reviewer or repository owner must merge the corrective PR. + +## Accepted Baseline +The process change starts from accepted release `v0.160.0` and framework commit `da4d927`. The current release, accepted registry, PR #77 and goal #76, the prior issue-first workflow memory, both repository skills, affected task templates, Git history, and GitHub state were inspected. No accepted scientific claim, release manifest, generated documentation, migration disposition, or physics implementation changes in this effort. + +## Constraints and Invariants +Artifact merge, claim promotion, and goal completion remain independent. Accepted canon governs releases and promotion but remains scientifically challengeable. Non-self-merge, issue-first coordination, immutable campaign history, and branch protections remain in force. The correction consolidates existing clauses rather than creating a parallel review system, and it must not turn speculative or defective work into mergeable code. + +## Decomposition +The effort follows one bounded process transaction and one separately reviewed scientific harvest. + +1. [x] Restore and reopen PR #77 and correct its PR/issue handoff. +2. [x] Extract its independently reusable exact-mass unit into harvest PR #82 while leaving #77 open. +3. [x] Define active-refactor and terminal-close lifecycle rules. +4. [x] Make canon conflicts eligible for a separate challenge without weakening promotion authority. +5. [x] Replace the public-export full-suite trigger with impact-based scoped/full selection. +6. [x] Align AGENTS, onboarding, the PR template, both research skills, and five memory templates. +7. [x] Add and run a contribution-policy regression test plus record-sensitive validators. +8. [ ] Open the issue-#81 process PR and hand merge authority to a distinct reviewer or owner. + +## Attempts +The attempts preserve the process failures that motivated and tested the repair. + +| Attempt | Candidate or repair | Artifact and command | Verdict | Mechanism | Next attempt | +| --- | --- | --- | --- | --- | --- | +| 0001 | Close PR #77 after identifying unmet promotion dependencies | GitHub close and branch cleanup | rejected process action; reversed | The review conflated claim promotion with artifact merge and treated canonical dependency gaps as terminal | Restore the head, reopen the PR, and create a focused harvest | +| 0002 | Apply the categorical public-export full-suite rule to harvest #82 | `scripts/validate.sh --full` | stopped at the user's scope correction; not counted as validation | The rule ignored low measured impact and repeated integrated coverage | Run the focused API tests and scoped workflow; revise the trigger | +| 0003 | First lifecycle-policy regression assertion | scoped workflow for `tests/test_public_contribution_surfaces.py` | one assertion failed | The test expected `live PR` while the skill deliberately requires the stronger `live source or harvest PR` wording | Correct only the assertion and rerun the failed scoped boundary | + +## Validation +Validation is proportionate to this documentation/template and one-test surface. Both edited skills pass their native validators. GitNexus detects 35 changed documentation sections, zero affected processes, and low risk. `scripts/validate.sh --pytest-scope tests/test_public_contribution_surfaces.py` passes all fixed repository checks and 9 targeted tests; the earlier interrupted full run and failed scoped attempt are not counted. `git diff --check` passes separately. Adding this final effort record requires only absolute-path memory validation and repository-schema validation, not another pytest replay. + +## Debt Ledger +The process-unit debt ledger is empty; independent merge remains workflow state, not hidden debt. + +| Debt | Introduced by | Why it is real | Discharge artifact | Status | +| --- | --- | --- | --- | --- | + +## Results +PR #77 is open with its restored head, and focused harvest PR #82 advances goal #76 without promoting claims. The corrective policy makes `refactor then merge` active work with an owner, live PR, repair, and landing test; permits unmerged closure only under enumerated terminal conditions; treats accepted canon as authoritative but revisable; and allows bounded additive public APIs to use scoped validation when impact evidence supports it. + +## Canonicalization +This is a process-only correction. Durable policy lives in `AGENTS.md`, `AGENTS_START_HERE.md`, the PR template, both research skills, and the aligned memory templates. The regression test prevents those public contribution surfaces from silently returning to the failed behavior. + +## Done Gate +The local process unit is complete and validated, but this effort remains active until its issue-#81 PR is opened and a distinct reviewer or repository owner merges it. PR #77 remains active while harvest #82 is reviewed; neither an unpromoted dependency nor unavailable self-merge authority closes it. + +## Cross-References +Canonical process issue: https://github.com/vantasnerdan/substrate-framework/issues/81. Corrected source campaign: https://github.com/vantasnerdan/substrate-framework/pull/77. Focused scientific harvest: https://github.com/vantasnerdan/substrate-framework/pull/82. Predecessor process record: `memory/vantasner/efforts/issue-first-non-self-merge-policy.md`. diff --git a/tests/test_public_contribution_surfaces.py b/tests/test_public_contribution_surfaces.py index 008540f2..ea4bdaae 100644 --- a/tests/test_public_contribution_surfaces.py +++ b/tests/test_public_contribution_surfaces.py @@ -111,6 +111,27 @@ def test_issue_forms_preserve_issue_first_and_rights_boundaries() -> None: assert "issue" in serialized.lower() +def test_pr_policy_keeps_viable_harvests_active_and_validation_scoped() -> None: + contract = (ROOT / "AGENTS.md").read_text(encoding="utf-8") + onboarding = (ROOT / "AGENTS_START_HERE.md").read_text(encoding="utf-8") + harvest_skill = ( + ROOT / ".agents/skills/research-pr-harvest/SKILL.md" + ).read_text(encoding="utf-8") + pr_template = (ROOT / ".github/pull_request_template.md").read_text( + encoding="utf-8" + ) + + for surface in (contract, onboarding, harvest_skill): + assert "terminal-close" in surface + assert "active refactor" in surface + assert "additive public export" in surface + + assert "live source or harvest PR" in harvest_skill + assert "current canon" in harvest_skill + assert "Source PR lifecycle" in pr_template + assert "Terminal-close evidence" in pr_template + + def test_contribution_policy_blocks_sensitive_and_unlicensed_sources() -> None: contributing = (ROOT / "CONTRIBUTING.md").read_text(encoding="utf-8").lower() assert "credentials" in contributing From 8bafe84d887ba5dace73f66fbb2bbcdc11158b55 Mon Sep 17 00:00:00 2001 From: axiom-marbell Date: Tue, 18 Aug 2026 11:29:46 +0200 Subject: [PATCH 2/3] memory: link active-harvest process handoff Advances #81 by recording PR #83 and its distinct-merge boundary. --- memory/codex/efforts/pr-lifecycle-active-harvest.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/memory/codex/efforts/pr-lifecycle-active-harvest.md b/memory/codex/efforts/pr-lifecycle-active-harvest.md index c2b8ec43..b77d2659 100644 --- a/memory/codex/efforts/pr-lifecycle-active-harvest.md +++ b/memory/codex/efforts/pr-lifecycle-active-harvest.md @@ -2,7 +2,7 @@ description: Keep viable scientific pull requests active through repair or harvest and scope validation by impact author: codex created: '2026-08-18T09:27:59Z' -updated: '2026-08-18T09:27:59Z' +updated: '2026-08-18T09:29:22Z' tags: - substrate-framework - effort @@ -32,7 +32,7 @@ The effort follows one bounded process transaction and one separately reviewed s 5. [x] Replace the public-export full-suite trigger with impact-based scoped/full selection. 6. [x] Align AGENTS, onboarding, the PR template, both research skills, and five memory templates. 7. [x] Add and run a contribution-policy regression test plus record-sensitive validators. -8. [ ] Open the issue-#81 process PR and hand merge authority to a distinct reviewer or owner. +8. [x] Open process PR #83 for issue #81 and hand merge authority to a distinct reviewer or owner. ## Attempts The attempts preserve the process failures that motivated and tested the repair. @@ -59,7 +59,7 @@ PR #77 is open with its restored head, and focused harvest PR #82 advances goal This is a process-only correction. Durable policy lives in `AGENTS.md`, `AGENTS_START_HERE.md`, the PR template, both research skills, and the aligned memory templates. The regression test prevents those public contribution surfaces from silently returning to the failed behavior. ## Done Gate -The local process unit is complete and validated, but this effort remains active until its issue-#81 PR is opened and a distinct reviewer or repository owner merges it. PR #77 remains active while harvest #82 is reviewed; neither an unpromoted dependency nor unavailable self-merge authority closes it. +The local process unit is complete and validated in PR #83, but this effort remains active until a distinct reviewer or repository owner merges it. PR #77 remains active while harvest #82 is reviewed; neither an unpromoted dependency nor unavailable self-merge authority closes it. ## Cross-References -Canonical process issue: https://github.com/vantasnerdan/substrate-framework/issues/81. Corrected source campaign: https://github.com/vantasnerdan/substrate-framework/pull/77. Focused scientific harvest: https://github.com/vantasnerdan/substrate-framework/pull/82. Predecessor process record: `memory/vantasner/efforts/issue-first-non-self-merge-policy.md`. +Canonical process issue: https://github.com/vantasnerdan/substrate-framework/issues/81. Corrective process PR: https://github.com/vantasnerdan/substrate-framework/pull/83. Corrected source campaign: https://github.com/vantasnerdan/substrate-framework/pull/77. Focused scientific harvest: https://github.com/vantasnerdan/substrate-framework/pull/82. Predecessor process record: `memory/vantasner/efforts/issue-first-non-self-merge-policy.md`. From d7d71fc0d394b8e1614533d0c37d1e124d363d85 Mon Sep 17 00:00:00 2001 From: axiom-marbell Date: Tue, 18 Aug 2026 11:37:02 +0200 Subject: [PATCH 3/3] ci: select validation scope from PR impact Advances #81 by replacing unconditional PR-wide pytest with conservative full, scoped, or fixed-only selection and a periodic full backstop. --- .agents/skills/research-pr-harvest/SKILL.md | 2 +- .github/pull_request_template.md | 2 +- .github/workflows/validate.yml | 15 +- AGENTS.md | 6 +- AGENTS_START_HERE.md | 7 +- memory-templates/campaign-proposal.md | 2 +- .../delegated-continuation-pr-template.md | 2 +- memory-templates/effort-contract.md | 2 +- memory-templates/research-arc.md | 2 +- .../efforts/pr-lifecycle-active-harvest.md | 9 +- scripts/validate.sh | 20 +- scripts/validate_changed.py | 263 ++++++++++++++++++ tests/test_public_contribution_surfaces.py | 7 + tests/test_validate_changed.py | 140 ++++++++++ tests/test_validate_script.py | 17 ++ 15 files changed, 475 insertions(+), 21 deletions(-) create mode 100644 scripts/validate_changed.py create mode 100644 tests/test_validate_changed.py diff --git a/.agents/skills/research-pr-harvest/SKILL.md b/.agents/skills/research-pr-harvest/SKILL.md index 9ea2d897..5278f2f7 100644 --- a/.agents/skills/research-pr-harvest/SKILL.md +++ b/.agents/skills/research-pr-harvest/SKILL.md @@ -162,7 +162,7 @@ An individual run may end at a clean handoff without lowering or closing the cam ## Validate proportionately -Run targeted tests and claim-appropriate oracles for each harvested unit, impact analysis for changed public symbols, affected consumers, and one repository validation at the final unchanged merge boundary. An additive public export may use scoped validation when impact is bounded, no existing contract changes, consumers are known, and targeted API coverage passes. Reserve full validation for promotion or release, shared numerics or verification machinery, claim/release governance semantics, changed existing public contracts with consumers, dependency or cross-cutting convention changes, multi-sector changes, or uncertain impact. Do not rerun unrelated full validation for each discarded campaign artifact. +Run targeted tests and claim-appropriate oracles for each harvested unit, impact analysis for changed public symbols, affected consumers, and one repository validation at the final unchanged merge boundary. An additive public export may use scoped validation when impact is bounded, no existing contract changes, consumers are known, and targeted API coverage passes. Reserve full validation for promotion or release, shared numerics or verification machinery, claim/release governance semantics, changed existing public contracts with consumers, dependency or cross-cutting convention changes, multi-sector changes, or uncertain impact. Pull-request CI uses `scripts/validate_changed.py` for this conservative decision and uses fixed checks only when no pytest scope is affected; scheduled/manual CI supplies the periodic full backstop. Do not rerun unrelated full validation for each discarded campaign artifact or again after merge. ## Report the disposition diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f2ebb6e1..a9528c15 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -97,7 +97,7 @@ the export alone as a full-suite trigger. - [ ] Targeted tests and named scientific verifiers pass. - [ ] Load-bearing mutations, counterexamples, or wrong-convention probes fail as expected. - [ ] Affected downstream consumers replay. -- [ ] `scripts/validate.sh --pytest-scope ...` passes with the exact selectors recorded, or the full-suite trigger is explained and `scripts/validate.sh --full` passes. +- [ ] `scripts/validate.sh --pytest-scope ...` passes with the exact selectors recorded, `--fixed-only` is justified by no affected pytest scope, or the full-suite trigger is explained and `scripts/validate.sh --full` passes; PR CI's `scripts/validate_changed.py` decision agrees. - [ ] The pytest scope remains valid against the merge base; an equivalent unchanged validation is not duplicated. - [ ] `git diff --check` passes in a separate invocation. - [ ] No unrelated, generated-by-hand, or host-specific artifacts are included. diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index d0977256..73556538 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -2,9 +2,8 @@ name: Repository validation on: pull_request: - push: - branches: - - main + schedule: + - cron: '17 3 * * 1' workflow_dispatch: permissions: @@ -23,6 +22,7 @@ jobs: - name: Check out the repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + fetch-depth: 0 persist-credentials: false - name: Set up Python @@ -38,5 +38,12 @@ jobs: - name: Bootstrap the repository run: scripts/bootstrap.sh - - name: Run full repository validation + - name: Run impact-scoped pull-request validation + if: github.event_name == 'pull_request' + env: + VALIDATION_BASE_SHA: ${{ github.event.pull_request.base.sha }} + run: python scripts/validate_changed.py --base "$VALIDATION_BASE_SHA" --head HEAD + + - name: Run periodic or manually requested full validation + if: github.event_name != 'pull_request' run: scripts/validate.sh --full diff --git a/AGENTS.md b/AGENTS.md index 3422187e..70de1850 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -206,7 +206,11 @@ governance semantics, changes an existing public contract with consumers, changes dependencies or cross-cutting conventions, spans multiple framework sectors, or has an uncertain dependency boundary. Calling `scripts/validate.sh` without arguments remains a backward-compatible alias for -`--full`. +`--full`. Pull-request CI runs `scripts/validate_changed.py` against the base +commit: it selects affected tests for bounded changes, uses `--fixed-only` when +no pytest scope is affected, and falls back to `--full` for the triggers above. +Scheduled or manually requested CI supplies the periodic integrated-main full +backstop; do not duplicate it on every merge push. Do not run the full suite a second time at the same unchanged boundary. Use targeted tests while developing, then run the appropriate scoped or full diff --git a/AGENTS_START_HERE.md b/AGENTS_START_HERE.md index 24338d58..8e8c5a48 100644 --- a/AGENTS_START_HERE.md +++ b/AGENTS_START_HERE.md @@ -219,8 +219,11 @@ Record the exact pytest selectors, commands, status codes, and meaningful verdicts in the PR; a scoped pass is not a repository-wide pass. A bounded PR can remain scoped through merge when its impact boundary is still valid against the current base. Do not repeat an equivalent validation at the same unchanged -boundary. Run the full suite periodically on integrated `main` as a backstop; a -pass count alone is not a review. +boundary. Pull-request CI uses `scripts/validate_changed.py` to reproduce a +conservative changed-file decision, including `--fixed-only` when no pytest +scope is affected and `--full` for cross-cutting or uncertain changes. Scheduled +or manual CI runs the periodic integrated-main full backstop rather than +duplicating it on every merge push; a pass count alone is not a review. ## 8. Open the pull request diff --git a/memory-templates/campaign-proposal.md b/memory-templates/campaign-proposal.md index ac3d06e2..e18b02c6 100644 --- a/memory-templates/campaign-proposal.md +++ b/memory-templates/campaign-proposal.md @@ -60,7 +60,7 @@ imports, parameters, residuals, convention conflicts, and broken consumers. The campaign remains active until the ledger is empty. ## Review and Promotion Plan -Name claim-level reviewers, acceptance gates, package extraction, release update, generated documentation, and accepted-memory synchronization. State the resulting source-unit disposition; a partial migration must preserve its exact remaining subclaims. For each `refactor then merge` atom, name its owner or handoff, live PR, exact repair, landing test, and source-PR lifecycle. Close unmerged only after the terminal-close test in `AGENTS.md`. Terminal `qualified`, `refuted`, `duplicate_evidence`, and `out_of_scope` decisions must name their disposition-specific reason and durable evidence paths; use `qualified` for mixed units that also map accepted claims. Materialize each evidence path before registering it. For predecessor migration, edit `migration/dispositions.yaml` and regenerate `migration/source-claims.yaml` with `scripts/inventory_claims.py`; never maintain the generated queue by hand. Record the impact-based scoped/full validation rationale and run validation and commit in separate process invocations; never let an unguarded later command mask an earlier failed gate. If a final attempt summarizes the promotion gate, create it with an explicit in-progress status, finalize it after the gate, and rerun only record-sensitive repository/generation checks rather than the unchanged full suite. +Name claim-level reviewers, acceptance gates, package extraction, release update, generated documentation, and accepted-memory synchronization. State the resulting source-unit disposition; a partial migration must preserve its exact remaining subclaims. For each `refactor then merge` atom, name its owner or handoff, live PR, exact repair, landing test, and source-PR lifecycle. Close unmerged only after the terminal-close test in `AGENTS.md`. Terminal `qualified`, `refuted`, `duplicate_evidence`, and `out_of_scope` decisions must name their disposition-specific reason and durable evidence paths; use `qualified` for mixed units that also map accepted claims. Materialize each evidence path before registering it. For predecessor migration, edit `migration/dispositions.yaml` and regenerate `migration/source-claims.yaml` with `scripts/inventory_claims.py`; never maintain the generated queue by hand. Record the impact-based fixed-only/scoped/full validation rationale and the agreeing `scripts/validate_changed.py` CI decision, and run validation and commit in separate process invocations; never let an unguarded later command mask an earlier failed gate. If a final attempt summarizes the promotion gate, create it with an explicit in-progress status, finalize it after the gate, and rerun only record-sensitive repository/generation checks rather than the unchanged full suite. ## Done Gate The campaign closes only on the complete positive success contract in `AGENTS.md`. If any gate fails, state the next attempt and continue. diff --git a/memory-templates/delegated-continuation-pr-template.md b/memory-templates/delegated-continuation-pr-template.md index b0257f92..5096c871 100644 --- a/memory-templates/delegated-continuation-pr-template.md +++ b/memory-templates/delegated-continuation-pr-template.md @@ -92,7 +92,7 @@ List individually accepted claims, source API/tests, immutable campaign record, Record the verified positive result and every harvested unit. If the parent objective is incomplete, use `Advances #N`, preserve the exact next decisive action, and leave the goal active. A clean harvest handoff may end this PR or agent run without pretending the campaign is complete. ## Done Gate -For full campaign completion, check all success conditions from `AGENTS.md`, targeted checks, downstream replay, generated-state consistency, one `scripts/validate.sh --full` run at the unchanged promotion boundary, and empty debt. For a progress merge, require that the canonical issue predates the PR, a distinct merger is identified, and each harvested unit passes its local claim, dependency, convention, consumer, impact, and proportionate scoped/full validation gates; keep the campaign and any live source-refactor PR open and do not promote unsupported headline claims. +For full campaign completion, check all success conditions from `AGENTS.md`, targeted checks, downstream replay, generated-state consistency, one `scripts/validate.sh --full` run at the unchanged promotion boundary, and empty debt. For a progress merge, require that the canonical issue predates the PR, a distinct merger is identified, and each harvested unit passes its local claim, dependency, convention, consumer, impact, and proportionate fixed-only/scoped/full validation gates; record the `scripts/validate_changed.py` CI decision, keep the campaign and any live source-refactor PR open, and do not promote unsupported headline claims. ## Cross-References Link parent/child contracts, proposal, claims, campaigns, source, tests, release, generated docs, and PR. diff --git a/memory-templates/effort-contract.md b/memory-templates/effort-contract.md index a4b2227c..8c57431a 100644 --- a/memory-templates/effort-contract.md +++ b/memory-templates/effort-contract.md @@ -56,7 +56,7 @@ Validation covers the actual objective, verifier sensitivity, limits, convention - Dependency replay, after AST preflight for direct/imported/dynamic legacy trapezoid access and repair of mutable consumers to `np.trapezoid` or a safe two-step fallback, or an alias-only replay for immutable source: - Targeted tests during implementation: - Impact analysis and validation-scope rationale (an additive bounded API may stay scoped; promotion/release, shared machinery, changed existing contracts, cross-cutting dependencies/conventions, multi-sector, or uncertain impact requires full): -- `scripts/validate.sh --pytest-scope ` or `scripts/validate.sh --full` (run the justified workflow once at the unchanged boundary): +- `scripts/validate.sh --fixed-only`, `--pytest-scope `, or `--full` plus the agreeing `scripts/validate_changed.py` CI decision (run the justified workflow once at the unchanged boundary): - `git diff --check` (run separately from commit so a later command cannot mask failure): ## Debt Ledger diff --git a/memory-templates/research-arc.md b/memory-templates/research-arc.md index 41f5ffd8..b77cc68f 100644 --- a/memory-templates/research-arc.md +++ b/memory-templates/research-arc.md @@ -119,7 +119,7 @@ Record accepted positive results and reproduction commands. Also record harveste Record extracted APIs/tests, accepted registry entries, immutable campaign location, release id, generated docs command, accepted-memory synchronization, and proposal/attempt memory separation. ## Done Gate -Check every success condition from `AGENTS.md`, confirm targeted science checks, downstream replay, `scripts/validate.sh --full` once at the unchanged promotion boundary, and an empty debt ledger. A progress harvest uses the impact-justified scoped/full workflow instead and leaves the arc active. Otherwise continue. +Check every success condition from `AGENTS.md`, confirm targeted science checks, downstream replay, `scripts/validate.sh --full` once at the unchanged promotion boundary, and an empty debt ledger. A progress harvest uses the impact-justified fixed-only/scoped/full workflow and records the agreeing `scripts/validate_changed.py` CI decision instead; it leaves the arc active. Otherwise continue. ## Cross-References Link the proposal, source artifacts, claims, reviewers, campaign, release, generated outputs, and related memory entries. diff --git a/memory/codex/efforts/pr-lifecycle-active-harvest.md b/memory/codex/efforts/pr-lifecycle-active-harvest.md index b77d2659..44e9dc4b 100644 --- a/memory/codex/efforts/pr-lifecycle-active-harvest.md +++ b/memory/codex/efforts/pr-lifecycle-active-harvest.md @@ -2,7 +2,7 @@ description: Keep viable scientific pull requests active through repair or harvest and scope validation by impact author: codex created: '2026-08-18T09:27:59Z' -updated: '2026-08-18T09:29:22Z' +updated: '2026-08-18T09:35:01Z' tags: - substrate-framework - effort @@ -31,7 +31,7 @@ The effort follows one bounded process transaction and one separately reviewed s 4. [x] Make canon conflicts eligible for a separate challenge without weakening promotion authority. 5. [x] Replace the public-export full-suite trigger with impact-based scoped/full selection. 6. [x] Align AGENTS, onboarding, the PR template, both research skills, and five memory templates. -7. [x] Add and run a contribution-policy regression test plus record-sensitive validators. +7. [x] Add a conservative changed-file CI selector, fixed-only mode, and focused regression tests so PRs do not replay the full suite mechanically. 8. [x] Open process PR #83 for issue #81 and hand merge authority to a distinct reviewer or owner. ## Attempts @@ -42,9 +42,10 @@ The attempts preserve the process failures that motivated and tested the repair. | 0001 | Close PR #77 after identifying unmet promotion dependencies | GitHub close and branch cleanup | rejected process action; reversed | The review conflated claim promotion with artifact merge and treated canonical dependency gaps as terminal | Restore the head, reopen the PR, and create a focused harvest | | 0002 | Apply the categorical public-export full-suite rule to harvest #82 | `scripts/validate.sh --full` | stopped at the user's scope correction; not counted as validation | The rule ignored low measured impact and repeated integrated coverage | Run the focused API tests and scoped workflow; revise the trigger | | 0003 | First lifecycle-policy regression assertion | scoped workflow for `tests/test_public_contribution_surfaces.py` | one assertion failed | The test expected `live PR` while the skill deliberately requires the stronger `live source or harvest PR` wording | Correct only the assertion and rerun the failed scoped boundary | +| 0004 | Verify final GitHub execution state | PR #82 check rollup and `.github/workflows/validate.yml` | CI optimization gap found | The workflow still hard-coded `--full` for every PR and would negate the local impact rule | Add `validate_changed.py`, fixed-only support, conservative full triggers, and a scheduled/manual full backstop | ## Validation -Validation is proportionate to this documentation/template and one-test surface. Both edited skills pass their native validators. GitNexus detects 35 changed documentation sections, zero affected processes, and low risk. `scripts/validate.sh --pytest-scope tests/test_public_contribution_surfaces.py` passes all fixed repository checks and 9 targeted tests; the earlier interrupted full run and failed scoped attempt are not counted. `git diff --check` passes separately. Adding this final effort record requires only absolute-path memory validation and repository-schema validation, not another pytest replay. +Validation is proportionate to this process, selector, and regression-test surface. Both edited skills pass their native validators. GitNexus reports low risk and no affected execution process. The final workflow command is `scripts/validate.sh --pytest-scope tests/test_public_contribution_surfaces.py tests/test_repository_validation.py tests/test_validate_changed.py tests/test_validate_script.py`; its 32 focused tests cover public policy, fixed checks, full/scoped/fixed-only dispatch, conservative source/governance/removal triggers, and workflow wiring. The earlier interrupted full run and failed scoped attempt are not counted. The final effort record passes absolute-path memory and repository-schema validation, and `git diff --check` passes separately. ## Debt Ledger The process-unit debt ledger is empty; independent merge remains workflow state, not hidden debt. @@ -53,7 +54,7 @@ The process-unit debt ledger is empty; independent merge remains workflow state, | --- | --- | --- | --- | --- | ## Results -PR #77 is open with its restored head, and focused harvest PR #82 advances goal #76 without promoting claims. The corrective policy makes `refactor then merge` active work with an owner, live PR, repair, and landing test; permits unmerged closure only under enumerated terminal conditions; treats accepted canon as authoritative but revisable; and allows bounded additive public APIs to use scoped validation when impact evidence supports it. +PR #77 is open with its restored head, and focused harvest PR #82 advances goal #76 without promoting claims. The corrective policy makes `refactor then merge` active work with an owner, live PR, repair, and landing test; permits unmerged closure only under enumerated terminal conditions; treats accepted canon as authoritative but revisable; and allows bounded additive public APIs to use scoped validation when impact evidence supports it. Pull-request CI now makes that same conservative decision automatically; periodic or manually requested CI retains the integrated full-suite backstop. ## Canonicalization This is a process-only correction. Durable policy lives in `AGENTS.md`, `AGENTS_START_HERE.md`, the PR template, both research skills, and the aligned memory templates. The regression test prevents those public contribution surfaces from silently returning to the failed behavior. diff --git a/scripts/validate.sh b/scripts/validate.sh index 37df620e..affed014 100755 --- a/scripts/validate.sh +++ b/scripts/validate.sh @@ -6,13 +6,15 @@ usage() { Usage: scripts/validate.sh scripts/validate.sh --full + scripts/validate.sh --fixed-only scripts/validate.sh --pytest-scope SELECTOR [SELECTOR ...] Every mode runs the repository, generated-state, memory, skill, import, and -compile checks. With no arguments or --full, pytest runs the complete suite. -Arguments after --pytest-scope must be repository test files, directories, or -node IDs. Pytest options are rejected so collection-only or similar flags cannot -be mistaken for executed validation. +compile checks. With no arguments or --full, pytest runs the complete suite; +--fixed-only runs no pytest and is appropriate only when impact selection finds +no affected test. Arguments after --pytest-scope must be repository test files, +directories, or node IDs. Pytest options are rejected so collection-only or +similar flags cannot be mistaken for executed validation. EOF } @@ -28,6 +30,14 @@ case "${1:-}" in exit 2 fi ;; + --fixed-only) + if [ "$#" -ne 1 ]; then + echo "ERROR: --fixed-only does not accept additional arguments" >&2 + usage >&2 + exit 2 + fi + pytest_mode="fixed-only" + ;; --pytest-scope) shift if [ "$#" -eq 0 ]; then @@ -97,6 +107,8 @@ memory validate "$repo_root/memory" if [ "$pytest_mode" = "full" ]; then PYTHONPATH="$repo_root/src" "$python_bin" -m pytest -q echo "ALL REPOSITORY WORKFLOW CHECKS PASS (full pytest suite)" +elif [ "$pytest_mode" = "fixed-only" ]; then + echo "ALL FIXED REPOSITORY CHECKS PASS (no affected pytest scope)" else printf 'Running requested pytest scope:' printf ' %q' "${pytest_args[@]}" diff --git a/scripts/validate_changed.py b/scripts/validate_changed.py new file mode 100644 index 00000000..d8eff29d --- /dev/null +++ b/scripts/validate_changed.py @@ -0,0 +1,263 @@ +#!/usr/bin/env python3 +"""Select the smallest safe repository-validation mode for a pull request.""" + +from __future__ import annotations + +import argparse +from dataclasses import dataclass +from pathlib import Path +import subprocess +import sys + + +ROOT = Path(__file__).resolve().parents[1] + +FULL_EXACT_PATHS = { + ".agent-memory.yaml", + "pyproject.toml", + "tests/conftest.py", + "src/substrate_framework/exact_symbolic.py", + "src/substrate_framework/numerics.py", + "src/substrate_framework/verification.py", + "tools/agent-memory/pyproject.toml", +} +FULL_PREFIXES = ( + "governance/", + "migration/", + "tools/agent-memory/src/", +) +PROCESS_POLICY_PATHS = { + "AGENTS.md", + "AGENTS_START_HERE.md", + "CONTRIBUTING.md", + ".github/pull_request_template.md", +} +VALIDATION_DRIVER_PATHS = { + ".github/workflows/validate.yml", + "scripts/validate.sh", + "scripts/validate_changed.py", +} + + +@dataclass(frozen=True) +class Change: + status: str + path: str + + +@dataclass(frozen=True) +class ValidationDecision: + mode: str + selectors: tuple[str, ...] + reasons: tuple[str, ...] + + +def parse_name_status(output: str) -> list[Change]: + """Parse ``git diff --name-status`` output, using the destination path.""" + + changes: list[Change] = [] + for line in output.splitlines(): + fields = line.split("\t") + if len(fields) < 2: + continue + raw_status = fields[0] + status = raw_status[:1] + path = fields[-1] if status in {"R", "C"} else fields[1] + changes.append(Change(status=status, path=path)) + return changes + + +def _has_removed_content(diff: str) -> bool: + return any( + line.startswith("-") and not line.startswith("---") + for line in diff.splitlines() + ) + + +def _existing_selector(path: str, repo_root: Path) -> str | None: + return path if (repo_root / path.split("::", 1)[0]).is_file() else None + + +def choose_validation_scope( + changes: list[Change], + *, + repo_root: Path = ROOT, + package_init_diff: str = "", +) -> ValidationDecision: + """Return a conservative full, scoped, or fixed-only validation decision.""" + + paths = {change.path for change in changes} + full_reasons: list[str] = [] + selectors: set[str] = set() + + for path in sorted(paths): + if path in FULL_EXACT_PATHS or path.startswith(FULL_PREFIXES): + full_reasons.append(f"cross-cutting path changed: {path}") + + package_changes = [ + change + for change in changes + if change.path.startswith("src/substrate_framework/") + and change.path.endswith(".py") + and change.path != "src/substrate_framework/__init__.py" + ] + added_modules = [change for change in package_changes if change.status == "A"] + changed_existing_modules = [ + change for change in package_changes if change.status != "A" + ] + if changed_existing_modules: + names = ", ".join(sorted(change.path for change in changed_existing_modules)) + full_reasons.append(f"existing framework module changed: {names}") + if len(added_modules) > 1: + full_reasons.append("multiple framework modules added; sector boundary is uncertain") + for change in added_modules: + stem = Path(change.path).stem + expected = f"tests/test_{stem}.py" + selector = _existing_selector(expected, repo_root) + if selector is None: + full_reasons.append(f"new framework module has no matching test file: {change.path}") + else: + selectors.add(selector) + + package_init_changed = "src/substrate_framework/__init__.py" in paths + if package_init_changed and _has_removed_content(package_init_diff): + full_reasons.append("package public surface removes or changes existing content") + elif package_init_changed and not added_modules: + full_reasons.append( + "package public surface changed without one tested new framework module" + ) + + removed_or_renamed_tests = [ + change + for change in changes + if ( + change.path.startswith("tests/") + or change.path.startswith("tools/agent-memory/tests/") + ) + and change.path.endswith(".py") + and change.status in {"D", "R"} + ] + if removed_or_renamed_tests: + full_reasons.append("test coverage was removed or renamed") + + unknown_scripts = { + path + for path in paths + if path.startswith("scripts/") + and path.endswith((".py", ".sh")) + and path not in VALIDATION_DRIVER_PATHS + } + if unknown_scripts: + full_reasons.append( + "validation impact is uncertain for executable scripts: " + + ", ".join(sorted(unknown_scripts)) + ) + + if full_reasons: + return ValidationDecision("full", (), tuple(full_reasons)) + + for path in sorted(paths): + if ( + path.startswith("tests/") + or path.startswith("tools/agent-memory/tests/") + ) and path.endswith(".py"): + selector = _existing_selector(path, repo_root) + if selector is not None: + selectors.add(selector) + + policy_changed = bool(paths & PROCESS_POLICY_PATHS) or any( + path.startswith(".agents/skills/") + or path.startswith("memory-templates/") + for path in paths + ) + if policy_changed: + for path in ( + "tests/test_public_contribution_surfaces.py", + "tests/test_repository_validation.py", + ): + selector = _existing_selector(path, repo_root) + if selector is not None: + selectors.add(selector) + + if paths & VALIDATION_DRIVER_PATHS: + for path in ( + "tests/test_validate_script.py", + "tests/test_validate_changed.py", + "tests/test_public_contribution_surfaces.py", + ): + selector = _existing_selector(path, repo_root) + if selector is not None: + selectors.add(selector) + + if selectors: + return ValidationDecision( + "scoped", + tuple(sorted(selectors)), + ("affected tests selected from changed paths",), + ) + return ValidationDecision( + "fixed-only", + (), + ("no changed path maps to an affected pytest scope",), + ) + + +def _git_output(*args: str) -> str: + result = subprocess.run( + ["git", *args], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + return result.stdout + + +def decision_for_refs(base: str, head: str) -> ValidationDecision: + changes = parse_name_status( + _git_output("diff", "--name-status", "--find-renames", f"{base}...{head}") + ) + init_diff = _git_output( + "diff", + "--unified=0", + f"{base}...{head}", + "--", + "src/substrate_framework/__init__.py", + ) + return choose_validation_scope(changes, package_init_diff=init_diff) + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--base", required=True, help="base commit or ref") + parser.add_argument("--head", default="HEAD", help="head commit or ref") + parser.add_argument( + "--print-only", + action="store_true", + help="print the decision without running validation", + ) + arguments = parser.parse_args(argv) + + decision = decision_for_refs(arguments.base, arguments.head) + print(f"Validation mode: {decision.mode}") + for reason in decision.reasons: + print(f"Reason: {reason}") + if decision.selectors: + print("Pytest selectors:") + for selector in decision.selectors: + print(f" {selector}") + if arguments.print_only: + return 0 + + command = [str(ROOT / "scripts/validate.sh")] + if decision.mode == "full": + command.append("--full") + elif decision.mode == "fixed-only": + command.append("--fixed-only") + else: + command.extend(("--pytest-scope", *decision.selectors)) + return subprocess.run(command, cwd=ROOT, check=False).returncode + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/test_public_contribution_surfaces.py b/tests/test_public_contribution_surfaces.py index ea4bdaae..cdb46532 100644 --- a/tests/test_public_contribution_surfaces.py +++ b/tests/test_public_contribution_surfaces.py @@ -77,7 +77,11 @@ def test_pull_request_workflow_is_least_privilege_and_sha_pinned() -> None: assert "secrets." not in workflow assert re.search(r"(?m)^permissions:\n contents: read$", workflow) assert "persist-credentials: false" in workflow + assert "scripts/validate_changed.py" in workflow + assert "Run impact-scoped pull-request validation" in workflow + assert "schedule:" in workflow assert "scripts/validate.sh --full" in workflow + assert "push:" not in workflow assert "timeout-minutes:" in workflow uses_lines = [line.strip() for line in workflow.splitlines() if "uses:" in line] @@ -130,6 +134,9 @@ def test_pr_policy_keeps_viable_harvests_active_and_validation_scoped() -> None: assert "current canon" in harvest_skill assert "Source PR lifecycle" in pr_template assert "Terminal-close evidence" in pr_template + assert "scripts/validate_changed.py" in ( + ROOT / ".github/workflows/validate.yml" + ).read_text(encoding="utf-8") def test_contribution_policy_blocks_sensitive_and_unlicensed_sources() -> None: diff --git a/tests/test_validate_changed.py b/tests/test_validate_changed.py new file mode 100644 index 00000000..b053a3c3 --- /dev/null +++ b/tests/test_validate_changed.py @@ -0,0 +1,140 @@ +from __future__ import annotations + +from pathlib import Path + +from scripts.validate_changed import ( + Change, + choose_validation_scope, + parse_name_status, +) + + +def _touch(root: Path, *paths: str) -> None: + for path in paths: + target = root / path + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text("# test fixture\n", encoding="utf-8") + + +def test_name_status_parser_uses_rename_destination() -> None: + changes = parse_name_status( + "M\tAGENTS.md\nA\ttests/test_new.py\nR100\told.py\tnew.py\n" + ) + assert changes == [ + Change("M", "AGENTS.md"), + Change("A", "tests/test_new.py"), + Change("R", "new.py"), + ] + + +def test_policy_change_selects_policy_and_repository_tests(tmp_path: Path) -> None: + _touch( + tmp_path, + "tests/test_public_contribution_surfaces.py", + "tests/test_repository_validation.py", + ) + decision = choose_validation_scope( + [Change("M", "AGENTS.md")], repo_root=tmp_path + ) + assert decision.mode == "scoped" + assert decision.selectors == ( + "tests/test_public_contribution_surfaces.py", + "tests/test_repository_validation.py", + ) + + +def test_unmapped_documentation_change_uses_fixed_checks_only(tmp_path: Path) -> None: + decision = choose_validation_scope( + [Change("M", "README.md")], repo_root=tmp_path + ) + assert decision.mode == "fixed-only" + assert decision.selectors == () + + +def test_new_module_with_matching_test_and_additive_export_is_scoped( + tmp_path: Path, +) -> None: + _touch(tmp_path, "tests/test_new_atom.py") + decision = choose_validation_scope( + [ + Change("A", "src/substrate_framework/new_atom.py"), + Change("M", "src/substrate_framework/__init__.py"), + Change("A", "tests/test_new_atom.py"), + ], + repo_root=tmp_path, + package_init_diff="@@ -1,0 +2 @@\n+from .new_atom import value\n", + ) + assert decision.mode == "scoped" + assert decision.selectors == ("tests/test_new_atom.py",) + + +def test_new_module_without_matching_test_forces_full(tmp_path: Path) -> None: + decision = choose_validation_scope( + [Change("A", "src/substrate_framework/untested.py")], + repo_root=tmp_path, + ) + assert decision.mode == "full" + assert "no matching test" in decision.reasons[0] + + +def test_existing_framework_module_change_forces_full(tmp_path: Path) -> None: + decision = choose_validation_scope( + [Change("M", "src/substrate_framework/model.py")], + repo_root=tmp_path, + ) + assert decision.mode == "full" + assert "existing framework module" in decision.reasons[0] + + +def test_removed_package_export_forces_full(tmp_path: Path) -> None: + decision = choose_validation_scope( + [Change("M", "src/substrate_framework/__init__.py")], + repo_root=tmp_path, + package_init_diff="@@ -2 +1,0 @@\n-from .old_api import value\n", + ) + assert decision.mode == "full" + assert "public surface" in decision.reasons[0] + + +def test_unpaired_additive_package_export_forces_full(tmp_path: Path) -> None: + decision = choose_validation_scope( + [Change("M", "src/substrate_framework/__init__.py")], + repo_root=tmp_path, + package_init_diff="@@ -1,0 +2 @@\n+from .existing_api import value\n", + ) + assert decision.mode == "full" + assert "without one tested new framework module" in decision.reasons[0] + + +def test_removed_test_forces_full(tmp_path: Path) -> None: + decision = choose_validation_scope( + [Change("D", "tests/test_deleted.py")], repo_root=tmp_path + ) + assert decision.mode == "full" + assert "coverage was removed" in decision.reasons[0] + + +def test_governance_change_forces_full(tmp_path: Path) -> None: + decision = choose_validation_scope( + [Change("M", "governance/claims.yaml")], repo_root=tmp_path + ) + assert decision.mode == "full" + assert "cross-cutting path" in decision.reasons[0] + + +def test_validation_driver_change_selects_its_regressions(tmp_path: Path) -> None: + _touch( + tmp_path, + "tests/test_validate_changed.py", + "tests/test_validate_script.py", + "tests/test_public_contribution_surfaces.py", + ) + decision = choose_validation_scope( + [Change("M", "scripts/validate.sh")], repo_root=tmp_path + ) + assert decision.mode == "scoped" + assert decision.selectors == ( + "tests/test_public_contribution_surfaces.py", + "tests/test_validate_changed.py", + "tests/test_validate_script.py", + ) diff --git a/tests/test_validate_script.py b/tests/test_validate_script.py index 962117e3..a85d114a 100644 --- a/tests/test_validate_script.py +++ b/tests/test_validate_script.py @@ -117,6 +117,23 @@ def test_scoped_validation_requires_a_selector( assert not command_log.exists() +def test_fixed_only_validation_skips_pytest( + validation_environment: tuple[dict[str, str], Path], +) -> None: + environment, command_log = validation_environment + + result = run_validation(environment, "--fixed-only") + + assert result.returncode == 0, result.stderr + assert "ALL FIXED REPOSITORY CHECKS PASS (no affected pytest scope)" in result.stdout + pytest_calls = [ + line + for line in command_log.read_text(encoding="utf-8").splitlines() + if line.startswith("python|-m|pytest|") + ] + assert pytest_calls == [] + + @pytest.mark.parametrize("selector", ["--collect-only", "src/substrate_framework"]) def test_scoped_validation_rejects_non_test_selectors( validation_environment: tuple[dict[str, str], Path], selector: str