feat(shapes): the practice is written where it is read, and the coverage line measures the slip (milestone 439 step 6)
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 16s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 47s
CI & Build / Python tests (push) Successful in 1m39s
CI & Build / Build & push image (push) Successful in 29s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 16s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 47s
CI & Build / Python tests (push) Successful in 1m39s
CI & Build / Build & push image (push) Successful in 29s
- reusing-code: "Before the turn ends — say what you built" — the four verdicts, the one classify_shapes(repo=…) call, and the component file as a shape. Description names the end-of-turn moment. - shape-accounting: the writer judges; audits are the check that it held. The write path SUGGESTS (no more hook instances); component file rows and whole-file canon described; scoped covers Svelte too. - _INSTRUCTIONS reuse line: "before the turn ends, say what you built (create_snippet the reusable, classify_shapes the rest)" — 1570/1600. - Coverage line: "written-shape check (7d): N turns checked, M asked, K left unjudged", from the Stop hook's recorded outcomes; silent until the question has been put. - test_guidance_ownership pins the new topic on reusing-code. - prior-art hook header no longer says it stamps instance rows. Plugin version minted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -56,7 +56,8 @@ reads Agent Skills) and in each tool's description.
|
||||
matched, not none. Rules bind; preferences guide and you keep them current;
|
||||
lessons inform.
|
||||
- Search before acting or building, scoped with the active project_id; start
|
||||
from a recorded snippet, and create_snippet what you build.
|
||||
from a recorded snippet; before the turn ends, say what you built
|
||||
(create_snippet the reusable, classify_shapes the rest).
|
||||
- Work is tasks (a fix is kind="issue"): in_progress on start, add_task_log as
|
||||
you go, done on finish; tag system_ids.
|
||||
- A plan is a milestone: find the existing one
|
||||
|
||||
@@ -922,6 +922,16 @@ async def compute_coverage(
|
||||
except Exception:
|
||||
logger.warning("stamp review read failed", exc_info=True)
|
||||
review = {}
|
||||
# The write-time figure (milestone 439): how often the end-of-turn
|
||||
# question was put on this project, and how often it was walked past.
|
||||
# An audit reads the ledger; this reads whether the reflex held.
|
||||
try:
|
||||
from scribe.services import shape_check as shape_check_svc
|
||||
|
||||
write_time = await shape_check_svc.window_summary(project_id)
|
||||
except Exception:
|
||||
logger.warning("write-time figure read failed", exc_info=True)
|
||||
write_time = None
|
||||
return {
|
||||
"total": len(rows),
|
||||
"accounted": len(rows) - unclassified,
|
||||
@@ -950,6 +960,8 @@ async def compute_coverage(
|
||||
# holds (#4608).
|
||||
"weak_stamps": review.get("weak_count", 0),
|
||||
"incoherent_canons": review.get("incoherent_count", 0),
|
||||
# {checked, asked, left, days} from the Stop hook's recorded outcomes.
|
||||
"write_time": write_time,
|
||||
# Honesty flag, not decoration: every surface that shows the number
|
||||
# is expected to carry it through.
|
||||
"estimate": True,
|
||||
@@ -1154,4 +1166,14 @@ def coverage_line(coverage: dict) -> str:
|
||||
review.append(f"{n_i} incoherent canon{'s' if n_i != 1 else ''}")
|
||||
if review:
|
||||
line += f"; {' · '.join(review)} to judge — stamps_to_review"
|
||||
# Whether the writers judged what they wrote (milestone 439). Silent until
|
||||
# the question has been put at least once: a project nobody has written to
|
||||
# through the plugin has no figure, not a perfect one.
|
||||
wt = coverage.get("write_time") or {}
|
||||
if wt.get("checked"):
|
||||
line += (
|
||||
f"; written-shape check ({wt.get('days', 7)}d): {wt['checked']} "
|
||||
f"turn{'s' if wt['checked'] != 1 else ''} checked, {wt.get('asked', 0)} asked, "
|
||||
f"{wt.get('left', 0)} left unjudged"
|
||||
)
|
||||
return line
|
||||
|
||||
@@ -27,6 +27,9 @@ report_check gives.
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from scribe.models import async_session
|
||||
from scribe.models.app_log import AppLog
|
||||
@@ -145,3 +148,45 @@ def parse_written(raw: str, *, cap: int = 200) -> list[tuple[str, str, str]]:
|
||||
if len(out) >= cap:
|
||||
break
|
||||
return out
|
||||
|
||||
|
||||
# The window the coverage line reports the write-time figure over. A week is
|
||||
# long enough to hold a few working sessions on a project and short enough
|
||||
# that a reflex that started slipping shows up while it is still news.
|
||||
WINDOW_DAYS = 7
|
||||
|
||||
|
||||
def summarise(outcomes: list[str]) -> dict:
|
||||
"""{checked, asked, left} from a list of recorded outcomes.
|
||||
|
||||
`checked` is turns the question was put to (a first stop that wrote
|
||||
something): passed + blocked. `asked` is the blocked ones. `left` is
|
||||
asks the agent walked past — the stop after a block that still had
|
||||
unjudged shapes. That last number is the slip the milestone exists to
|
||||
make visible."""
|
||||
checked = sum(1 for o in outcomes if o in ("passed", "blocked"))
|
||||
asked = sum(1 for o in outcomes if o == "blocked")
|
||||
left = sum(1 for o in outcomes if o == "left_after_block")
|
||||
return {"checked": checked, "asked": asked, "left": left}
|
||||
|
||||
|
||||
async def window_summary(project_id: int, *, days: int = WINDOW_DAYS) -> dict:
|
||||
"""The write-time figure for one project over the last ``days``."""
|
||||
since = datetime.now(timezone.utc) - timedelta(days=days)
|
||||
async with async_session() as session:
|
||||
rows = (await session.execute(
|
||||
select(AppLog.details).where(
|
||||
AppLog.category == "plugin",
|
||||
AppLog.action == "shape_check",
|
||||
AppLog.created_at >= since,
|
||||
)
|
||||
)).scalars().all()
|
||||
outcomes: list[str] = []
|
||||
for raw in rows:
|
||||
try:
|
||||
details = json.loads(raw or "{}")
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
if details.get("project_id") == project_id:
|
||||
outcomes.append(details.get("outcome") or "")
|
||||
return {**summarise(outcomes), "days": days}
|
||||
|
||||
Reference in New Issue
Block a user