Compare commits

...
Author SHA1 Message Date
conductor 78584cd6ce chore(release): auto-bump version to 0.54.5 — earned by #247 (fix(research): issue context from API — dispatch events carry no issue payload)
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / lint (push) Successful in 31s
CI Pipeline / unit-tests (push) Successful in 43s
CI Pipeline / canary (push) Successful in 26s
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / required-checks (push) Successful in 1s
Promotion record: #247
2026-09-08 07:35:05 +01:00
conductor 6ea9a5d4e4 chore(release): auto-bump version to 0.54.5 — earned by #247 (fix(research): issue context from API — dispatch events carry no issue payload)
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / lint (push) Successful in 30s
CI Pipeline / canary (push) Successful in 26s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / unit-tests (push) Successful in 46s
CI Pipeline / required-checks (push) Successful in 1s
Promotion record: #247
2026-09-08 07:35:03 +01:00
conductor 1a522c6602 chore(release): auto-bump version to 0.54.5 — earned by #247 (fix(research): issue context from API — dispatch events carry no issue payload)
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / lint (push) Successful in 31s
CI Pipeline / canary (push) Successful in 26s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / unit-tests (push) Successful in 45s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / required-checks (push) Successful in 2s
Promotion record: #247
2026-09-08 07:35:00 +01:00
conductor a0a1d4f3d3 chore(release): auto-bump version to 0.54.5 — earned by #247 (fix(research): issue context from API — dispatch events carry no issue payload)
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / lint (push) Successful in 30s
CI Pipeline / canary (push) Successful in 32s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / unit-tests (push) Successful in 45s
CI Pipeline / required-checks (push) Successful in 2s
Promotion record: #247
2026-09-08 07:34:57 +01:00
conductor c85bfa9a85 chore(release): auto-bump version to 0.54.5 — earned by #247 (fix(research): issue context from API — dispatch events carry no issue payload)
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / lint (push) Successful in 30s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / canary (push) Failing after 26s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / unit-tests (push) Successful in 45s
CI Pipeline / required-checks (push) Successful in 1s
Promotion record: #247
2026-09-08 07:34:55 +01:00
conductor c76f5d9cc8 Merge pull request 'fix(research): issue context from API — dispatch events carry no issue payload' (#247) from research-structuralization into master
CI Pipeline / best-practices (push) Successful in 4s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / canary (push) Successful in 27s
CI Pipeline / lint (push) Successful in 31s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / gitea-offline (push) Successful in 15s
CI Pipeline / unit-tests (push) Successful in 49s
CI Pipeline / required-checks (push) Successful in 2s
2026-09-08 07:34:41 +01:00
Nick 275715fbc0 merge master
Auto-label / priority-gate (pull_request) Skipped
Auto-label / auto-label (pull_request) Successful in 2s
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / canary (push) Successful in 26s
CI Pipeline / lint (push) Successful in 33s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / unit-tests (push) Successful in 46s
CI Pipeline / best-practices (pull_request) Successful in 2s
CI Pipeline / documentation (pull_request) Successful in 2s
CI Pipeline / lint (pull_request) Successful in 31s
CI Pipeline / canary (pull_request) Successful in 28s
CI Pipeline / gitea-offline (pull_request) Successful in 13s
Spec gate (headless) / spec-gate (pull_request) Successful in 5s
CI Pipeline / type-check (pull_request) Successful in 24s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / unit-tests (pull_request) Successful in 44s
CI Pipeline / required-checks (pull_request) Successful in 1s
Auto-version and Release / release (pull_request) Successful in 28s
2026-09-08 07:32:36 +01:00
Nick 1ea3462edb fix(research): issue title/body fetched from API — dispatch events carry no issue payload
CI Pipeline / best-practices (push) Successful in 4s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / canary (push) Successful in 27s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / lint (push) Successful in 33s
CI Pipeline / unit-tests (push) Successful in 49s
CI Pipeline / gitea-offline (push) Successful in 16s
CI Pipeline / required-checks (push) Successful in 1s
2026-09-08 07:32:11 +01:00
conductor 4573bb6a2b chore(release): auto-bump version to 0.54.4 — earned by #246 (fix(research): dispatch trigger for the research stage — label-event quirk)
CI Pipeline / required-checks (push) Successful in 0s
CI Pipeline / lint (push) Successful in 29s
CI Pipeline / unit-tests (push) Successful in 47s
CI Pipeline / canary (push) Successful in 27s
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / gitea-offline (push) Successful in 14s
Promotion record: #246
2026-09-08 07:22:31 +01:00
conductor 4b67831aa8 chore(release): auto-bump version to 0.54.4 — earned by #246 (fix(research): dispatch trigger for the research stage — label-event quirk)
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / lint (push) Successful in 30s
CI Pipeline / canary (push) Successful in 27s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / unit-tests (push) Successful in 43s
CI Pipeline / required-checks (push) Successful in 1s
Promotion record: #246
2026-09-08 07:22:29 +01:00
conductor 1f1bd9e620 chore(release): auto-bump version to 0.54.4 — earned by #246 (fix(research): dispatch trigger for the research stage — label-event quirk)
CI Pipeline / best-practices (push) Successful in 4s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / canary (push) Successful in 29s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / lint (push) Successful in 33s
CI Pipeline / unit-tests (push) Successful in 50s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / required-checks (push) Successful in 1s
Promotion record: #246
2026-09-08 07:22:25 +01:00
conductor e1f8ff0406 chore(release): auto-bump version to 0.54.4 — earned by #246 (fix(research): dispatch trigger for the research stage — label-event quirk)
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / lint (push) Successful in 31s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / canary (push) Successful in 26s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / unit-tests (push) Successful in 50s
CI Pipeline / required-checks (push) Successful in 2s
Promotion record: #246
2026-09-08 07:22:23 +01:00
conductor cb0ed1bf60 chore(release): auto-bump version to 0.54.4 — earned by #246 (fix(research): dispatch trigger for the research stage — label-event quirk)
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / lint (push) Successful in 31s
CI Pipeline / documentation (push) Successful in 11s
CI Pipeline / unit-tests (push) Successful in 43s
CI Pipeline / canary (push) Failing after 27s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / required-checks (push) Successful in 1s
Promotion record: #246
2026-09-08 07:22:21 +01:00
conductor 301a751761 Merge pull request 'fix(research): dispatch trigger for the research stage — label-event quirk' (#246) from research-structuralization into master
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / canary (push) Successful in 28s
CI Pipeline / lint (push) Successful in 32s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / unit-tests (push) Successful in 51s
CI Pipeline / required-checks (push) Successful in 2s
2026-09-08 07:22:08 +01:00
Nick 6c824c1e62 fix(research): workflow_dispatch trigger for the research stage — label-event payload quirk (label.name match skipped on API-applied labels); dispatch route is deterministic
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 2s
Auto-label / priority-gate (pull_request) Skipped
Auto-label / auto-label (pull_request) Successful in 2s
CI Pipeline / canary (push) Successful in 28s
CI Pipeline / lint (push) Successful in 33s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / unit-tests (push) Successful in 49s
CI Pipeline / best-practices (pull_request) Successful in 2s
CI Pipeline / documentation (pull_request) Successful in 3s
CI Pipeline / lint (pull_request) Successful in 31s
CI Pipeline / canary (pull_request) Successful in 27s
Spec gate (headless) / spec-gate (pull_request) Successful in 4s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / gitea-offline (pull_request) Successful in 13s
CI Pipeline / unit-tests (pull_request) Successful in 44s
CI Pipeline / type-check (pull_request) Successful in 24s
CI Pipeline / required-checks (pull_request) Successful in 1s
Auto-version and Release / release (pull_request) Successful in 31s
2026-09-08 07:20:30 +01:00
Nick 6140f8e087 feat(research): PR template gains Research provenance section (P0.7 planning callback — synthesis digest flows to implementation)
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / lint (push) Successful in 32s
CI Pipeline / canary (push) Successful in 32s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / unit-tests (push) Successful in 52s
CI Pipeline / required-checks (push) Successful in 0s
2026-09-08 07:16:57 +01:00
Nick a8cebea264 feat(research): MORE_RESEARCH re-dispatch semantics (P0.6) — sweep keeps MORE_RESEARCH rounds eligible (re-dispatch when label lost); PROCEED rounds are done. Disposition routing documented in stage workflow
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / canary (push) Successful in 30s
CI Pipeline / lint (push) Successful in 31s
CI Pipeline / type-check (push) Successful in 27s
CI Pipeline / gitea-offline (push) Successful in 26s
CI Pipeline / unit-tests (push) Successful in 1m39s
CI Pipeline / required-checks (push) Successful in 0s
2026-09-08 07:12:34 +01:00
Nick 7f050a13df feat(research): structured research artifact schema + causal validation (P0.4, red-first)
CI Pipeline / best-practices (push) Successful in 4s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / canary (push) Successful in 28s
CI Pipeline / lint (push) Successful in 32s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / unit-tests (push) Successful in 50s
CI Pipeline / required-checks (push) Successful in 1s
Per the Forge Cognitive Research Loop handoff §8/§9/§11/§12:
- OUTWARD valid only with real discovery (≥2 sources, url+tool+learned each, ≥1 query recorded)
- INNOVATION candidates must derive_from prior claims; ≥1 from OUTWARD unless exempt
- SYNTHESIS must cite evidence_used; MORE_RESEARCH requires named blocking_unknowns
- dispositions: PROCEED | MORE_RESEARCH (nothing else)

10 red-first tests frozen green.
2026-09-08 07:08:06 +01:00
Nick 78e506b99d fix(canary): release-token binding asserts ROBOT_TOKEN — 242's merge dropped the change (canary froze the broken GITEA_TOKEN contract while the workflow correctly uses ROBOT_TOKEN)
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / lint (push) Successful in 33s
CI Pipeline / canary (push) Successful in 29s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / unit-tests (push) Successful in 49s
CI Pipeline / gitea-offline (push) Successful in 18s
CI Pipeline / required-checks (push) Successful in 1s
2026-09-08 06:56:10 +01:00
conductor 6bfe993801 chore(release): auto-bump version to 0.54.3 — earned by #244 (fix(pipeline): re-wire umbrella impact gate — lost in #212 merge resolution)
CI Pipeline / lint (push) Successful in 31s
CI Pipeline / unit-tests (push) Successful in 44s
CI Pipeline / canary (push) Failing after 27s
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / required-checks (push) Successful in 2s
Promotion record: #244
2026-09-08 06:55:42 +01:00
conductor 092393a620 chore(release): auto-bump version to 0.54.3 — earned by #244 (fix(pipeline): re-wire umbrella impact gate — lost in #212 merge resolution)
CI Pipeline / best-practices (push) Successful in 4s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / lint (push) Successful in 31s
CI Pipeline / canary (push) Failing after 29s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / unit-tests (push) Successful in 51s
CI Pipeline / required-checks (push) Successful in 1s
Promotion record: #244
2026-09-08 06:55:39 +01:00
conductor 3162c99e4f chore(release): auto-bump version to 0.54.3 — earned by #244 (fix(pipeline): re-wire umbrella impact gate — lost in #212 merge resolution)
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / lint (push) Successful in 30s
CI Pipeline / canary (push) Failing after 25s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / unit-tests (push) Successful in 44s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / required-checks (push) Successful in 0s
Promotion record: #244
2026-09-08 06:55:37 +01:00
conductor 1e0c1a0618 chore(release): auto-bump version to 0.54.3 — earned by #244 (fix(pipeline): re-wire umbrella impact gate — lost in #212 merge resolution)
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / lint (push) Successful in 30s
CI Pipeline / canary (push) Failing after 25s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / unit-tests (push) Successful in 48s
CI Pipeline / required-checks (push) Successful in 1s
Promotion record: #244
2026-09-08 06:55:34 +01:00
conductor 2974b173d3 chore(release): auto-bump version to 0.54.3 — earned by #244 (fix(pipeline): re-wire umbrella impact gate — lost in #212 merge resolution)
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / canary (push) Failing after 27s
CI Pipeline / lint (push) Successful in 33s
CI Pipeline / type-check (push) Successful in 26s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / unit-tests (push) Successful in 48s
CI Pipeline / required-checks (push) Successful in 1s
Promotion record: #244
2026-09-08 06:55:32 +01:00
conductor 90fc6061bc Merge pull request 'fix(pipeline): re-wire umbrella impact gate — lost in #212 merge resolution' (#244) from umbrella-rewire into master
CI Pipeline / best-practices (push) Successful in 4s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / canary (push) Failing after 28s
CI Pipeline / lint (push) Successful in 33s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / gitea-offline (push) Successful in 15s
CI Pipeline / unit-tests (push) Successful in 49s
CI Pipeline / required-checks (push) Successful in 1s
2026-09-08 06:55:19 +01:00
Nick 3c688bd126 fix(pipeline): re-wire umbrella impact gate — lost in #212 merge resolution
Auto-label / priority-gate (pull_request) Skipped
Auto-label / auto-label (pull_request) Successful in 3s
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / canary (push) Failing after 30s
CI Pipeline / lint (push) Successful in 32s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / unit-tests (push) Successful in 50s
CI Pipeline / best-practices (pull_request) Successful in 2s
CI Pipeline / documentation (pull_request) Successful in 2s
CI Pipeline / lint (pull_request) Successful in 32s
CI Pipeline / canary (pull_request) Failing after 25s
Spec gate (headless) / spec-gate (pull_request) Successful in 5s
CI Pipeline / gitea-offline (pull_request) Successful in 13s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / unit-tests (pull_request) Successful in 45s
CI Pipeline / type-check (pull_request) Successful in 24s
CI Pipeline / required-checks (pull_request) Successful in 1s
Auto-version and Release / release (pull_request) Successful in 31s
2026-09-08 06:50:29 +01:00
conductor 5ba6301047 chore(release): auto-bump version to 0.54.2 — earned by #243 (chore: trigger release (conductor push-whitelisted))
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / canary (push) Failing after 28s
CI Pipeline / lint (push) Successful in 31s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / unit-tests (push) Successful in 49s
CI Pipeline / gitea-offline (push) Successful in 18s
CI Pipeline / required-checks (push) Successful in 0s
Promotion record: #243
2026-09-08 06:40:44 +01:00
conductor bfb0a28626 chore(release): auto-bump version to 0.54.2 — earned by #243 (chore: trigger release (conductor push-whitelisted))
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / lint (push) Successful in 30s
CI Pipeline / canary (push) Failing after 26s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / unit-tests (push) Successful in 44s
CI Pipeline / required-checks (push) Successful in 2s
Promotion record: #243
2026-09-08 06:40:43 +01:00
conductor f15e0f1b0f chore(release): auto-bump version to 0.54.2 — earned by #243 (chore: trigger release (conductor push-whitelisted))
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / lint (push) Successful in 29s
CI Pipeline / canary (push) Failing after 26s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / unit-tests (push) Successful in 44s
CI Pipeline / required-checks (push) Successful in 2s
Promotion record: #243
2026-09-08 06:40:39 +01:00
conductor 661f1f9869 chore(release): auto-bump version to 0.54.2 — earned by #243 (chore: trigger release (conductor push-whitelisted))
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / lint (push) Successful in 30s
CI Pipeline / canary (push) Failing after 27s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / unit-tests (push) Successful in 49s
CI Pipeline / required-checks (push) Successful in 1s
Promotion record: #243
2026-09-08 06:40:36 +01:00
conductor 892ae96de8 chore(release): auto-bump version to 0.54.2 — earned by #243 (chore: trigger release (conductor push-whitelisted))
CI Pipeline / best-practices (push) Successful in 1s
CI Pipeline / lint (push) Successful in 29s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / canary (push) Failing after 26s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / unit-tests (push) Failing after 40s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / required-checks (push) Successful in 1s
Promotion record: #243
2026-09-08 06:40:34 +01:00
conductor f5d8ab15bc Merge pull request 'chore: trigger release (conductor push-whitelisted)' (#243) from trigger-bump-2 into master
CI Pipeline / best-practices (push) Successful in 4s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / canary (push) Failing after 26s
CI Pipeline / lint (push) Successful in 31s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / unit-tests (push) Successful in 48s
CI Pipeline / required-checks (push) Successful in 1s
2026-09-08 06:40:22 +01:00
Nick 656afa07c6 chore: trigger release — conductor push-whitelisted, direct-commit bump unblocked
Auto-label / priority-gate (pull_request) Skipped
CI Pipeline / best-practices (push) Successful in 4s
Auto-label / auto-label (pull_request) Successful in 2s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / canary (push) Failing after 28s
CI Pipeline / lint (push) Successful in 31s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / unit-tests (push) Successful in 48s
CI Pipeline / best-practices (pull_request) Successful in 2s
CI Pipeline / documentation (pull_request) Successful in 3s
CI Pipeline / lint (pull_request) Successful in 29s
CI Pipeline / canary (pull_request) Failing after 29s
CI Pipeline / gitea-offline (pull_request) Successful in 14s
CI Pipeline / required-checks (push) Successful in 1s
Spec gate (headless) / spec-gate (pull_request) Successful in 4s
CI Pipeline / unit-tests (pull_request) Successful in 44s
CI Pipeline / type-check (pull_request) Successful in 25s
CI Pipeline / required-checks (pull_request) Successful in 1s
Auto-version and Release / release (pull_request) Failing after 24s
2026-09-08 06:38:22 +01:00
conductor 280c40394f Merge pull request 'fix(release): ROBOT_TOKEN binding + canary contract update (supersedes #241)' (#242) from release-token-fix into master
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / canary (push) Failing after 26s
CI Pipeline / lint (push) Successful in 33s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / unit-tests (push) Successful in 47s
CI Pipeline / required-checks (push) Successful in 1s
2026-09-08 06:32:40 +01:00
Nick 810af91368 merge research-structuralization (ROBOT_TOKEN in auto-version + canary contract update)
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 9s
CI Pipeline / canary (push) Failing after 26s
CI Pipeline / lint (push) Successful in 30s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / unit-tests (push) Successful in 48s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / best-practices (pull_request) Successful in 3s
CI Pipeline / documentation (pull_request) Successful in 2s
Auto-label / priority-gate (pull_request) Skipped
Auto-label / auto-label (pull_request) Successful in 2s
CI Pipeline / canary (pull_request) Failing after 26s
CI Pipeline / lint (pull_request) Successful in 32s
CI Pipeline / type-check (pull_request) Successful in 25s
Spec gate (headless) / spec-gate (pull_request) Successful in 6s
CI Pipeline / gitea-offline (pull_request) Successful in 13s
CI Pipeline / unit-tests (pull_request) Successful in 47s
CI Pipeline / required-checks (pull_request) Successful in 1s
Auto-version and Release / release (pull_request) Failing after 9s
2026-09-08 06:29:30 +01:00
Nick 7089285a3e Merge remote-tracking branch 'gitea/research-structuralization' into sweep-live
Auto-label / priority-gate (pull_request) Skipped
CI Pipeline / best-practices (push) Successful in 4s
Auto-label / auto-label (pull_request) Successful in 4s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / canary (push) Failing after 27s
CI Pipeline / lint (push) Successful in 32s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / unit-tests (push) Successful in 48s
CI Pipeline / required-checks (push) Successful in 2s
Spec gate (headless) / spec-gate (pull_request) Successful in 6s
CI Pipeline / lint (pull_request) Successful in 30s
CI Pipeline / unit-tests (pull_request) Successful in 43s
CI Pipeline / canary (pull_request) Failing after 27s
CI Pipeline / best-practices (pull_request) Successful in 3s
CI Pipeline / documentation (pull_request) Successful in 2s
CI Pipeline / type-check (pull_request) Successful in 24s
CI Pipeline / gitea-offline (pull_request) Successful in 14s
CI Pipeline / required-checks (pull_request) Successful in 2s
Auto-version and Release / release (pull_request) Failing after 10s
2026-09-08 06:23:10 +01:00
Nick 4a33074b6a fix(release): auto-version authenticates via ROBOT_TOKEN — the reserved GITEA_TOKEN is the read-only auto token (gitea-actions bot cannot commit to protected master; 403 'user cannot commit to repo') 2026-09-08 06:22:55 +01:00
conductor 99de8ce1be Merge pull request 'fix(release): surface gate self-heals base drift + drift detail to stdout' (#240) from research-structuralization into master
CI Pipeline / best-practices (push) Successful in 5s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / canary (push) Successful in 29s
CI Pipeline / lint (push) Successful in 34s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / unit-tests (push) Successful in 49s
CI Pipeline / required-checks (push) Successful in 1s
2026-09-08 06:18:44 +01:00
conductor a1dcdadde2 Merge branch 'master' into research-structuralization
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / lint (push) Successful in 31s
CI Pipeline / canary (push) Successful in 32s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / unit-tests (push) Successful in 53s
CI Pipeline / best-practices (pull_request) Successful in 2s
CI Pipeline / documentation (pull_request) Successful in 2s
CI Pipeline / lint (pull_request) Successful in 30s
CI Pipeline / canary (pull_request) Successful in 28s
CI Pipeline / unit-tests (pull_request) Successful in 44s
CI Pipeline / gitea-offline (pull_request) Successful in 14s
CI Pipeline / required-checks (push) Successful in 2s
CI Pipeline / type-check (pull_request) Successful in 24s
Spec gate (headless) / spec-gate (pull_request) Successful in 7s
CI Pipeline / required-checks (pull_request) Successful in 1s
Auto-version and Release / release (pull_request) Failing after 7s
2026-09-08 06:17:03 +01:00
Nick 56bc364832 fix(release): surface gate recovers from base drift — unchanged surfaces carrying a stale version are force-edited to NEW (with evidence line); drift detail printed to stdout (stderr was swallowed by runner logs)
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / lint (push) Successful in 33s
CI Pipeline / canary (push) Successful in 27s
Auto-label / auto-label (pull_request) Successful in 5s
CI Pipeline / unit-tests (push) Successful in 48s
Auto-label / priority-gate (pull_request) Skipped
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / best-practices (pull_request) Successful in 2s
CI Pipeline / documentation (pull_request) Successful in 2s
CI Pipeline / lint (pull_request) Successful in 31s
CI Pipeline / canary (pull_request) Successful in 26s
Spec gate (headless) / spec-gate (pull_request) Successful in 4s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / type-check (pull_request) Successful in 24s
CI Pipeline / gitea-offline (pull_request) Successful in 13s
CI Pipeline / unit-tests (pull_request) Successful in 45s
CI Pipeline / required-checks (pull_request) Successful in 1s
2026-09-08 06:15:19 +01:00
conductor 8bcf6a81b0 Merge pull request 'chore: trigger release pipeline (empty commit — proves direct-commit bump)' (#239) from trigger-bump into master
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / canary (push) Successful in 27s
CI Pipeline / lint (push) Successful in 30s
CI Pipeline / type-check (push) Successful in 25s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / unit-tests (push) Successful in 48s
CI Pipeline / required-checks (push) Successful in 1s
2026-09-08 06:09:39 +01:00
Nick d1121f6f52 chore: trigger release pipeline — proves direct-commit bump end-to-end (surfaces aligned by #238)
Auto-label / priority-gate (pull_request) Skipped
Auto-label / auto-label (pull_request) Successful in 4s
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / canary (push) Successful in 27s
CI Pipeline / lint (push) Successful in 30s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / unit-tests (push) Successful in 48s
CI Pipeline / best-practices (pull_request) Successful in 2s
CI Pipeline / documentation (pull_request) Successful in 2s
CI Pipeline / lint (pull_request) Successful in 29s
CI Pipeline / canary (pull_request) Successful in 25s
CI Pipeline / gitea-offline (pull_request) Successful in 13s
Spec gate (headless) / spec-gate (pull_request) Successful in 5s
CI Pipeline / required-checks (push) Successful in 1s
CI Pipeline / type-check (pull_request) Successful in 24s
CI Pipeline / unit-tests (pull_request) Successful in 43s
CI Pipeline / required-checks (pull_request) Successful in 1s
Auto-version and Release / release (pull_request) Failing after 9s
2026-09-08 06:08:06 +01:00
conductor 01b0129c2e Merge pull request 'chore(release): reconcile version surfaces — pyproject 0.49.0→0.54.1, README badge' (#238) from version-reconcile into master
CI Pipeline / best-practices (push) Successful in 4s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / canary (push) Successful in 27s
CI Pipeline / lint (push) Successful in 32s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / type-check (push) Successful in 24s
CI Pipeline / unit-tests (push) Successful in 49s
CI Pipeline / required-checks (push) Successful in 1s
2026-09-08 06:04:23 +01:00
13 changed files with 420 additions and 30 deletions

No files matched your search

+6
View File
@@ -83,3 +83,9 @@ DEPENDENCIES (epic + issues). -->
## Plain-language review notes
<!-- Explain what Nick should inspect, approve, reject, or question. -->
## Research provenance
<!-- If this PR's issue went through a research round: link the research file
(research/issue-<N>-research.md) and state which synthesis decisions were
adopted. If no research round: write "none" — the spec-gate will flag it. -->
-->
+18 -7
View File
@@ -31,7 +31,7 @@ jobs:
- name: Auto-bump if the PR did not carry a version bump
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GITEA_TOKEN: ${{ secrets.ROBOT_TOKEN }}
RELEASE_SERVER_URL: ${{ gitea.server_url }}
RELEASE_REPO: ${{ gitea.repository }}
PR_TITLE: ${{ gitea.event.pull_request.title }}
@@ -126,20 +126,31 @@ jobs:
drifts = []
for rel, pat in checks:
if rel not in edited:
# unchanged surface: must ALREADY carry NEW
# unchanged surface: must ALREADY carry NEW — if it carries a
# DIFFERENT version (base drift, e.g. pyproject at 0.49.0 while
# master is 0.54.1), RECOVER by force-editing to NEW. The gate
# stays: nothing commits unless every surface carries NEW.
status, cur = api(f"/contents/{rel}?ref=master")
text = base64.b64decode(cur.get("content", "")).decode("utf-8", "replace") if status == 200 else ""
if status != 200:
drifts.append(f"{rel}: unreadable ({status})")
continue
text = base64.b64decode(cur.get("content", "")).decode("utf-8", "replace")
hits = re.findall(pat, text)
if NEW not in hits:
drifts.append(f"{rel}: unchanged but does not carry {NEW} (found {sorted(set(hits))})")
fixed = re.sub(pat, lambda m: m.group(0).replace(m.group(1), NEW), text, count=1)
if fixed != text:
edited[rel] = (fixed, cur["sha"])
print(f" RECOVERED {rel}: base drift {sorted(set(hits))} -> {NEW}")
else:
drifts.append(f"{rel}: pattern present but unfixable (found {sorted(set(hits))})")
else:
hits = re.findall(pat, edited[rel][0])
if NEW not in hits:
drifts.append(f"{rel}: edited content does not carry {NEW}")
if drifts:
print("VERSION-SURFACE DRIFT — bump aborted, nothing committed:", file=sys.stderr)
print("VERSION-SURFACE DRIFT — bump aborted, nothing committed:")
for d in drifts:
print(f" - {d}", file=sys.stderr)
print(f" - {d}")
sys.exit(1)
# Commit each surface DIRECTLY to master (bookkeeping, not reviewable code)
@@ -162,7 +173,7 @@ jobs:
PYBUMP
- name: Create release from merged pull request
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GITEA_TOKEN: ${{ secrets.ROBOT_TOKEN }}
PR_TITLE: ${{ gitea.event.pull_request.title }}
PR_BODY: ${{ gitea.event.pull_request.body }}
RELEASE_API_URL: ${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases
+4
View File
@@ -80,6 +80,10 @@ jobs:
python3 -m venv .test-venv
.test-venv/bin/pip install --disable-pip-version-check -r requirements-ci.txt
.test-venv/bin/pip install --disable-pip-version-check -e . --no-deps
- name: Umbrella impact gate (propagation contract)
run: |
python3 scripts/umbrella_impact.py check \
--base "$(git merge-base origin/master HEAD)" --head HEAD
- name: Test with coverage (global floor + changed-lines ratchet)
run: |
# #149 coverage ratchet — both floors live in .coveragerc; raise,
+38 -14
View File
@@ -21,19 +21,28 @@ name: Research stage (headless)
on:
issues:
types: [labeled]
workflow_dispatch:
inputs:
issue_number:
description: "Issue to research"
required: true
round:
description: "Research round number"
required: false
default: "1"
permissions:
issues: write
contents: write
concurrency:
group: research-stage-${{ gitea.event.issue.number }}
group: research-stage-${{ gitea.event.inputs.issue_number || gitea.event.issue.number }}
cancel-in-progress: false
jobs:
research:
runs-on: rune-ci-desktop
if: gitea.event.label.name == 'research-pending'
if: ${{ gitea.event.label.name == 'research-pending' || gitea.event_name == 'workflow_dispatch' }}
steps:
- name: Check out master (research reads the codebase, not a PR)
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
@@ -47,21 +56,28 @@ jobs:
HOME: /var/lib/gitea-desktop-runner
XDG_CONFIG_HOME: /home/nick/.config
XDG_DATA_HOME: /var/lib/gitea-desktop-runner/.local-share-mimo
ISSUE_NUMBER: ${{ gitea.event.issue.number }}
ISSUE_TITLE: ${{ gitea.event.issue.title }}
ISSUE_BODY: ${{ gitea.event.issue.body }}
ISSUE_NUMBER: ${{ gitea.event.inputs.issue_number || gitea.event.issue.number }}
GITEA_TOKEN: ${{ secrets.ROBOT_TOKEN }}
SERVER: ${{ gitea.server_url }}
run: |
# Prompt = governed repo template (prompts/research-round.md) with
# placeholders substituted. The template is the TUI-callback seam:
# future L3 (Rune TUI) owns/overrides it via reviewed diffs.
# Fetch issue title/body from the API (dispatch events carry no issue
# payload). Deterministic, no scraping.
python3 - <<'PY'
import os
import json, os, urllib.request
token = os.environ["GITEA_TOKEN"]
base = os.environ["SERVER"].rstrip("/") + "/api/v1/repos/conductor/rune"
n = os.environ["ISSUE_NUMBER"]
req = urllib.request.Request(
f"{base}/issues/{n}",
headers={"Authorization": "token " + token})
with urllib.request.urlopen(req, timeout=30) as r:
issue = json.loads(r.read())
tpl = open("prompts/research-round.md").read()
out = (tpl.replace("{{ISSUE_NUMBER}}", os.environ["ISSUE_NUMBER"])
.replace("{{ISSUE_TITLE}}", os.environ["ISSUE_TITLE"])
.replace("{{ISSUE_BODY}}", os.environ["ISSUE_BODY"] or "(empty body)"))
out = (tpl.replace("{{ISSUE_NUMBER}}", n)
.replace("{{ISSUE_TITLE}}", issue.get("title", ""))
.replace("{{ISSUE_BODY}}", issue.get("body") or "(empty body)"))
open("/tmp/research_prompt.txt", "w").write(out)
print(f"prompt built: {len(out)} chars from prompts/research-round.md")
print(f"prompt built: {len(out)} chars (issue #{n}: {issue.get('title','')[:50]})")
PY
/var/lib/gitea-desktop-runner/bin/mimo run \
@@ -96,7 +112,7 @@ jobs:
env:
GITEA_TOKEN: ${{ secrets.ROBOT_TOKEN }}
SERVER: ${{ gitea.server_url }}
ISSUE_NUMBER: ${{ gitea.event.issue.number }}
ISSUE_NUMBER: ${{ gitea.event.inputs.issue_number || gitea.event.issue.number }}
run: |
python3 - <<'PY'
import json, os, subprocess, urllib.request
@@ -119,6 +135,14 @@ jobs:
with urllib.request.urlopen(req, timeout=30) as r:
print(f"research comment posted: {r.status}")
# Disposition routing (§14): PROCEED → research-clear fires via the
# marker comment. MORE_RESEARCH → strip the synthesis from the
# comment is NOT needed (the gate keys on all 4 phase markers, which
# are present); instead the run_agent_stage result records the
# disposition and the ROUND increments on the next dispatch. The
# scheduled sweep re-dispatches when research-pending persists and
# no stage is running (§19 reconciliation path).
# 2. Research file committed straight to master by the robot identity
subprocess.run(["git", "config", "user.name", "conductor"], check=True)
subprocess.run(["git", "config", "user.email", "nick@thingscouldgetdicey.online"], check=True)
+1 -1
View File
@@ -1,6 +1,6 @@
# Rune
**Current release: v0.54.1**
**Current release: v0.54.5**
**Rune is a persistent personal agent designed to accumulate experience, develop reusable competence, and remain recognisably the same agent even when its underlying foundation model changes.**
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "rune"
version = "0.54.1"
version = "0.54.5"
description = "Rune — a stateful coding assistant with persistent memory, orchestration, and TUI"
requires-python = ">=3.10"
+97
View File
@@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""research_artifact.py — structured research-round schema + validation (P0.4).
Per the Forge Cognitive Research Loop handoff §8: machine-readable research
round with causal evidence links. Phases are FROZEN artifacts — the later
phase's input is the persisted prior artifact (§7: "the later phase must not
be able to pretend it saw research that the pipeline never recorded").
Schema (minimal, causally sufficient):
issue, round, receipt (from run_agent_stage.build_receipt),
phases: {inward: {claims: [{id, claim, evidence[]}]},
outward: {sources: [{id, url, title, retrieved_at, tool,
content_hash, learned}],
claims: [{id, claim, supports[]}]},
innovation: {candidates: [{id, proposal, derived_from[],
expected_gain, risks[]}]},
synthesis: {decision, evidence_used[], blocking_unknowns[],
feasibility, kill_test,
disposition: PROCEED|MORE_RESEARCH}}
"""
from __future__ import annotations
import json
import re
import sys
from pathlib import Path
def validate_outward(outward: dict) -> list[str]:
"""§9: OUTWARD valid only with real discovery — ≥2 sources, each with
url + tool receipt + learned note; ≥1 search query recorded."""
errs = []
sources = outward.get("sources", [])
if len(sources) < 2:
errs.append(f"OUTWARD: {len(sources)} sources < 2 (real discovery required)")
for s_ in sources:
for field in ("id", "url", "tool", "learned"):
if not s_.get(field):
errs.append(f"OUTWARD source {s_.get('id', '?')}: missing {field}")
if not re.match(r"https?://", s_.get("url", "")):
errs.append(f"OUTWARD source {s_.get('id', '?')}: url not http(s)")
if not outward.get("queries"):
errs.append("OUTWARD: no search queries recorded (discovery, not recall)")
return errs
def validate_causal_links(artifact: dict) -> list[str]:
"""§11/§12: innovation candidates derive_from prior claims; synthesis
decisions cite evidence_used. At least one candidate depends on OUTWARD
evidence unless synthesis records 'no external mechanism relevant'."""
errs = []
phases = artifact.get("phases", {})
inward_ids = {c["id"] for c in phases.get("inward", {}).get("claims", [])}
outward_ids = {c["id"] for c in phases.get("outward", {}).get("claims", [])}
candidates = phases.get("innovation", {}).get("candidates", [])
synthesis = phases.get("synthesis", {})
known = inward_ids | outward_ids
for c in candidates:
refs = set(c.get("derived_from", []))
if not refs:
errs.append(f"INNOVATION {c.get('id', '?')}: no derived_from (decorative, not causal)")
continue
if not refs & known:
errs.append(f"INNOVATION {c.get('id', '?')}: derived_from references unknown claims {sorted(refs - known)}")
outward_dependent = any(set(c.get("derived_from", [])) & outward_ids for c in candidates)
if not outward_dependent and "no external mechanism relevant" not in synthesis.get("decision", ""):
errs.append("no INNOVATION candidate derives from OUTWARD evidence — research was decorative")
if not synthesis.get("evidence_used"):
errs.append("SYNTHESIS: evidence_used empty — decisions untraceable")
if synthesis.get("disposition") == "MORE_RESEARCH" and not synthesis.get("blocking_unknowns"):
errs.append("MORE_RESEARCH disposition without named blocking_unknowns")
return errs
def validate(artifact: dict) -> list[str]:
errs = validate_outward(artifact.get("phases", {}).get("outward", {}))
errs += validate_causal_links(artifact)
if artifact.get("phases", {}).get("synthesis", {}).get("disposition") not in ("PROCEED", "MORE_RESEARCH"):
errs.append("synthesis.disposition must be PROCEED or MORE_RESEARCH")
return errs
def main_with(argv: list[str]) -> int:
artifact = json.loads(Path(argv[1]).read_text())
errs = validate(artifact)
if errs:
print("research artifact INVALID:")
for e in errs:
print(f" - {e}")
return 1
print("research artifact VALID (outward discovery + causal links verified)")
return 0
if __name__ == "__main__":
sys.exit(main_with(sys.argv))
+7 -2
View File
@@ -133,8 +133,13 @@ def compute_targets(issues: list[dict], api) -> list[dict]:
if unsatisfied:
continue
_, comments = api.get(f"/issues/{i['number']}/comments")
if has_research_round(comments if isinstance(comments, list) else []):
continue
comments = comments if isinstance(comments, list) else []
if has_research_round(comments):
# A round exists — re-queue ONLY if its disposition says MORE_RESEARCH
# (§14: the loop continues). Latest round wins (comments are append-only).
more = any(re.search(r"Disposition:\s*MORE_RESEARCH", c.get("body", ""), re.IGNORECASE) for c in comments)
if not more:
continue
targets.append(i)
return targets
+1 -1
View File
@@ -3,4 +3,4 @@
This is the main entry point for the Rune program.
"""
__version__ = "0.54.1"
__version__ = "0.54.5"
+2 -2
View File
@@ -252,7 +252,7 @@ def main() -> int:
)
check(
"Rune minor version advertises provisioning and restore verification",
'__version__ = "0.54.1"' in rune_version,
'__version__ = "0.54.5"' in rune_version,
failures,
)
# #169 policy update: auto-bump on merge is sanctioned (Nick directive:
@@ -270,7 +270,7 @@ def main() -> int:
)
check(
"release job explicitly binds its Gitea token",
"GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}" in versioning,
"GITEA_TOKEN: ${{ secrets.ROBOT_TOKEN }}" in versioning,
failures,
)
+2 -2
View File
@@ -7,7 +7,7 @@ mode system, subject header, shared memory and identity.
This is not an interface. This is how I see, speak, and interact with the world.
Version: v0.54.1
Version: v0.54.5
Features: 41 (24 original + 17 new native commands)
Usage:
@@ -1923,7 +1923,7 @@ class RuneApp(App):
# Show version info
self.chat_channel.add_message(
"system",
f"[dim]Rune v0.54.1 | Textual TUI | {len(loaded)} native modules[/dim]",
f"[dim]Rune v0.54.5 | Textual TUI | {len(loaded)} native modules[/dim]",
)
# Auto-show night mode suggestion if evening/night hours
+227
View File
@@ -0,0 +1,227 @@
"""Red-first tests for scripts/research_artifact.py — P0.4 causal validation.
Per the Forge Cognitive Research Loop handoff:
§9 OUTWARD = real discovery (≥2 sources with url+tool+learned, ≥1 query)
§11 INNOVATION candidates MUST derive_from prior claims; ≥1 from OUTWARD
§12 SYNTHESIS decisions cite evidence_used
§14 MORE_RESEARCH requires named blocking_unknowns
"""
from __future__ import annotations
import importlib.util
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parent.parent
SPEC = importlib.util.spec_from_file_location("research_artifact", ROOT / "scripts" / "research_artifact.py")
ra = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(ra)
def make_artifact(**overrides):
base = {
"issue": 42,
"round": 1,
"receipt": {"search_or_fetch_calls": 3, "distinct_web_domains": 2},
"phases": {
"inward": {
"claims": [{"id": "I1", "claim": "store uses FTS5", "evidence": ["src/rune/memory_store.py:86"]}]
},
"outward": {
"queries": ["python sqlite fts5 hybrid search"],
"sources": [
{
"id": "O1",
"url": "https://sqlite.org/fts5.html",
"title": "FTS5",
"retrieved_at": "2026-09-08T00:00Z",
"tool": "webfetch",
"content_hash": "abc",
"learned": "FTS5 supports external content tables",
},
{
"id": "O2",
"url": "https://github.com/asg017/sqlite-vec",
"title": "sqlite-vec",
"retrieved_at": "2026-09-08T00:00Z",
"tool": "webfetch",
"content_hash": "def",
"learned": "vec0 virtual tables for embedding search",
},
],
"claims": [{"id": "O-C1", "claim": "hybrid FTS+vec is viable", "supports": ["O1", "O2"]}],
},
"innovation": {
"candidates": [
{
"id": "C1",
"proposal": "hybrid recall via FTS+vec union",
"derived_from": ["I1", "O-C1"],
"expected_gain": "better recall",
"risks": ["latency"],
},
]
},
"synthesis": {
"decision": "adopt hybrid recall",
"evidence_used": ["I1", "O-C1", "C1"],
"blocking_unknowns": [],
"feasibility": "mechanism proven by O2",
"kill_test": "recall returns >0 rows on seeded store",
"disposition": "PROCEED",
},
},
}
for k, v in overrides.items():
if k == "phases":
base["phases"].update(v)
else:
base[k] = v
return base
def test_valid_artifact_passes():
assert ra.validate(make_artifact()) == []
def test_outward_single_source_fails():
a = make_artifact(
phases={
"outward": {
"queries": ["q"],
"sources": [{"id": "O1", "url": "https://x", "tool": "webfetch", "learned": "y"}],
"claims": [],
}
}
)
errs = ra.validate(a)
assert any("sources < 2" in e for e in errs)
def test_outward_missing_learned_fails():
a = make_artifact(
phases={
"outward": {
"queries": ["q"],
"sources": [
{"id": "O1", "url": "https://x", "tool": "webfetch", "learned": ""},
{"id": "O2", "url": "https://y", "tool": "webfetch", "learned": "z"},
],
"claims": [],
}
}
)
errs = ra.validate(a)
assert any("missing learned" in e for e in errs)
def test_outward_no_queries_fails():
a = make_artifact(
phases={
"outward": {
"queries": [],
"sources": [
{"id": "O1", "url": "https://x", "tool": "webfetch", "learned": "y"},
{"id": "O2", "url": "https://y", "tool": "webfetch", "learned": "z"},
],
"claims": [],
}
}
)
errs = ra.validate(a)
assert any("no search queries" in e for e in errs)
def test_innovation_without_derived_from_fails():
a = make_artifact(
phases={
"innovation": {
"candidates": [{"id": "C1", "proposal": "x", "derived_from": [], "expected_gain": "", "risks": []}]
}
}
)
errs = ra.validate(a)
assert any("no derived_from" in e for e in errs)
def test_innovation_unknown_claim_reference_fails():
a = make_artifact(
phases={
"innovation": {
"candidates": [
{"id": "C1", "proposal": "x", "derived_from": ["I999"], "expected_gain": "", "risks": []}
]
}
}
)
errs = ra.validate(a)
assert any("unknown claims" in e for e in errs)
def test_no_outward_derived_candidate_fails_unless_exempt():
a = make_artifact(
phases={
"innovation": {
"candidates": [{"id": "C1", "proposal": "x", "derived_from": ["I1"], "expected_gain": "", "risks": []}]
}
}
)
errs = ra.validate(a)
assert any("decorative" in e for e in errs)
# exemption: synthesis explicitly records no external mechanism relevant
a["phases"]["synthesis"]["decision"] = "internal-only; no external mechanism relevant"
errs2 = ra.validate(a)
assert not any("decorative" in e for e in errs2)
def test_synthesis_empty_evidence_used_fails():
a = make_artifact(
phases={
"synthesis": {
"decision": "x",
"evidence_used": [],
"blocking_unknowns": [],
"feasibility": "",
"kill_test": "",
"disposition": "PROCEED",
}
}
)
errs = ra.validate(a)
assert any("evidence_used empty" in e for e in errs)
def test_more_research_without_unknowns_fails():
a = make_artifact(
phases={
"synthesis": {
"decision": "x",
"evidence_used": ["I1"],
"blocking_unknowns": [],
"feasibility": "",
"kill_test": "",
"disposition": "MORE_RESEARCH",
}
}
)
errs = ra.validate(a)
assert any("blocking_unknowns" in e for e in errs)
def test_invalid_disposition_fails():
a = make_artifact(
phases={
"synthesis": {
"decision": "x",
"evidence_used": ["I1"],
"blocking_unknowns": [],
"feasibility": "",
"kill_test": "",
"disposition": "VIBES",
}
}
)
errs = ra.validate(a)
assert any("disposition" in e for e in errs)
+16
View File
@@ -174,3 +174,19 @@ def test_skips_issues_with_open_pr(forge, monkeypatch):
monkeypatch.setattr(rs, "open_pr_issue_refs", lambda api: {30})
targets = rs.compute_targets(forge.issues, forge)
assert [t["number"] for t in targets] == [31]
def test_more_research_round_not_treated_as_done(forge):
"""P0.6 §14: a completed round with MORE_RESEARCH disposition must NOT be
treated as done (the backfill sweep must not skip it as 'already
researched' if the label was lost — the stage re-dispatches)."""
forge.issues = [_issue(32)] # label lost (event delivery failure)
forge.comments = {32: [{"body": RESEARCH_COMMENT + "\n\nDisposition: MORE_RESEARCH (blocking: U1 perf numbers)"}]}
targets = rs.compute_targets(forge.issues, forge)
assert [t["number"] for t in targets] == [32], "MORE_RESEARCH stays eligible for re-dispatch"
def test_proceed_round_not_requeued(forge):
forge.issues = [_issue(33)] # NOT labeled (research-clear already ran)
forge.comments = {33: [{"body": RESEARCH_COMMENT + "\n\nDisposition: PROCEED"}]}
assert rs.compute_targets(forge.issues, forge) == []