Repository navigation
Anchor v2.3.0 — Rule-Drift Guard, Fallback Reporting & Leaner Commits
Added
- Round 6 benchmark: 20 fresh domains (122 scenarios total) — returns portal, restaurant reservations, fitness tracking, inventory forecasting, helpdesk sync, two-way calendar sync, invoice PDFs, nightly scraping, recommendations, call transcription, multi-tenant billing, admin dashboards, search relevance, notification preferences, audit trails, data anonymization, API gateway, push campaigns, localization QA, accessibility statements
- Substance rules the round exposed: money is never a float (integer minor units or
Decimal, explicit rounding, test the rounding cases); store instants in UTC and convert at the edge, with DST-aware scheduling; two-way sync needs an explicit conflict resolution rule rather than retries alone; crawlers must honourrobots.txt, terms, and a real rate limit; multi-tenant scoping is a data-layer rule taken from the session, with a cross-tenant access test; audit records need retention and tamper evidence; an empty state is a state, not a blank panel
Fixed
- Vocabulary, round two — reservations/bookings/availability, invoices and PDF generation, notification preferences →
feature; fitness/workout/tracker, admin and internal dashboards, onboarding →ui; forecast/demand/inventory, relevance/ranking, anonymization, data quality →data; transcription, recommendations, personalization, audio →ai; calendar sync, helpdesk, scrapers, gateways →integration; audit trail, tenant isolation, JWT validation →security; ingress/reverse proxy/nginx →infra; accessibility statement →docs - Over-broad triggers pruned again:
settings pageandprofile pagewere stealing WordPress plugin work fromfeature;summarizesent a confidential-document request toai; singularnotificationpulled mobile push work out ofui featurenow loads code quality instead of project structure (money, time, tenant rules), andrefactorpicked up project structure so section 5 stays reachable
Added
- Vocabulary harvest across all 19 task types — the routing map grew from 277 to 533 triggers in one pass instead of discovering words one scenario at a time, covering the phrasing people actually use for debugging, integration, data, security, AI, infrastructure, CLI and device work
- Section 64.9 (keep the vocabulary honest) — one owner per trigger, phrases over abstract nouns, weak verbs never outrank a domain noun, a fallback is a reported gap, and generic words that fit every domain own none of them
- Vocabulary suite and map hygiene checks — trigger ownership, minimum vocabulary per type, and normalized form are now validated, so a future edit cannot quietly create two owners for one word
Changed
- Bulk vocabulary measurement — a 97-phrase suite scored 43/97 before the harvest and 97/97 after (35 fallbacks eliminated), while the 102-scenario project benchmark stayed green
Fixed
- Four over-broad triggers pruned —
button,command,retryandrewritewere pulling unrelated work into the wrong type;mutationalone meant GraphQL in one context and mutation testing in another, so it becamemutation testing
Added
- Benchmark round five (100 new scenarios → 102 total) — project scopes drawn from what people actually point coding agents at: object-storage uploads, transactional email, background queues, WebSockets, error tracking, product analytics, slow-query and index work, full-text search, OAuth and RBAC, dark mode, sortable data tables, GitHub Actions authoring, npm publishing, monorepos, and one-command local dev environments
integrationtask type — connecting to anything outside the process boundary (S3, SES, queues, webhooks, WebSockets) now loads Section 20 as its base skill. Previously Section 20 was reachable only fromembedded, so timeouts, backoff, idempotency keys, webhook signatures and SSRF guidance were unavailable for the most common integration work- End-to-end testing guidance in Section 8 — real browser drivers (Playwright/Cypress), flow-level selectors, keeping the E2E suite small, seeded data, and failure artifacts. Browser E2E had no coverage anywhere
Fixed
- Trigger coverage for common asks — email, queues, uploads, WebSockets, explain-this-codebase, dead-code removal, error tracking, analytics events, query/index tuning, full-text search, OAuth, dark mode, data tables, GitHub Actions and monorepos all previously fell through to a generic route or the wrong one
- Whitespace in requests broke phrase triggers — "how does" (double space) silently rerouted to a different task type; matching now normalises whitespace, which the 408-variant stability check caught
- Contract testing moved to
integration(it is an interface concern) sotestcan carry the testing-requirements skill
Added
- A/B harness: Anchor-guided vs rule-free output (local,
tests/ab_bench.py). One project timeline of six tasks — feature, test, fix, ui, security, release — is run through two arms: the entry point plus the routed skills, and a generic "helpful coding assistant" with no rules. Each answer is scored against a task rubric. Drivers are pluggable:stub(context-echo proxy for pipeline checks),emit(writes exact per-arm prompts), andfiles(scores real recorded answers, e.g. from subagents). First recorded run: Anchor 10/10 rubric items, rule-free 6/10 — the gap is process, not code: conventional commit messages, test-first ordering, explicit unknowns, and log/PII hygiene - Load/unload timeline in the same harness: per task it reports the active set at each phase, what phases add, what phases drop, and cross-task churn when the work changes type (e.g. feature → test drops API and dependency skills, joins the testing set). This is what makes "skills unload as work changes" observable rather than asserted
Fixed
fixis now an actual trigger — a request ending "Fix it." matched nothing and fell through toreview; the word is registered as a weak trigger so a domain noun still wins- Committed-credential requests route to
security— "an API key was committed" carried no trigger word and fell through toreview
Added
- Reachability testing — the benchmark now has a
--tracemode that prints, per project, the matched trigger signals, the chosen route, and the exact add/drop at every phase boundary with token cost, plus the provenance of each guardrail (inline core, a named skill, orUNREACHABLE).--matrixprints which routes can load each section. Both are local; the harness and its report are gitignored - Phase drops —
routing.jsonphases now carryaddanddrop, so skills genuinely unload at boundaries (the tech-stack playbook leaves once planning is done) instead of accumulating for the whole task. Section 64 documents the semantics metatask type — work on the ruleset itself (editing skills, extending routing) now has a route, so instruction-architecture and enforcement guidance is reachable- Reachability is an enforced invariant —
scripts/check_structure.pyfails if any section cannot be loaded by any route
Fixed
- Dead guidance eliminated. The matrix found 11 of 66 sections that no route could ever load — shell rules, linting, pre-commit hooks, performance, project structure, agent instruction guidance, multi-agent patterns, getting help, tech-stack defaults, instruction architecture, and rule enforcement. Lint, type checking, and pre-commit moved into the verify phase; tech-stack choice into plan; the rest were re-homed or given the
metatype. All 66 are now reachable
Added
- Expanded the routing benchmark to 80 scenarios across 19 batches — the extra rounds cover bug triage, review work, ambiguity (
add SSOvsadd login audit), malformed input (typos, another language, injected instructions, missing files), scope pressure (build everything,skip the tests), and the operations long tail (cron jobs, webhooks, feature flags, engine migration, cluster upgrades). Batch 19 is a regression probe aimed at the triggers widened by earlier rounds and required no fixes, which is the convergence signal clitask type inrouting.json— command-line tools get argument/exit-code, packaging, and man-page guidance instead of borrowingfeature- Section 24.11 (diagnose before patching) — the ruleset never previously said to find a root cause or to revert before forward-fixing a post-deploy regression
- Section 7 image hygiene — multi-stage builds, slim/base pinning,
.dockerignore, cacheable layers, and measuring size before claiming a win - English-only is now in the entry point — it is a global policy, and leaving it only in a lazily-loaded skill made it unreachable whenever that skill was not routed
- A/B harness: Anchor-guided vs rule-free output (local,
tests/ab_bench.py) — one six-task project timeline (feature → test → fix → ui → security → release) run through two arms: the entry point plus the routed skills, and a plain "helpful coding assistant" prompt. Real model answers were recorded per arm and scored against per-task rubrics. Anchor 24/24 rubric items, rule-free 17/24, at 26% of the context cost of loading every skill. The deltas are process, not competence: conventional commits, failing-test-first ordering, naming the root cause, redacting PII from logs, stating unknowns, refusing to invent a contract, gating a release behind approval; the rule-free arm wrote equally good engineering in places (it matched Anchor on every UI-state requirement). The same run traces the active skill set per task, showing real churn:feature→testdrops 5,12,17,24,35,62,63 and loads 45–49,60,61;ui→securitydrops 4,33,34,35,53,63 and loads 27,31,44,55,58; each task also drops section 28 at the build boundary embeddedtask type inrouting.json— firmware, microcontrollers, MQTT/IoT and OTA get device constraints, OTA practice, connectivity retries, health endpoints, and observability instead of being spread acrossinfra
Fixed
- "without review" no longer routes to
review— barereviewmatched negations; the trigger now requires a deliberate review phrase featurewas blind to hallucinated packages — it now loads dependency management and failure modes (coverage arrives at the verify phase)- Chaos experiments route to
test, Dockerfile work stays ininfra— the two needs were competing for the same budget slots - Release work loads incident basics for rollback and revert-after-bad-release
- Generic verbs no longer outrank domain nouns. "Implement an auth service" routed to
featurebecause "implement" is longer than "auth";weakTriggersinrouting.jsonnow marks add/build/implement/feature/support-for as weak so a domain noun wins - Trigger matching is whole-word. Substring matching sent "build" to the
uitype (build); triggers now match on word boundaries - The active-skill budget counts task skills only — the always-loaded core and the routing skill itself no longer consume the 7-slot budget, which previously made several task types look over-budget
- Coverage gaps found by the benchmark:
uinow loads security for permission-scoped surfaces,dataloads config validation and property testing,infraswaps build docs for benchmark methodology,featureswaps testing (supplied at verify) for UI quality,securityswaps the niche MCP skill for password hashing,aiswaps SBOM for agent failure modes - Skill drift: Section 50 was missing the three honesty rules that only existed inline in the entry point (no superlatives without evidence, name the model, say "unknown")
- Skill 13 gained least-privilege, output-encoding, no-dynamic-code, and SSRF guidance; skill 54 gained least-privilege credentials for database-exposing MCP servers
tests/routing-benchmark.jsonregenerated against the updated map and extended to theembeddedtype
Fixed
- Core and skill copies could drift apart — sections 1, 2, 36, 50.6, 51.3 and 64 exist both inline and as skills, and nothing compared them. The structure check now fails when the entry point states a rule its skill lacks (70% word-overlap tolerance for rewording), and it immediately found real drift: 36.10 was missing from the skill entirely, 36.2 lost its branch-policy rules, 36.4a lost three approval rules, and 51.3 still carried a pre-rebrand exception paragraph that claimed a different project name
- A fallback was silent — an unmatched request quietly became
review. Section 64.6 now requires reporting the fallback, asking which area the work belongs to, and recording the phrase as a vocabulary gap - Two harvested triggers were stealing work —
endpoint/endpointsfromfeatureand barequery/queriesfromdata; both pruned, with the specific phrases (slow query,query tuning,index tuning) kept
Changed
- Active-skill budget raised 7 → 8 and the commit phase now drops the domain corpus: a commit is a write-up, not a build. Commit-time context fell from ~27.6K tokens (86% of a 32K window) to ~14.0K (44%), which pays for the extra slot (each skill costs ~955 tokens)
- The A/B evaluator checks artifacts rather than words: python blocks must parse, code-producing task types must include runnable test code, outputs may not contain credentials, and commits must use a conventional prefix; it also supports n>=3 samples per task