fix: correct requirement half-life to 720h per spec - #11
Merged
Conversation
The `requirement` type half-life was set to 4032h (24 weeks), matching the `correction` value. Spec is 720h (30 days). The 5.6x deviation made requirement-typed beliefs decay 5.6x slower than intended, allowing stale requirements to retain mass long after they should have decayed toward the prior. Adds test_type_half_lives.py asserting all four constants exactly so the values cannot drift silently in future edits.
robotrocketscience
added a commit
that referenced
this pull request
May 8, 2026
10 atomic commits total (this is #11). Lands the harness, dispatcher, band-checker, nightly cron, docs, and skeleton canonical JSON. The calibration pass that fills v2.0.0.json with real numbers is the explicit operator action this gate hands off. Verification: - pytest: 2624 passed, 41 skipped (no regressions vs main). - New tests: 29 unit (10 dispatcher / 19 tolerance, all stub-driven, <1s wall) + 2 inert-list/integration adjustments. - Discretion grep against entire branch diff vs github/main: clean. - benchmarks.run loads + canonical_invocations matches the spec (4 MAB + 1 LoCoMo + 1 LongMemEval + 4 StructMemEval + 1 AMA = 11). - benchmarks.tolerance compute_band/classify/check_report exercised end-to-end via tests including override-from-canonical fallback. - Workflow YAML parses; uses the same dedicated-branch pattern replay-soak adopted in #461 (sidesteps main ruleset). Blockers (require user decision before next phase): - [user] Calibration pass: run `aelf bench all --canonical --out benchmarks/results/v2.0.0.json` ≥3 times against full data dirs (HF caches populated, /tmp/LoCoMo + /tmp/StructMemEval present, ANTHROPIC_API_KEY for judge metrics if in scope), take observed range × 1.5 as the per-metric override band, replace the skeleton with the result. Until this lands, the cron's band-check is a no-op (no leaves to check → PASS by default). - [user] PR-smoke fixtures (offline): not landed in this PR. Spec recommended `tests/fixtures/bench_smoke/` pinned fixtures so a 2-min PR smoke job can exercise real adapter shapes without HF download. This needs licensed-content review (LoCoMo turns, MAB QA) and is scope for a follow-up. Pytest unit tests cover dispatcher regressions in the meantime via stubs. - [user] README badge cron-rewrite path: badge ships with placeholder state ("pending first canonical run"). The cron's `bench-canonical-results` branch commit is wired, but the README in-place rewrite (between `bench-canonical-badge:start/end` markers) is not. Wire it in a follow-up after first canonical run, or wire it now if you want — small sed in the workflow. Open questions: - adapter exit-code 2 contract (skipped_data_missing): dispatcher honors it but no adapter currently emits it. Spec called for it. Can land per-adapter as separate small PRs once data-dir plumbing is decided. Rollback: - `git revert 2a11f22..HEAD` (or revert just the `feat(cli)` commit b5564d1 to disable `aelf bench all` while keeping the modules available via `python -m benchmarks.run`).
robotrocketscience
added a commit
that referenced
this pull request
May 8, 2026
10 atomic commits total (this is #11). Lands the harness, dispatcher, band-checker, nightly cron, docs, and skeleton canonical JSON. The calibration pass that fills v2.0.0.json with real numbers is the explicit operator action this gate hands off. Verification: - pytest: 2624 passed, 41 skipped (no regressions vs main). - New tests: 29 unit (10 dispatcher / 19 tolerance, all stub-driven, <1s wall) + 2 inert-list/integration adjustments. - Discretion grep against entire branch diff vs github/main: clean. - benchmarks.run loads + canonical_invocations matches the spec (4 MAB + 1 LoCoMo + 1 LongMemEval + 4 StructMemEval + 1 AMA = 11). - benchmarks.tolerance compute_band/classify/check_report exercised end-to-end via tests including override-from-canonical fallback. - Workflow YAML parses; uses the same dedicated-branch pattern replay-soak adopted in #461 (sidesteps main ruleset). Blockers (require user decision before next phase): - [user] Calibration pass: run `aelf bench all --canonical --out benchmarks/results/v2.0.0.json` ≥3 times against full data dirs (HF caches populated, /tmp/LoCoMo + /tmp/StructMemEval present, ANTHROPIC_API_KEY for judge metrics if in scope), take observed range × 1.5 as the per-metric override band, replace the skeleton with the result. Until this lands, the cron's band-check is a no-op (no leaves to check → PASS by default). - [user] PR-smoke fixtures (offline): not landed in this PR. Spec recommended `tests/fixtures/bench_smoke/` pinned fixtures so a 2-min PR smoke job can exercise real adapter shapes without HF download. This needs licensed-content review (LoCoMo turns, MAB QA) and is scope for a follow-up. Pytest unit tests cover dispatcher regressions in the meantime via stubs. - [user] README badge cron-rewrite path: badge ships with placeholder state ("pending first canonical run"). The cron's `bench-canonical-results` branch commit is wired, but the README in-place rewrite (between `bench-canonical-badge:start/end` markers) is not. Wire it in a follow-up after first canonical run, or wire it now if you want — small sed in the workflow. Open questions: - adapter exit-code 2 contract (skipped_data_missing): dispatcher honors it but no adapter currently emits it. Spec called for it. Can land per-adapter as separate small PRs once data-dir plumbing is decided. Rollback: - `git revert 2a11f22..HEAD` (or revert just the `feat(cli)` commit b5564d1 to disable `aelf bench all` while keeping the modules available via `python -m benchmarks.run`).
robotrocketscience
added a commit
that referenced
this pull request
May 9, 2026
…ats/health)
Per MCP best-practices guidance: tools that return structured data
should support both JSON (machine-readable, default) and Markdown
(human-readable). For aelfrice's four read-only tools, the LLM in the
host loop natively reads JSON dicts; markdown is only useful when raw
tool output flows through to a human display surface.
Implements the markdown path with an always-dict return wrapper to
preserve the dict-only return type:
{"kind": "<original.kind>.markdown",
"format": "markdown",
"text": "rendered string"}
Per-tool renderers (`_render_search_markdown`, `_render_locked_markdown`,
`_render_stats_markdown`, `_render_health_markdown`) live as
module-level helpers so they're testable without fastmcp installed.
The pure handlers tool_search / tool_locked / tool_stats / tool_health
gain a `response_format: str = "json"` kwarg; default behavior is
unchanged. Wrappers expose the param via a shared _ResponseFormat
Annotated alias with `pattern=r"^(json|markdown)$"` so hosts get a
clean validation error on unknown formats.
Defensive: pure handlers fall through to JSON for unrecognized format
strings rather than raising — covered by a regression test.
6 new tests cover: markdown wrapping for each of the 4 read tools,
JSON default unchanged, unknown format fall-through.
Phase 3 I2 of the mcp-server-properly-built audit. Closes audit minor
gap #11 (no JSON/Markdown response_format).
robotrocketscience
added a commit
that referenced
this pull request
May 9, 2026
…dy for PR
Closes the mcp-builder audit of src/aelfrice/mcp_server.py started this
session. Resolves both CRITICAL findings, all six MAJOR findings, and
five of the eight MINOR findings. The deferred minors are documented
in this gate body for triage in follow-up issues.
## Commits in scope (10 atomic + 1 phase-1 gate before this)
Phase 1 — server is startable and discoverable
9adca85 feat(mcp): aelf mcp subcommand + python -m fallback
904bfb0 feat(mcp): docstrings on all 12 wrappers + AST regression guard
455eaac docs(mcp): document `aelf mcp` entrypoint + fix stale refs
46df710 gate: phase 1
Phase 2 — well-formed
a7576ad feat(mcp): annotations on every @mcp.tool
232d8ab feat(mcp): instructions= overview + 9-vs-12 stale comment fix
5f21c29 fix(mcp): tool_lock structured error vs AssertionError
Phase 3 — input/output discipline
6c6bf6c feat(mcp): Pydantic Field constraints via Annotated
174d245 feat(mcp): cursor pagination on tool_locked
7f04675 feat(mcp): response_format=markdown on read tools
Phase 4 — wrapper-layer testing
0214c17 test(mcp): wrapper-layer tests via static AST + fastmcp shim
## Verification
- pytest 76/76 passing across MCP test files (40 prior + 36 new):
test_mcp_server.py, test_mcp_lock_via_worker.py,
test_mcp_wrapper_layer.py, test_cli_mcp.py
- aelf --help: 'mcp' subcommand visible with help string
- aelf mcp (no [mcp] extra): exits 1 with actionable stderr
- Static AST guards: docstrings, annotations, instructions=, store
lifetime, no print()-to-stdout, all green
- fastmcp shim test: 12 tools register, all with full annotations,
read-only / destructive sets match expected
- Discretion grep: clean (only mentions Claude Code/Desktop, both
pre-existing on main as the intended MCP host targets)
## Audit findings closure
CRITICAL #1 Server unstartable → CLOSED (Phase 1 C1)
CRITICAL #2 Empty tool descriptions → CLOSED (Phase 1 C2)
MAJOR #3 No tool annotations → CLOSED (Phase 2 M1)
MAJOR #4 No Pydantic input validation → CLOSED (Phase 3 I1)
MAJOR #5 No server instructions= → CLOSED (Phase 2 M2)
MAJOR #6 No README/docs MCP setup section → CLOSED (Phase 1 C3)
MAJOR #7 Registration layer untested → CLOSED (Phase 4)
MAJOR #8 tool_lock hard assert → CLOSED (Phase 2 M4)
MINOR #11 No response_format enum → CLOSED (Phase 3 I2)
MINOR #12 No pagination on aelf_locked → CLOSED (Phase 3 I3)
MINOR #13 Stale 9-vs-12 tools comment → CLOSED (Phase 2 M3)
## Deferred (file follow-up issues)
MINOR #1 Server name "aelfrice" vs convention "aelfrice_mcp"
— backwards-compat break, not load-bearing. Defer.
MINOR #6 Sync handlers (no async def) — defensible for SQLite,
revisit if/when an async I/O dep lands.
MINOR #14 Polymorphic tool_onboard (3 input shapes in one tool)
— design call. Re-evaluate after host telemetry.
MINOR #15 Untyped fastmcp cast `_FastMCP: Any` — fastmcp lacks
stubs upstream. Pragmatic; revisit when stubs ship.
## Behavior changes worth flagging in PR description
- `tool_locked` return shape: ADDED keys total/has_more/next_offset.
Existing keys (kind/n/locked) preserved. Callers that consumed
`n` as "total locks across all locks" should switch to `total`.
- `tool_locked` returns first 50 by default instead of all locks. Tests
on stores with <50 locks behave identically.
- `tool_lock` returns lock.error dict (with `error` field) instead of
raising AssertionError on empty derivation. Callers that grep for
AssertionError need to update; callers that check `out["kind"]` are
fine.
## Blockers (require user decision before next phase)
- [user] Open PR? Branch is local-only on `feat/mcp-server-properly-built`.
Need a `git push -u github feat/mcp-server-properly-built` to land it,
then `gh pr create`. Not done by this gate.
- [user] File the four deferred-minor follow-up issues now or after
PR merge?
- [user] Resume parked feat/aelf-upgrade-imperative work next session?
That branch has no commits but the worktree state is captured in the
end-of-session handoff.
Open questions: (none)
Rollback:
- Whole branch: never merged → just delete `feat/mcp-server-properly-built`.
- Per-phase: `git revert <phase-commit-range>` (see commit list above).
robotrocketscience
added a commit
that referenced
this pull request
May 9, 2026
…ats/health)
Per MCP best-practices guidance: tools that return structured data
should support both JSON (machine-readable, default) and Markdown
(human-readable). For aelfrice's four read-only tools, the LLM in the
host loop natively reads JSON dicts; markdown is only useful when raw
tool output flows through to a human display surface.
Implements the markdown path with an always-dict return wrapper to
preserve the dict-only return type:
{"kind": "<original.kind>.markdown",
"format": "markdown",
"text": "rendered string"}
Per-tool renderers (`_render_search_markdown`, `_render_locked_markdown`,
`_render_stats_markdown`, `_render_health_markdown`) live as
module-level helpers so they're testable without fastmcp installed.
The pure handlers tool_search / tool_locked / tool_stats / tool_health
gain a `response_format: str = "json"` kwarg; default behavior is
unchanged. Wrappers expose the param via a shared _ResponseFormat
Annotated alias with `pattern=r"^(json|markdown)$"` so hosts get a
clean validation error on unknown formats.
Defensive: pure handlers fall through to JSON for unrecognized format
strings rather than raising — covered by a regression test.
6 new tests cover: markdown wrapping for each of the 4 read tools,
JSON default unchanged, unknown format fall-through.
Phase 3 I2 of the mcp-server-properly-built audit. Closes audit minor
gap #11 (no JSON/Markdown response_format).
robotrocketscience
added a commit
that referenced
this pull request
May 9, 2026
…dy for PR
Closes the mcp-builder audit of src/aelfrice/mcp_server.py started this
session. Resolves both CRITICAL findings, all six MAJOR findings, and
five of the eight MINOR findings. The deferred minors are documented
in this gate body for triage in follow-up issues.
## Commits in scope (10 atomic + 1 phase-1 gate before this)
Phase 1 — server is startable and discoverable
9adca85 feat(mcp): aelf mcp subcommand + python -m fallback
904bfb0 feat(mcp): docstrings on all 12 wrappers + AST regression guard
455eaac docs(mcp): document `aelf mcp` entrypoint + fix stale refs
46df710 gate: phase 1
Phase 2 — well-formed
a7576ad feat(mcp): annotations on every @mcp.tool
232d8ab feat(mcp): instructions= overview + 9-vs-12 stale comment fix
5f21c29 fix(mcp): tool_lock structured error vs AssertionError
Phase 3 — input/output discipline
6c6bf6c feat(mcp): Pydantic Field constraints via Annotated
174d245 feat(mcp): cursor pagination on tool_locked
7f04675 feat(mcp): response_format=markdown on read tools
Phase 4 — wrapper-layer testing
0214c17 test(mcp): wrapper-layer tests via static AST + fastmcp shim
## Verification
- pytest 76/76 passing across MCP test files (40 prior + 36 new):
test_mcp_server.py, test_mcp_lock_via_worker.py,
test_mcp_wrapper_layer.py, test_cli_mcp.py
- aelf --help: 'mcp' subcommand visible with help string
- aelf mcp (no [mcp] extra): exits 1 with actionable stderr
- Static AST guards: docstrings, annotations, instructions=, store
lifetime, no print()-to-stdout, all green
- fastmcp shim test: 12 tools register, all with full annotations,
read-only / destructive sets match expected
- Discretion grep: clean (only mentions Claude Code/Desktop, both
pre-existing on main as the intended MCP host targets)
## Audit findings closure
CRITICAL #1 Server unstartable → CLOSED (Phase 1 C1)
CRITICAL #2 Empty tool descriptions → CLOSED (Phase 1 C2)
MAJOR #3 No tool annotations → CLOSED (Phase 2 M1)
MAJOR #4 No Pydantic input validation → CLOSED (Phase 3 I1)
MAJOR #5 No server instructions= → CLOSED (Phase 2 M2)
MAJOR #6 No README/docs MCP setup section → CLOSED (Phase 1 C3)
MAJOR #7 Registration layer untested → CLOSED (Phase 4)
MAJOR #8 tool_lock hard assert → CLOSED (Phase 2 M4)
MINOR #11 No response_format enum → CLOSED (Phase 3 I2)
MINOR #12 No pagination on aelf_locked → CLOSED (Phase 3 I3)
MINOR #13 Stale 9-vs-12 tools comment → CLOSED (Phase 2 M3)
## Deferred (file follow-up issues)
MINOR #1 Server name "aelfrice" vs convention "aelfrice_mcp"
— backwards-compat break, not load-bearing. Defer.
MINOR #6 Sync handlers (no async def) — defensible for SQLite,
revisit if/when an async I/O dep lands.
MINOR #14 Polymorphic tool_onboard (3 input shapes in one tool)
— design call. Re-evaluate after host telemetry.
MINOR #15 Untyped fastmcp cast `_FastMCP: Any` — fastmcp lacks
stubs upstream. Pragmatic; revisit when stubs ship.
## Behavior changes worth flagging in PR description
- `tool_locked` return shape: ADDED keys total/has_more/next_offset.
Existing keys (kind/n/locked) preserved. Callers that consumed
`n` as "total locks across all locks" should switch to `total`.
- `tool_locked` returns first 50 by default instead of all locks. Tests
on stores with <50 locks behave identically.
- `tool_lock` returns lock.error dict (with `error` field) instead of
raising AssertionError on empty derivation. Callers that grep for
AssertionError need to update; callers that check `out["kind"]` are
fine.
## Blockers (require user decision before next phase)
- [user] Open PR? Branch is local-only on `feat/mcp-server-properly-built`.
Need a `git push -u github feat/mcp-server-properly-built` to land it,
then `gh pr create`. Not done by this gate.
- [user] File the four deferred-minor follow-up issues now or after
PR merge?
- [user] Resume parked feat/aelf-upgrade-imperative work next session?
That branch has no commits but the worktree state is captured in the
end-of-session handoff.
Open questions: (none)
Rollback:
- Whole branch: never merged → just delete `feat/mcp-server-properly-built`.
- Per-phase: `git revert <phase-commit-range>` (see commit list above).
This was referenced Jul 29, 2026
[Umbrella] Inert, unreachable, and decorative mechanisms — graph substrate and the delete list
#1162
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
requirementtype half-life was 4032h (24 weeks) inscoring.py:25. Spec value is 720h (30 days), carried from the previous codebase's CHANGELOG. The 5.6× deviation slowed decay of requirement-typed beliefs to a quarter of the intended rate.tests/test_type_half_lives.pyasserting all four constants exactly (factual 336h, requirement 720h, preference 2016h, correction 4032h) plus thetype_half_life()lookup behavior, so values cannot drift silently.Test plan
uv run pytest -q→ 24 passed (was 17, +7 new)git log --show-signature -1→Good "git" signature