Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ before using the task picker; its coverage fields identify prior checks and
remaining obligations. Explicit user tasks take precedence over that default
assignment; do not reroll an already assigned task.

For a fresh assignment, run `python3 -B scripts/choose_current_research_task.py`.
This entry point resolves generic baseline commands and retains each frozen
card beside the current command. The historical picker and root guide stay
unchanged because research packages pin their bytes. The current command
examples are in [the standing procedure](docs/research/agent-procedure.md).

For repeated/overnight loops, follow the
[upstream refresh checkpoint](autoresearch.md#looping-agents-refresh-upstream-at-each-research-checkpoint)
at every new round, before a major pivot and before submission. Fetch canonical
Expand Down
10 changes: 5 additions & 5 deletions docs/research/agent-procedure.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,15 @@ Run this standard-library helper:

```sh
# Python 3.10 or later; no third-party dependencies.
python3 scripts/choose_research_task.py
python3 scripts/choose_current_research_task.py
# To reproduce a selection, pass the emitted 32-hex-digit nonce:
python3 scripts/choose_research_task.py --seed YOUR_NONCE
python3 scripts/choose_current_research_task.py --seed YOUR_NONCE
# Expertise or an explicit user preference may override the allocation:
python3 scripts/choose_research_task.py --track complex-mechanisms
python3 scripts/choose_current_research_task.py --track complex-mechanisms
# Retrieve a specific card after assignment:
python3 scripts/choose_research_task.py --task connector-alphabet-change
python3 scripts/choose_current_research_task.py --task connector-alphabet-change
# Optional: surface structured handoffs/claims from an existing overlap cache.
python3 scripts/choose_research_task.py --coordination-cache /tmp/overlap.json
python3 scripts/choose_current_research_task.py --coordination-cache /tmp/overlap.json
```

It reads [the task catalogue](agent-task-catalog.json), draws a
Expand Down
46 changes: 37 additions & 9 deletions docs/research/selected-baseline.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Productive-helper baseline orientation

For the selected #4359 productive-helper certificate, run from the repository root:
For the current selected productive-helper certificate, run from the repository root:

```sh
python3 -B scripts/research_selected_baseline.py
Expand Down Expand Up @@ -56,8 +56,8 @@ python3 -B scripts/research_baseline_sparse.py
```

Inspect its suggested sparse-checkout additions and materialize the inputs.
Then use `research_selected_baseline.py` for the productive-helper summary,
in place of the historical baseline command in its `next` field. Diagnosis
Then run the current command in its `next` field. It uses
`research_selected_baseline.py` for the productive-helper summary. Diagnosis
does not verify the inputs; the successor's source check remains mandatory.

## Compatibility and validation
Expand Down Expand Up @@ -85,13 +85,41 @@ The test filename is included by the existing research onboarding discovery
command. The full selected cold replay is a separate obligation and was not
run for this reporting adapter.

## Open next questions
## Current task-picker commands

The current task-picker CLI, `scripts/choose_current_research_task.py`, resolves the exact generic command
`python3 -B scripts/research_baseline.py` to the current selected baseline.
Each affected suggestion retains its complete original entry in
`catalog_first_step` and explains the change in `first_step_resolution`.
Only the command changes. The inspection text, scope, task, method, scope key
and acceptance gate stay the same. Other scientific first-step commands are
not changed. The CLI checks that the current helper exists, or is an exact
HEAD file omitted by a sparse checkout, before it emits the resolved command.
A sparse result names the missing helper for materialization.

The historical `scripts/choose_research_task.py`, catalogue bytes, `select()`
result and package `TASK.json` original-card receipts remain exact. The current
entry point runs the historical CLI with unchanged arguments and resolves its
JSON output. Help and error output keep the child status and text. This separate
entry point is necessary because a frozen package also pins the picker script
as a runtime input. It must not be changed or silently repinned. This distinction is deliberate: the CLI gives current
orientation, while the stored card records the historical assignment. Do not
copy the resolved command into an old package or repin its frozen inputs.
[PR4906](https://github.com/CrocSwap/integer-mult-bounds/pull/4906) found 57
runtime consumers of the catalogue and requested this receipt-preserving
migration. [PR5359](https://github.com/CrocSwap/integer-mult-bounds/pull/5359)
provides the separate authenticated historical complex-ceiling replay.
That scientific command is outside this generic-baseline resolution rule.

The displayed scientific starting points still need a current overlap and
scope check. Command resolution is not a strategic reassessment or scientific
acceptance. Tests cover all 16 generic cards, unchanged other cards, exact
command matching, missing/deleted/sparse helpers and actual CLI output.
Run them with `python3 -B -m unittest discover -s tests -p test_research_current_assignment.py`.
Current CLI resolution and these controls: Ali Romman with substantial OpenAI
Codex assistance; Apache-2.0.

The active catalogue and pinned brief still name the historical entry point.
A maintainer can migrate those references after choosing how to preserve the
dependent frozen packages' source receipts. The first step is to inventory
their `VERIFY.json` and `SOURCES.json` dependencies; changing the helper bytes
or silently repinning old inputs is not part of this contribution.
## Open next questions

Future selected certificate schemas need an explicit budget-stage adapter.
Start from the new selected record and reviewed source schema, then add a
Expand Down
67 changes: 67 additions & 0 deletions scripts/choose_current_research_task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env python3
"""Show current first steps and preserve the frozen task-picker output.

Ali Romman with substantial OpenAI Codex assistance. Apache-2.0.
Arguments and selection are delegated to the historical picker CLI.
"""
import json
from pathlib import Path
import runpy
import subprocess
import sys

ROOT = Path(__file__).resolve().parents[1]
LEGACY = ROOT / "scripts/choose_research_task.py"
check_catalog_files = runpy.run_path(str(LEGACY))["check_catalog_files"]


def resolve_current_first_steps(result, root):
"""Resolve generic orientation at the CLI boundary; keep the source card."""
old = ["python3", "-B", "scripts/research_baseline.py"]
current = "scripts/research_selected_baseline.py"
cards = [card for card in result["suggestions"]
if card["first_step"]["command"] == old]
if not cards:
return []
omitted = check_catalog_files(root, [
(current, "missing current first-step script: " + current),
])
for card in cards:
original = card["first_step"]
card["catalog_first_step"] = original
card["first_step"] = {**original, "command": ["python3", "-B", current]}
card["first_step_resolution"] = (
"The current CLI uses the selected-certificate baseline for generic "
"orientation. catalog_first_step is the exact frozen card entry. "
"The task, scientific checks and acceptance gate are unchanged."
)
return omitted


def main():
child = subprocess.run([sys.executable, "-B", str(LEGACY), *sys.argv[1:]],
capture_output=True, text=True, encoding="utf-8")
if child.returncode or any(arg in ("-h", "--help") for arg in sys.argv[1:]):
sys.stdout.write(child.stdout)
sys.stderr.write(child.stderr)
return child.returncode
try:
result = json.loads(child.stdout)
omitted = resolve_current_first_steps(result, ROOT)
if omitted:
sparse = result.setdefault("sparse_checkout", {"omitted_catalogue_paths": []})
sparse["omitted_current_first_step_paths"] = omitted
sparse["scope"] = (
"These absent regular files match HEAD and skip-worktree index entries. "
"Materialize the chosen task's inputs before running it; this validates "
"first-step references, not replay dependencies."
)
except (OSError, ValueError, KeyError, TypeError) as exc:
sys.stderr.write("current first-step resolution failed: " + str(exc) + "\n")
return 2
print(json.dumps(result, indent=2))
return 0


if __name__ == "__main__":
raise SystemExit(main())
2 changes: 1 addition & 1 deletion tests/test_research_coordination.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def test_root_entry_and_priorities_links(self):
self.assertIn('docs/research/current-priorities.md',root)
self.assertIn('docs/research/agent-procedure.md',root)
procedure=(ROOT/'docs/research/agent-procedure.md').read_text()
self.assertLess(procedure.index('**Before drawing a card**'),procedure.index('python3 scripts/choose_research_task.py'))
self.assertLess(procedure.index('**Before drawing a card**'),procedure.index('python3 scripts/choose_current_research_task.py'))


if __name__=='__main__':unittest.main()
99 changes: 99 additions & 0 deletions tests/test_research_current_assignment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""Current orientation must preserve frozen task-selection evidence."""
import copy
import importlib.util
import json
from pathlib import Path
import subprocess
import sys
import tempfile
import unittest

ROOT = Path(__file__).resolve().parents[1]
SPEC = importlib.util.spec_from_file_location("current_picker", ROOT / "scripts/choose_current_research_task.py")
current = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(current)
LEGACY = current.LEGACY
legacy = current.runpy.run_path(str(LEGACY))
CATALOG = json.loads((ROOT / legacy["CATALOG"]).read_text(encoding="utf-8"))


class CurrentResearchAssignmentTests(unittest.TestCase):
def test_every_frozen_card_and_other_fields_stay_exact(self):
before = copy.deepcopy(CATALOG)
resolved = 0
for track in CATALOG["tracks"]:
for task in track["tasks"]:
result = legacy["select"](CATALOG, "a1" * 16, task_id=task["id"])
original = copy.deepcopy(result)
current.resolve_current_first_steps(result, ROOT)
card = result["suggestions"][0]
if task["first_step"]["command"] == ["python3", "-B", "scripts/research_baseline.py"]:
resolved += 1
self.assertEqual(card["catalog_first_step"], task["first_step"])
self.assertEqual(card["first_step"]["command"],
["python3", "-B", "scripts/research_selected_baseline.py"])
self.assertTrue(card.pop("first_step_resolution"))
card["first_step"] = card.pop("catalog_first_step")
self.assertEqual(result, original)
self.assertEqual(resolved, 16)
self.assertEqual(CATALOG, before)

def test_only_exact_generic_command_is_resolved(self):
result = copy.deepcopy(legacy["select"](CATALOG, "0" * 32, task_id="portable-replay"))
result["suggestions"][0]["first_step"]["command"].append("--historical-fixture")
original = copy.deepcopy(result)
self.assertEqual(current.resolve_current_first_steps(result, ROOT), [])
self.assertEqual(result, original)

def test_missing_current_helper_does_not_change_receipt(self):
result = legacy["select"](CATALOG, "0" * 32, task_id="portable-replay")
original = copy.deepcopy(result)
with tempfile.TemporaryDirectory() as directory:
with self.assertRaisesRegex(ValueError, "missing current first-step script"):
current.resolve_current_first_steps(result, Path(directory))
self.assertEqual(result, original)

def cli(self, script, args):
return subprocess.run([sys.executable, "-B", str(script), *args], cwd=ROOT,
capture_output=True, text=True, encoding="utf-8")

def test_cli_preserves_legacy_output_and_emits_both_commands(self):
args = ["--seed", "0" * 32, "--task", "portable-replay"]
original = self.cli(LEGACY, args)
output = self.cli(ROOT / "scripts/choose_current_research_task.py", args)
self.assertEqual(original.returncode, 0)
self.assertEqual(output.returncode, 0)
old, new = json.loads(original.stdout), json.loads(output.stdout)
card = new["suggestions"][0]
self.assertEqual(card["first_step"]["command"],
["python3", "-B", "scripts/research_selected_baseline.py"])
self.assertEqual(card.pop("catalog_first_step"), old["suggestions"][0]["first_step"])
self.assertTrue(card.pop("first_step_resolution"))
card["first_step"] = old["suggestions"][0]["first_step"]
self.assertEqual(new, old)
self.assertEqual(self.cli(LEGACY, args).stdout, original.stdout)

def test_help_and_error_preserve_child_status_and_output(self):
for args, code in ((["--help"], 0), (["--unknown-option"], 2), (["--seed", "bad"], 2)):
original = self.cli(LEGACY, args)
output = self.cli(ROOT / "scripts/choose_current_research_task.py", args)
self.assertEqual(output.returncode, code)
self.assertEqual((output.stdout, output.stderr), (original.stdout, original.stderr))

def test_sparse_helper_is_allowed_but_deletion_is_rejected(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
name = "scripts/research_selected_baseline.py"
path = root / name; path.parent.mkdir(); path.write_text("print('fixture')\n")
def git(*args):
subprocess.run(["git", *args], cwd=root, check=True, capture_output=True)
git("init", "-q"); git("add", name)
git("-c", "user.name=Fixture", "-c", "user.email=fixture@example.invalid",
"-c", "core.hooksPath=/dev/null", "commit", "-qm", "helper")
git("update-index", "--skip-worktree", name); path.unlink()
result = legacy["select"](CATALOG, "0" * 32, task_id="portable-replay")
self.assertEqual(current.resolve_current_first_steps(result, root), [name])
git("update-index", "--no-skip-worktree", name)
result = legacy["select"](CATALOG, "0" * 32, task_id="portable-replay")
with self.assertRaisesRegex(ValueError, "missing current first-step script"):
current.resolve_current_first_steps(result, root)