Files
cfw-autumn/AGENTS.md
2026-05-08 13:04:03 +08:00

8.9 KiB

Concise Comments

Cap any single comment block at two lines. Most should be one.

  • Default to writing no comment. Only add one when the WHY is non-obvious — a hidden constraint, a subtle invariant, a workaround for a specific bug, behavior that would surprise a reader.
  • If you're tempted to write a third line, the comment is wrong: either delete it, or replace it with a clearer name / smaller function so the code explains itself.
  • Don't restate WHAT the code does — well-named identifiers already do that.
  • Don't reference the current task, fix, or callers ("used by X", "added for the Y flow", "handles the case from issue #123") — those belong in the PR description and rot as the codebase evolves.
  • This applies to inline // comments AND JSDoc/docstring blocks. A JSDoc whose summary line + one detail line covers the WHY is fine; a four-line paragraph is not.

If something genuinely needs more explanation, link to a doc or a memory file rather than inlining a wall of text in source.

Autumn Shared Utils

Before writing inline .filter(), .find(), .some(), boolean predicate, or <src>To<dst> transform logic over Autumn objects (Price, Entitlement, FullCusProduct, FullCustomer, Feature, etc.), check autumn/shared/utils/ for an existing helper. Reaching for array.filter(... === id) directly is almost always a sign the utility was missed.

The package is organized resource-first, pattern-second. Within each <resource>Utils/ folder:

  • classify*/is* boolean predicates (isPrepaidPrice, isCustomerProductPaidRecurring)
  • convert*/<src>To<dst> transforms (cusProductToPrices, entToPrice)
  • find*/Array.find lookups (findFeatureById, findPriceByFeatureId)
  • filter*/Array.filter collections (filterCustomerProductsByFeatureId)
  • enrich* files — augment with joined data (enrichEntitlementWithFeature)

If the helper you need doesn't exist, ALWAYS ask the user before adding one. Naming and folder placement are cross-cutting and non-trivial — wrong placement clutters @autumn/shared for every consumer.

Full convention (folder tree, naming nuances, anti-patterns): see the shared-utils skill.

Installing External Skills

Third-party skills installed via bunx skills add <pkg> land under each agent's local skill dir (.claude/skills/, .cursor/skills/, etc.). Those locations are NOT a source of truth — bun ai sync only reads from ai/config/skills/** and prunes anything else it manages, so a raw bunx skills add will not propagate to the other consumer repos (autumn, cloud).

Workflow

  1. Install via the CLI as usual:
    bunx skills add <owner>/<repo>
    
  2. Move the installed skill folder(s) into ai/config/skills/external/<skill-name>/. external/ is core, so both autumn and cloud consume it. Use cloud/external/ only if the skill references cloud-only code.
  3. Delete the leftover copies from .claude/skills/, .cursor/skills/, .agents/skills/, .opencode/skills/bun ai sync will recreate them as symlinks.
  4. Run bun ai sync to symlink the skill into every agent dir across every repo that pulls the ai/ submodule.

Notes

  • Skill folder names must be globally unique across ai/config/skills/** (sync flattens them).
  • Keep upstream SKILL.md frontmatter intact — name and description drive when the agent loads it. Only edit if the description is not specific enough about WHEN to use the skill.
  • If the skill ships a references/ or scripts/ subfolder, copy the whole directory tree, not just SKILL.md.
  • Re-running bunx skills add upstream-updates: install fresh, diff against ai/config/skills/external/<name>/, then promote the changes.

Scope Cache Refresh Changes Safely

When changing cache-refresh behavior for API routes:

  • Prefer editing server/src/honoMiddlewares/refreshCacheConfigs.ts to add or remove route entries.
  • Do not remove or bypass logic in server/src/honoMiddlewares/refreshCacheMiddleware.ts unless the user explicitly asks to change middleware behavior globally.
  • If the request is ambiguous, ask whether they want a route-level config change or a global middleware behavior change.

Cache Version Increment Policy

Treat cache_version as a DB-side stale-sync guard for syncItemV4, not a general cache mutation counter.

Increment cache_version only when needed

Increment only for DB updates that must not be overwritten by stale sync payloads read from cache (for example lifecycle or billing transitions).

Do not increment on cache-side/runtime patch paths

For runtime balance/reset/cache patch flows, do not bump cache_version in cache writers or Lua update scripts.

Examples:

  • FullSubject cache patch helpers (updateSubjectBalanceCache, reset/deduction cache patch sync helpers)
  • FullSubject Lua cache update scripts (updateSubjectBalances)
  • Update-balance runtime paths that patch Redis and then sync

Call-site rule for CusEntService.update

When using CusEntService.update(...) in runtime FullSubject cache patch flows, set incrementCacheVersion explicitly.

  • Use incrementCacheVersion: false for routine balance/reset/adjustment updates that are mirrored to Redis.
  • Use incrementCacheVersion: true only for intentional DB-side stale-write protection transitions.

Why

Incorrect version bumps create CACHE_VERSION_MISMATCH conflicts in syncItemV4, causing repeated invalidation and stale/lost update behavior.

Legacy exception (review-required)

There is a legacy-compatibility exception in the adjust-balance flow:

  • adjustBalanceDbAndCache currently uses CusEntService.increment/decrement, which increments cache_version.
  • Treat this as a reviewable legacy action, not a pattern to copy into new runtime/cache patch paths.
  • Any new or refactored runtime balance/reset/cache patch code should continue following this policy and avoid adding new cache-version bumps by default.

Project Context System

Projects maintain state in .context/<project>/ folders across sessions. Tasks are optional parallel workstreams within a project.

Default: NOT interacting with a project

Unless the user explicitly mentions a project or task by name, assume the current conversation is NOT associated with any project. Session-start hooks may surface a list of active projects as reference material — that alone is NOT a signal that the current work belongs to any of them.

Do not:

  • Read .context/** files proactively
  • Write, update, or append to any project's STATUS.md / DECISIONS.md / sessions
  • Assume a script, audit, or change is part of a project just because it touches files related to one (e.g. a user-of-framework script is not part of the framework's project)

Only engage with .context/<project>/ when the user explicitly references the project, opens a task in it, or asks for project-tracking actions.

Reading context (when a project IS referenced)

When the user mentions a project or task name and .context/<name>/ exists:

  1. Read project STATUS.md first (20-30 line "resume card")
  2. If the project has tasks/, list active tasks
  3. If the user mentions a specific task, read tasks/<task>/STATUS.md
  4. Read the most recent session summary if more detail is needed
  5. Do NOT read everything upfront. Use progressive disclosure.

Updating context (at breakpoints, NOT continuously)

Only update when the current work IS part of a project (see default-off rule above). When it is, update at these moments ONLY:

  • Phase or milestone completed
  • Architectural decision made (append to DECISIONS.md)
  • User says they're done or switching tasks
  • Blocker discovered or resolved
  • Task created, completed, or handed off

Do NOT update context during normal coding work. Work first, compact at breakpoints.

<<<<<<< HEAD A STATUS.md entry should record changes to the project itself -- not one-off work that merely uses the project (e.g. writing a consumer script of a framework is not a framework-project update).

A STATUS.md entry should record changes to the project itself — not one-off work that merely uses the project (e.g. writing a consumer script of a framework is not a framework-project update).

a62195ba706f12fbd35f7ecf48b4701011115101

Compaction quality

STATUS.md must be:

  • Correct (reflects actual current state, not stale)
  • Complete (no critical information missing)
  • Concise (project < 30 lines, task < 20 lines)

Always REWRITE STATUS.md completely rather than append.

File structure

.context/<project>/
  STATUS.md       -- project resume card (includes Active Tasks section)
  PLAN.md         -- phases and architecture
  DECISIONS.md    -- append-only decision log
  sessions/       -- dated session summaries
  tasks/          -- optional parallel workstreams
    <task>/
      STATUS.md   -- task resume card
      DECISIONS.md

Task handoff

When a task is being handed to another agent, ensure the task's STATUS.md is up to date -- it's the handoff artifact.