@ryuenn3123/agentic-senior-core 4.0.2 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/.agent-context/rules/api-docs.md +14 -0
  2. package/.agent-context/rules/api-versioning.md +93 -0
  3. package/.agent-context/rules/background-jobs.md +93 -0
  4. package/.agent-context/rules/config-and-flags.md +79 -0
  5. package/.agent-context/rules/database-design.md +32 -0
  6. package/.agent-context/rules/frontend-architecture.md +35 -0
  7. package/.agent-context/rules/migrations.md +84 -0
  8. package/.agent-context/rules/observability.md +69 -0
  9. package/.agent-context/rules/resilience.md +78 -0
  10. package/.agent-context/rules/security.md +28 -0
  11. package/AGENTS.md +8 -8
  12. package/README.md +42 -9
  13. package/bin/agentic-senior-core.js +6 -0
  14. package/lib/cli/audits/typography-palette-anti-repeat/color-utils.mjs +156 -0
  15. package/lib/cli/audits/typography-palette-anti-repeat/file-scanner.mjs +103 -0
  16. package/lib/cli/audits/typography-palette-anti-repeat/typography-utils.mjs +70 -0
  17. package/lib/cli/audits/typography-palette-anti-repeat-audit.mjs +255 -0
  18. package/lib/cli/commands/audit-design-anti-repeat.mjs +198 -0
  19. package/lib/cli/commands/upgrade.mjs +1 -0
  20. package/lib/cli/utils.mjs +1 -0
  21. package/package.json +4 -4
  22. package/scripts/audit-cache-layer-contract.mjs +5 -0
  23. package/scripts/audit-caching-scope-hygiene.mjs +5 -0
  24. package/scripts/audit-typography-palette-anti-repeat.mjs +120 -0
  25. package/scripts/clean-local-artifacts.mjs +0 -1
  26. package/scripts/frontend-usability-audit.mjs +5 -8
  27. package/scripts/release-gate/static-checks.mjs +7 -7
  28. package/scripts/validate/config.mjs +0 -2
  29. package/scripts/validate/coverage-checks.mjs +1 -42
  30. package/scripts/validate.mjs +42 -7
  31. package/scripts/migrate-rule-format/id-prefix-table.mjs +0 -37
  32. package/scripts/migrate-rule-format/parse-legacy.mjs +0 -180
  33. package/scripts/migrate-rule-format/render-new.mjs +0 -169
  34. package/scripts/migrate-rule-format/roundtrip-validate.mjs +0 -89
  35. package/scripts/migrate-rule-format.mjs +0 -192
  36. package/scripts/v3-purge-audit.mjs +0 -236
@@ -0,0 +1,78 @@
1
+ ---
2
+ id_prefix: RES
3
+ domain: resilience
4
+ priority: critical
5
+ scope: backend
6
+ last_validated: 2026-05-17
7
+ applies_to:
8
+ - backend
9
+ - fullstack
10
+ keywords:
11
+ - resilience
12
+ - timeout
13
+ - retry
14
+ - deadline
15
+ - degradation
16
+ - backpressure
17
+ ---
18
+
19
+ # Resilience Boundary
20
+
21
+ The system must remain useful when a dependency is slow, partial, or unavailable. Resilience is the property that the user-facing operation either completes within an agreed budget, fails fast with a clear outcome, or runs in a documented degraded mode; it is not the absence of failure. Vendor names and library names that may appear in commentary (timeout libraries, service meshes, adaptive-concurrency runtimes) are not authority for this rule.
22
+
23
+ ## RES-001: Timeouts and deadlines (Mandatory)
24
+
25
+ 1. Every outbound network or inter-process call must carry an explicit timeout derived from the user-facing operation's worst-acceptable latency. Library defaults and "no timeout" are not acceptable.
26
+ 2. The timeout must be smaller than the upstream caller's remaining latency budget. A request handler that has 800 ms of remaining budget must not issue a downstream call configured to wait 5 seconds.
27
+ 3. The system must propagate the caller's remaining deadline downstream so a downstream operation cannot continue past the upstream's expiration. If the platform exposes a deadline header or a context cancellation primitive, use it; otherwise, transmit the remaining budget as an explicit field on the call.
28
+ 4. Connect, read, and idle timeouts must be set independently. A combined "request timeout" that hides which phase exceeded the budget makes failure analysis ambiguous.
29
+ 5. Reject reliance on default timeouts. Reject "infinite" or platform-maximum timeouts on user-facing call paths. Reject configuring a downstream timeout larger than the upstream caller's remaining budget.
30
+
31
+ ## RES-002: Retries (Mandatory)
32
+
33
+ 1. Retries are allowed only on operations that are idempotent on the target, or on operations that carry an idempotency identifier the target honors within a documented retention window.
34
+ 2. Retries must use exponential backoff with jitter and a documented attempt cap. Fixed-interval retries are forbidden because they synchronize callers during incidents.
35
+ 3. Each retry must inherit the caller's remaining deadline; the sum of attempts plus backoff must not exceed the original budget.
36
+ 4. Retries must distinguish retriable failures (transient network, explicit retry-after, documented `5xx` semantics) from non-retriable failures (validation, authorization, business-rule rejection); non-retriable failures must surface immediately without consuming retry budget.
37
+ 5. Reject retries on non-idempotent writes that lack an idempotency identifier on the target. Reject retry storms: any pattern where many callers retry on the same schedule, without jitter, and without a global cap, is a defect even if each caller is locally well-behaved.
38
+
39
+ ## RES-003: Failing fast on unhealthy dependencies (Mandatory)
40
+
41
+ 1. When a dependency's failure rate, latency, or queue depth indicates it cannot serve traffic within the user-facing latency budget, the calling component must shed load on that dependency rather than continue to issue calls that will time out.
42
+ 2. Load shedding is an outcome, not a named pattern. The system may achieve it through any mechanism appropriate to the platform: a service mesh fault-tolerance policy, an adaptive-concurrency limiter, an in-process state machine that rejects calls during a recovery window, or a rate limiter informed by health probes. Choose what the platform supports; do not write a custom mechanism when the platform provides one.
43
+ 3. The shedding component must emit a telemetry event when it transitions between healthy, degraded, and shedding states, so an operator can correlate user impact with the decision to shed.
44
+ 4. The shedding component must have a documented recovery path: how it re-admits traffic to the dependency, how it confirms the dependency is healthy, and how it bounds the rate of recovery to avoid a thundering herd.
45
+ 5. Reject open-loop retry behavior against an upstream that has been unhealthy for longer than its documented recovery window. Reject "fail open" defaults on security-relevant calls (authorization, license enforcement); those must fail closed and the user-facing impact must be a documented degraded mode, not silent permission.
46
+
47
+ ## RES-004: Dependency isolation (Mandatory)
48
+
49
+ 1. Independent dependencies must use independent resource pools (connection pools, thread pools, semaphore quotas, or platform-equivalent admission control) so the saturation of one upstream cannot exhaust the pools used by another.
50
+ 2. The size of each pool must be derived from the dependency's documented capacity and the caller's latency budget, not from a default.
51
+ 3. The system must expose the in-flight count, the queue depth, and the rejection count for each pool as telemetry, so an operator can distinguish "dependency slow" from "caller's pool exhausted".
52
+ 4. Reject sharing one global pool across unrelated downstream calls when the platform supports separation. Reject silent unbounded queueing in front of a saturated pool; bounded queues with explicit rejection are mandatory.
53
+
54
+ ## RES-005: Graceful degradation (Mandatory)
55
+
56
+ 1. For each dependency, the implementation must answer one question explicitly in code or in a runbook: "what does the caller still do for the user when this dependency is unavailable?" A generic catch-all error response is not an answer.
57
+ 2. Acceptable degraded behaviors include: serving a cached or older version of the data with a freshness indicator, returning a partial result with the unavailable section labelled as such, queueing the request for later processing with a documented user-visible acknowledgement, or refusing the operation with a clear error that names the unavailable subsystem.
58
+ 3. The user-facing error path must distinguish "we cannot do this right now, retry later" from "we will not do this, do not retry"; clients reading these responses act differently on the two.
59
+ 4. Degraded behavior must be observable: a request served from a fallback path must emit telemetry that records which fallback fired, why, and how stale or partial the result was.
60
+ 5. Reject silent degradation. A response that hides a fallback from the caller, or that omits a stale-data marker when the data is stale, is a defect.
61
+
62
+ ## RES-006: Backpressure across producers and consumers (Mandatory)
63
+
64
+ 1. Any boundary between an unbounded producer (user requests, upstream events, ingestion stream) and a bounded consumer must apply backpressure: shed load, throttle the producer, or expose lag back to the producer. Silent unbounded growth of an in-memory or on-disk queue is forbidden.
65
+ 2. The boundary must expose its current lag, drop rate, and rejection reason as telemetry, so an operator can choose between scaling the consumer, slowing the producer, or shedding low-value traffic.
66
+ 3. Reject "queue grows until OOM" as an acceptable failure mode. Reject "we will scale later" as a substitute for explicit backpressure on a path that already takes user traffic.
67
+
68
+ ## RES-007: Citations and freshness
69
+
70
+ Authority and background reading for the rules in this file:
71
+
72
+ - AWS Well-Architected Framework, Reliability Pillar (REL05 and surrounding controls): authority on dependency isolation, timeouts, retries with backoff, and graceful degradation as deployment-architecture concerns. Verify against the Reliability Pillar version current at audit time.
73
+ - Google SRE Workbook chapters on overload and addressing cascading failures: background reading on load shedding, graceful degradation, and the difference between transient and persistent failure modes.
74
+ - IETF RFC 7231 and successor specifications for HTTP semantics: authority for which response statuses are safe to retry by default.
75
+ - IETF RFC 7234 and `Retry-After` semantics: authority for cooperating with explicit retry-control signals from upstreams.
76
+
77
+ Vendor-specific resilience libraries (circuit-breaker libraries, service-mesh fault-tolerance modules, language-runtime cancellation primitives) are illustrative implementations of the outcomes above; they are not authority. Choose the implementation appropriate to the platform.
78
+ <!-- DURABILITY CHECK: Rule relies exclusively on architectural invariants and relative operational thresholds. Valid beyond standard tooling lifecycles. -->
@@ -3,6 +3,7 @@ id_prefix: SEC
3
3
  domain: security
4
4
  priority: critical
5
5
  scope: all-tasks
6
+ last_validated: 2026-05-17
6
7
  applies_to:
7
8
  - backend
8
9
  - frontend
@@ -43,3 +44,30 @@ Use the security model and libraries already present in the project. If security
43
44
  4. Sanitization must match the sink: SQL, shell, file path, log, HTML, template, and URL contexts need different protections.
44
45
  5. Authorization must be resource-aware when data ownership matters. Prefer row, tenant, account, organization, or resource-level checks over role-only checks for sensitive records.
45
46
  6. For high-risk changes, check current framework security docs and record the relevant source or assumption in the implementation notes.
47
+
48
+ ## SEC-003: Authentication versus authorization
49
+
50
+ 1. Authentication proves identity (this caller is who they claim to be); authorization grants capability (this identity may perform this action on this resource). The two are different concerns and must be implemented as distinct layers.
51
+ 2. Request-handling code must not conflate them. A handler that checks "is this caller logged in?" and treats the answer as permission to mutate the resource is a defect, regardless of how strong the authentication check is.
52
+ 3. The authorization decision layer must be independently testable from the request-handling layer. The system must support tests that pass an authenticated principal plus a target resource and a requested action and assert the decision, without standing up the full HTTP transport.
53
+ 4. Authorization decisions must be recorded as audit events [REF:OBS-004]: who acted, what was acted upon, the requested action, and the decision. A "permitted" decision and a "denied" decision both belong on the audit stream.
54
+ 5. Reject controllers that mix authentication checks, business policy, and persistence in one block. Reject role-only authorization on resources that have owners; ownership-, tenant-, or relationship-aware authorization is required when records have owners.
55
+
56
+ ## SEC-004: Credential storage
57
+
58
+ 1. Passwords and other reversible-equivalent credentials must be hashed with a memory-hard, computationally-tunable algorithm intended for password storage. Argon2id is the current widely accepted default; bcrypt remains acceptable on platforms where a memory-hard implementation is unavailable or on platforms where the operational surface has already standardized on it. The mechanism must be tunable: as hardware improves, the work factor must be raised without a code change.
59
+ 2. General-purpose hash functions (MD5, SHA-1, SHA-256, SHA-3 by themselves) are forbidden for password storage. They are designed to be fast; password storage requires a function designed to be slow under attacker hardware.
60
+ 3. Stored credentials must include a per-credential random salt at the size and shape the chosen algorithm specifies; global pepper, if used, must come from the secret manager [REF:CFG-002], not from source.
61
+ 4. Verification must be constant-time for the comparison step where the platform supports it, to limit timing-side-channel inference about partial matches.
62
+ 5. Credential rotation must be supported as a runtime operation: the system must be able to re-hash a credential at the next successful authentication when the work factor or algorithm changes, without forcing a coordinated reset.
63
+ 6. Reject storing credentials with a general-purpose hash. Reject storing credentials in plaintext for any reason, including "for support recovery". Reject pinning a work factor that the platform's hardware has outpaced; the work factor is a tuning parameter, not a constant.
64
+
65
+ ## SEC-005: Service-to-service authentication
66
+
67
+ 1. Service-to-service identity must be cryptographically verifiable at the receiving end. Acceptable mechanisms include mutual TLS with verified peer certificates, OIDC client-credentials flow with short-lived tokens, signed bearer tokens with a documented issuer and verifiable signature, or platform-equivalent verifiable identity (workload identity, signed JWT-over-mTLS).
68
+ 2. Tokens used between services must carry a short time-to-live (the value depends on the platform's revocation latency and the operation's blast radius; record the chosen TTL and the rationale, do not inherit a default).
69
+ 3. The receiving service must validate the token's issuer, signature, audience, expiration, and not-before fields on every request. A cached "yes this is valid" decision that bypasses signature validation is a defect.
70
+ 4. Reject shared static tokens (a single long-lived API key embedded in every caller) as the sole identity mechanism between services. Static tokens are acceptable only as one factor inside a stronger mechanism, or as a deliberate fallback for narrowly-scoped, audit-logged emergency access.
71
+ 5. Reject IP-allow-list as a substitute for cryptographic identity on networks the producer does not exclusively control. Reject "trusted network" as a substitute for verifying the caller; zero-trust is the default at the service boundary.
72
+ 6. Authority for the rules above includes OWASP ASVS sections on authentication and session management, IETF RFC 6749 (OAuth 2.0) and successors for token-based identity, and the platform's current workload-identity documentation. Verify the current versions at audit time.
73
+ <!-- DURABILITY CHECK: Rule relies exclusively on architectural invariants and relative operational thresholds. Valid beyond standard tooling lifecycles. -->
package/AGENTS.md CHANGED
@@ -31,21 +31,21 @@ Keep it short. Do not load every rule just to fill it out.
31
31
  Avoid repeated command output. Do not rerun broad inspections unless edits changed the result. Prefer targeted reads, targeted searches, concise diffs, and final validation gates.
32
32
 
33
33
  ## Layer Index
34
- ### Layer 1: Rules (15 Files) [SCOPE-RESOLVED]
34
+ ### Layer 1: Rules (21 Files) [SCOPE-RESOLVED]
35
35
  Location: `.agent-context/rules/`.
36
36
 
37
37
  Load only relevant rule files. Do not read the entire rule directory by default.
38
38
 
39
- Available rules: `naming-conv.md` (`NAME-*`, v4), `architecture.md` (`ARCH-*`, v4), `security.md` (`SEC-*`, v4), `performance.md` (`PERF-*`, v4), `error-handling.md` (`ERR-*`, v4), `testing.md` (`TEST-*`, v4), `git-workflow.md` (`GIT-*`, v4), `efficiency-vs-hype.md` (`DEP-*`, v4), `api-docs.md` (`API-*`, v4), `microservices.md` (`SVC-*`, v4), `event-driven.md` (`EVT-*`, v4), `database-design.md` (`DATA-*`, v4), `realtime.md` (`RT-*`, v4), `frontend-architecture.md` (`FE-*`, v4), `docker-runtime.md` (`DOCK-*`, v4).
39
+ Available rules: `naming-conv.md` (`NAME-*`, v4), `architecture.md` (`ARCH-*`, v4), `security.md` (`SEC-*`, v4), `performance.md` (`PERF-*`, v4), `error-handling.md` (`ERR-*`, v4), `testing.md` (`TEST-*`, v4), `git-workflow.md` (`GIT-*`, v4), `efficiency-vs-hype.md` (`DEP-*`, v4), `api-docs.md` (`API-*`, v4), `microservices.md` (`SVC-*`, v4), `event-driven.md` (`EVT-*`, v4), `database-design.md` (`DATA-*`, v4), `realtime.md` (`RT-*`, v4), `frontend-architecture.md` (`FE-*`, v4), `docker-runtime.md` (`DOCK-*`, v4), `observability.md` (`OBS-*`, v4), `resilience.md` (`RES-*`, v4), `migrations.md` (`MIG-*`, v4), `background-jobs.md` (`JOB-*`, v4), `config-and-flags.md` (`CFG-*`, v4), `api-versioning.md` (`VER-*`, v4).
40
40
 
41
- For Docker or Compose work, load `docker-runtime.md` and verify the latest official Docker docs before authoring container assets. Also perform live web research for Docker and framework/package setup claims. For framework or package setup work, use the latest stable compatible dependency set and official setup flow unless a documented compatibility constraint blocks it. Prefer official framework scaffolders when they create the supported project shape; manual file assembly needs a repo, prototype, learning, or architecture reason. New dependencies are allowed when they improve efficiency, delivery time, correctness, accessibility, UX, or maintainability. Do not treat dependency avoidance or vague performance fear as a default reason to skip a modern maintained library.
41
+ For Docker or Compose work, load `docker-runtime.md` and verify the latest official Docker docs before authoring container assets. Also perform live web research for Docker and framework/package setup claims. For framework or package setup work, use the latest stable compatible dependency set and official setup flow unless a documented compatibility constraint blocks it; prefer official framework scaffolders when they create the supported project shape. New dependencies are allowed when they improve efficiency, delivery time, correctness, accessibility, UX, or maintainability. Do not treat dependency avoidance or vague performance fear as a default reason to skip a modern maintained library.
42
42
 
43
43
  Backend/API routing:
44
- - Data/schema/persistence: `architecture.md`, `database-design.md`, `performance.md`, `testing.md`.
45
- - Endpoint/API/error contracts: `architecture.md`, `api-docs.md`, `error-handling.md`, `security.md`, `testing.md`.
46
- - Auth/secrets/uploads/permissions: `security.md`, `error-handling.md`, `testing.md`.
47
- - Queue/worker/cron/events/retry: `event-driven.md`, `database-design.md`, `error-handling.md`, `performance.md`, `testing.md`.
48
- - Multi-service/distributed boundaries: `microservices.md`, `event-driven.md`, `database-design.md`, `api-docs.md`, `architecture.md`.
44
+ - Data/schema/persistence: `architecture.md`, `database-design.md`, `migrations.md`, `performance.md`, `testing.md`.
45
+ - Endpoint/API/error contracts: `architecture.md`, `api-docs.md`, `api-versioning.md`, `error-handling.md`, `observability.md`, `security.md`, `testing.md`.
46
+ - Auth/secrets/uploads/permissions: `security.md`, `config-and-flags.md`, `error-handling.md`, `observability.md`, `testing.md`.
47
+ - Queue/worker/cron/events/retry: `event-driven.md`, `background-jobs.md`, `resilience.md`, `database-design.md`, `error-handling.md`, `observability.md`, `performance.md`, `testing.md`.
48
+ - Multi-service/distributed boundaries: `microservices.md`, `event-driven.md`, `database-design.md`, `api-docs.md`, `architecture.md`, `resilience.md`, `observability.md`, `performance.md`.
49
49
 
50
50
  Use the union once when scopes overlap. Do not create framework-specific governance adapters.
51
51
 
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Agentic-Senior-Core
4
4
 
5
- ### Force your AI Agent to code like a Staff Engineer, not a Junior.
5
+ ### Change your AI Agent to code like a Staff Engineer, not a Junior.
6
6
 
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
8
8
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
@@ -10,7 +10,7 @@
10
10
  **Production-grade Rules Engine (Governance Engine) for AI coding agents.**
11
11
  Works with Cursor, Windsurf, GitHub Copilot, Claude Code, Gemini, and other LLM-powered IDE workflows.
12
12
 
13
- Current package version: 4.0.0. Last published version before this release: 3.0.50.
13
+ Current package version: 4.1.0. Last published version before this release: 4.0.3.
14
14
 
15
15
  Highlights:
16
16
  - Uses `AGENTS.md` as the canonical instruction entrypoint.
@@ -22,7 +22,37 @@ Highlights:
22
22
 
23
23
  ---
24
24
 
25
- ## What's New in v4
25
+ ## Why this exists
26
+
27
+ A coding agent that has read every framework tutorial on the internet still ships junior-grade work because nothing in its training tells it which trade-off matters in your codebase. This pack is a small set of plain-language rules an agent loads only when the work scope calls for them: data, endpoints, observability, resilience, migrations, jobs, configuration, versioning, security, design, and a few more. The rules are written as invariants and as bad habits to reject, not as opinions about which framework is fashionable. An agent that reads them treats your repo with the discipline of a senior engineer the first time, instead of after three rounds of review. You install it with one command, you can revert it with a backup, and it does not depend on any particular IDE or LLM provider.
28
+
29
+ ### Long-Term Stability
30
+
31
+ The rules in this pack are written as invariants, outcomes, and freshness criteria, not as named patterns, library prescriptions, or magic-number thresholds. A migration rule says "DDL expected to hold a lock longer than the service's acceptable request-latency threshold must use an online migration mechanism", not "use pgroll for tables larger than ten million rows". A resilience rule says "fail fast when a dependency is unhealthy and shed load before shared resources are exhausted", not "implement a circuit breaker using library X". The intent is that each rule continues to tell a future maintainer the right thing to do across at least three years of framework and tooling churn. Where a rule cites a specific tool name or numeric threshold, the citation block at the bottom of the rule carries a freshness anchor and (in the next release) a `last_validated` date so the next maintainer knows when the technology references were last cross-checked against current practice.
32
+
33
+ ---
34
+
35
+ ## What's New in v4.1
36
+
37
+ This release adds six backend rule files that bring the backend pack to parity with the existing frontend rule. They are technology-neutral by construction and follow the same v4 numbered-Markdown format as the prior fifteen rules.
38
+
39
+ - `observability.md` (`OBS-*`) — observability as structured per-request events; metrics, logs, and traces as derived views; SLO-backed alerting; cardinality, signal-substitution, and audit-stream rules.
40
+ - `resilience.md` (`RES-*`) — explicit timeouts and deadline propagation, idempotency-required retries, dependency isolation, fail-fast as outcome (not named pattern), explicit graceful degradation, observable backpressure.
41
+ - `migrations.md` (`MIG-*`) — expand-contract / parallel-change for live data, deploy-ordering invariant, lock-posture rule keyed to the service's own latency budget, idempotent resumable backfills, mandatory risk fields per migration ticket.
42
+ - `background-jobs.md` (`JOB-*`) — job-shape selection (scheduled vs queued vs stream vs one-shot), per-job ownership and runbook, job-level idempotency, lease/checkpoint/graceful-shutdown for long jobs, poison-message and dead-letter discipline, UTC schedules, jittered fan-out, explicit backpressure.
43
+ - `config-and-flags.md` (`CFG-*`) — configuration sources and startup validation, secret handling, four-way feature-flag taxonomy (release / kill switch / experiment / entitlement) with per-flag owner and expiry, safe defaults on flag-service outage, no-branch-on-environment rule.
44
+ - `api-versioning.md` (`VER-*`) — single versioning strategy per surface, breaking-vs-non-breaking definitions, deprecation discipline (in-band signal via RFC 9745 / RFC 8594 where adopted, plus migration guide and telemetry), explicit support windows, additive evolution as default, CI-blocking compatibility checks.
45
+
46
+ Plus targeted refinements to four existing rules:
47
+
48
+ - `frontend-architecture.md` — FE-012 Data state surface, FE-017 Interactivity priority, FE-018 Internationalization as layout, FE-019 Theme as context.
49
+ - `security.md` — SEC-003 authn vs authz, SEC-004 memory-hard credential storage, SEC-005 cryptographically verifiable service-to-service identity.
50
+ - `database-design.md` — DATA-003 money and time (no floating-point money, UTC for real-world moments), DATA-004 concurrency and write conflicts.
51
+ - `api-docs.md` — API-005 cross-reference to the new versioning rule, API-012 idempotency as a runtime invariant (not just a documentation field).
52
+
53
+ Plus repository hygiene: archive consolidation into `docs/archive/HISTORY.md`, removal of the v3-purge and migrate-rule-format tooling (now under `docs/archive/migrations/`), and inlining of the terminology mapping into this README.
54
+
55
+ ## What's New in v4 (prior)
26
56
 
27
57
  The internal `.agent-context/rules/` pack is now numbered Markdown with YAML frontmatter and stable section IDs (e.g. `FE-004`, `ARCH-009`, `API-006`). This is a breaking change for downstream consumers that parse rule headings; the migration guide lives in `CHANGELOG.md` under `4.0.0`. Repository-wide impact:
28
58
 
@@ -41,7 +71,7 @@ npx @ryuenn3123/agentic-senior-core init
41
71
 
42
72
  One command to initialize `AGENTS.md`, native import bridges, checklists, policies, state files, and the lazy `.agent-context/` rule library for your project.
43
73
 
44
- > **See [docs/doc-index.md](docs/doc-index.md), [docs/deep-dive.md](docs/deep-dive.md), and [docs/roadmap.md](docs/roadmap.md) for deeper CLI, architecture, integration, and roadmap context.**
74
+ > **See [docs/doc-index.md](docs/doc-index.md), [docs/deep-dive.md](docs/deep-dive.md), and [docs/deep-analysis-and-roadmap-backlog.md](docs/deep-analysis-and-roadmap-backlog.md) for deeper CLI, architecture, integration, and roadmap context.**
45
75
 
46
76
  - Default init copies the compact instruction surface and writes onboarding, selected policy, token optimization, and memory continuity state.
47
77
  - MCP workspace files are disabled by default. Add `--mcp-template` when you want starter IDE MCP configuration files.
@@ -92,7 +122,6 @@ If you see `Property $schema is not allowed`, keep `.vscode/mcp.json` without `$
92
122
  | `agentic-senior-core init` | Initialize the compact project guidance pack and native agent entrypoints |
93
123
  | `agentic-senior-core upgrade --dry-run` | Preview managed-surface upgrades |
94
124
  | `agentic-senior-core optimize --show` | Show token optimization state |
95
- | `npm run audit:v3-purge` | Run deep purge readiness audit (no deletion) |
96
125
  | `npm run clean:local` | Remove ignored local reports, backups, benchmarks, and active-memory state |
97
126
  | `agentic-senior-core mcp` | Start local MCP stdio runtime |
98
127
 
@@ -138,7 +167,12 @@ Deprecated legacy files such as `.instructions.md`, `.agent-instructions.md`, `.
138
167
 
139
168
  Rule: on first mention in developer-facing docs, include canonical term in parentheses.
140
169
 
141
- Full mapping reference: docs/terminology-mapping.md
170
+ Examples:
171
+ - `Federated Rules Operations (Federated Governance)`
172
+ - `Rules Engine (Governance Engine)`
173
+ - `quality checks (guardrails)`
174
+
175
+ Compliance boundary: formal policy and audit artifacts must keep canonical terminology for operational traceability.
142
176
 
143
177
  ---
144
178
 
@@ -146,11 +180,10 @@ Full mapping reference: docs/terminology-mapping.md
146
180
 
147
181
  - FAQ: docs/faq.md
148
182
  - Deep dive internals: docs/deep-dive.md
149
- - Archived V2 upgrade playbook: docs/archive/v2-upgrade-playbook.md
183
+ - Project history (phase outcomes, archived playbooks, retired roadmap): docs/archive/HISTORY.md
150
184
  - Integration playbook: docs/integration-playbook.md
151
185
  - Benchmark and stack reference: docs/benchmark-reference.md
152
- - Terminology mapping reference: docs/terminology-mapping.md
153
- - Product roadmap: docs/roadmap.md
186
+ - Active roadmap and backlog: docs/deep-analysis-and-roadmap-backlog.md
154
187
 
155
188
  ---
156
189
 
@@ -15,6 +15,7 @@ import { runMcpServerCommand } from '../lib/cli/commands/mcp.mjs';
15
15
  import { runOptimizeCommand, parseOptimizeArguments } from '../lib/cli/commands/optimize.mjs';
16
16
  import { runInitCommand, parseInitArguments } from '../lib/cli/commands/init.mjs';
17
17
  import { runUpgradeCommand, parseUpgradeArguments } from '../lib/cli/commands/upgrade.mjs';
18
+ import { runDesignAntiRepeatAuditCommand } from '../lib/cli/commands/audit-design-anti-repeat.mjs';
18
19
 
19
20
  async function main() {
20
21
  const commandArgument = process.argv[2];
@@ -63,6 +64,11 @@ async function main() {
63
64
  return;
64
65
  }
65
66
 
67
+ if (commandArgument === 'audit:design-anti-repeat') {
68
+ const auditExitCode = await runDesignAntiRepeatAuditCommand(commandArguments);
69
+ exit(auditExitCode);
70
+ }
71
+
66
72
  console.error(`Unknown command: ${commandArgument}`);
67
73
  printUsage();
68
74
  exit(1);
@@ -0,0 +1,156 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * Color parsing and distance utilities for the typography/palette
5
+ * anti-repeat audit. OKLCH math is contained here so the main audit module
6
+ * stays focused on file scanning and ledger cross-check.
7
+ */
8
+
9
+ export const HEX_COLOR_PATTERN = /#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})\b/g;
10
+ export const OKLCH_PATTERN = /oklch\(\s*([^)]+)\s*\)/gi;
11
+
12
+ export function expandShortHexColor(rawHexColor) {
13
+ if (rawHexColor.length !== 4) {
14
+ return rawHexColor;
15
+ }
16
+ const expandedChars = [];
17
+ for (let charIndex = 1; charIndex < rawHexColor.length; charIndex += 1) {
18
+ expandedChars.push(rawHexColor[charIndex], rawHexColor[charIndex]);
19
+ }
20
+ return `#${expandedChars.join('')}`.toLowerCase();
21
+ }
22
+
23
+ export function normalizeHexColor(rawHexColor) {
24
+ if (typeof rawHexColor !== 'string' || rawHexColor.length === 0) {
25
+ return null;
26
+ }
27
+ const trimmedHex = rawHexColor.trim().toLowerCase();
28
+ if (!trimmedHex.startsWith('#')) {
29
+ return null;
30
+ }
31
+ if (trimmedHex.length === 4) {
32
+ return expandShortHexColor(trimmedHex);
33
+ }
34
+ if (trimmedHex.length === 7 || trimmedHex.length === 9) {
35
+ return trimmedHex.slice(0, 7);
36
+ }
37
+ return null;
38
+ }
39
+
40
+ function parseOklchNumber(rawNumber, percentScale) {
41
+ const numberString = String(rawNumber || '').trim();
42
+ if (numberString.endsWith('%')) {
43
+ const percentValue = parseFloat(numberString.slice(0, -1));
44
+ if (!Number.isFinite(percentValue)) {
45
+ return Number.NaN;
46
+ }
47
+ return (percentValue / 100) * percentScale;
48
+ }
49
+ return parseFloat(numberString);
50
+ }
51
+
52
+ export function parseOklchTriple(oklchExpression) {
53
+ const componentTokens = String(oklchExpression || '')
54
+ .replace(/\//g, ' ')
55
+ .split(/[\s,]+/)
56
+ .filter((componentToken) => componentToken.length > 0);
57
+ if (componentTokens.length < 3) {
58
+ return null;
59
+ }
60
+ const lightness = parseOklchNumber(componentTokens[0], 1);
61
+ const chroma = parseFloat(componentTokens[1]);
62
+ const hueDegrees = parseOklchNumber(componentTokens[2], 360);
63
+ if (!Number.isFinite(lightness) || !Number.isFinite(chroma) || !Number.isFinite(hueDegrees)) {
64
+ return null;
65
+ }
66
+ return { lightness, chroma, hueDegrees };
67
+ }
68
+
69
+ /**
70
+ * L*C*H-style perceptual distance proxy. OKLCH is perceptually uniform,
71
+ * so a Cartesian-like delta over (L, C, C * Δhue_radians) tracks visible
72
+ * difference well enough for an anti-repeat heuristic.
73
+ */
74
+ export function oklchPerceptualDistance(leftColor, rightColor) {
75
+ const lightnessDelta = leftColor.lightness - rightColor.lightness;
76
+ const chromaDelta = leftColor.chroma - rightColor.chroma;
77
+ const hueDelta = ((leftColor.hueDegrees - rightColor.hueDegrees + 540) % 360) - 180;
78
+ const hueArcRadians = (hueDelta * Math.PI) / 180;
79
+ const hueChromaDelta = leftColor.chroma * hueArcRadians;
80
+ return Math.sqrt(
81
+ lightnessDelta * lightnessDelta
82
+ + chromaDelta * chromaDelta
83
+ + hueChromaDelta * hueChromaDelta,
84
+ );
85
+ }
86
+
87
+ export function extractColorOccurrencesFromText(sourceText) {
88
+ /** @type {{ kind: 'hex' | 'oklch', raw: string, normalized: string | null, oklch: { lightness: number, chroma: number, hueDegrees: number } | null, index: number }[]} */
89
+ const colorOccurrences = [];
90
+
91
+ HEX_COLOR_PATTERN.lastIndex = 0;
92
+ let hexMatch;
93
+ // eslint-disable-next-line no-cond-assign
94
+ while ((hexMatch = HEX_COLOR_PATTERN.exec(sourceText)) !== null) {
95
+ colorOccurrences.push({
96
+ kind: 'hex',
97
+ raw: hexMatch[0],
98
+ normalized: normalizeHexColor(hexMatch[0]),
99
+ oklch: null,
100
+ index: hexMatch.index,
101
+ });
102
+ }
103
+
104
+ OKLCH_PATTERN.lastIndex = 0;
105
+ let oklchMatch;
106
+ // eslint-disable-next-line no-cond-assign
107
+ while ((oklchMatch = OKLCH_PATTERN.exec(sourceText)) !== null) {
108
+ colorOccurrences.push({
109
+ kind: 'oklch',
110
+ raw: oklchMatch[0],
111
+ normalized: oklchMatch[0].toLowerCase(),
112
+ oklch: parseOklchTriple(oklchMatch[1]),
113
+ index: oklchMatch.index,
114
+ });
115
+ }
116
+
117
+ return colorOccurrences;
118
+ }
119
+
120
+ export function extractLedgerColors(antiRepeatLedger) {
121
+ const previousPalettes = Array.isArray(antiRepeatLedger?.previousPalettes)
122
+ ? antiRepeatLedger.previousPalettes
123
+ : [];
124
+
125
+ /** @type {{ kind: 'hex' | 'oklch', normalized: string | null, oklch: { lightness: number, chroma: number, hueDegrees: number } | null, sourceEntry: any }[]} */
126
+ const ledgerColors = [];
127
+ for (const ledgerEntry of previousPalettes) {
128
+ const summaryString = String(ledgerEntry?.summary || '');
129
+ if (!summaryString) {
130
+ continue;
131
+ }
132
+ HEX_COLOR_PATTERN.lastIndex = 0;
133
+ let hexMatch;
134
+ // eslint-disable-next-line no-cond-assign
135
+ while ((hexMatch = HEX_COLOR_PATTERN.exec(summaryString)) !== null) {
136
+ ledgerColors.push({
137
+ kind: 'hex',
138
+ normalized: normalizeHexColor(hexMatch[0]),
139
+ oklch: null,
140
+ sourceEntry: ledgerEntry,
141
+ });
142
+ }
143
+ OKLCH_PATTERN.lastIndex = 0;
144
+ let oklchMatch;
145
+ // eslint-disable-next-line no-cond-assign
146
+ while ((oklchMatch = OKLCH_PATTERN.exec(summaryString)) !== null) {
147
+ ledgerColors.push({
148
+ kind: 'oklch',
149
+ normalized: oklchMatch[0].toLowerCase(),
150
+ oklch: parseOklchTriple(oklchMatch[1]),
151
+ sourceEntry: ledgerEntry,
152
+ });
153
+ }
154
+ }
155
+ return ledgerColors;
156
+ }
@@ -0,0 +1,103 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * Filesystem scanning for the typography/palette anti-repeat audit. Walks
5
+ * the repository tree, skips noisy directories (node_modules, build outputs,
6
+ * etc.), and yields scannable CSS-like and token-config file paths.
7
+ */
8
+
9
+ import { readdirSync, statSync, existsSync } from 'node:fs';
10
+ import { extname, join, relative, sep } from 'node:path';
11
+
12
+ export const DEFAULT_CSS_FILE_EXTENSIONS = new Set(['.css', '.scss', '.sass', '.less']);
13
+ export const TOKEN_CONFIG_FILE_NAMES = new Set([
14
+ 'tailwind.config.js',
15
+ 'tailwind.config.cjs',
16
+ 'tailwind.config.mjs',
17
+ 'tailwind.config.ts',
18
+ 'theme.config.js',
19
+ 'theme.config.ts',
20
+ 'design-tokens.js',
21
+ 'design-tokens.ts',
22
+ ]);
23
+ export const SCAN_SKIP_DIRECTORY_NAMES = new Set([
24
+ 'node_modules',
25
+ '.git',
26
+ '.agentic-backup',
27
+ '.benchmarks',
28
+ 'dist',
29
+ 'build',
30
+ '.next',
31
+ 'out',
32
+ 'coverage',
33
+ '.cache',
34
+ ]);
35
+
36
+ function isInsideSkippedDirectory(absolutePath, repositoryRootPath) {
37
+ const relativePath = relative(repositoryRootPath, absolutePath);
38
+ if (!relativePath || relativePath.startsWith('..')) {
39
+ return true;
40
+ }
41
+ const pathSegments = relativePath.split(sep);
42
+ return pathSegments.some((segmentName) => SCAN_SKIP_DIRECTORY_NAMES.has(segmentName));
43
+ }
44
+
45
+ function* walkDirectoryEntries(currentDirectoryPath, repositoryRootPath) {
46
+ const directoryEntries = readdirSync(currentDirectoryPath, { withFileTypes: true });
47
+ for (const directoryEntry of directoryEntries) {
48
+ const childPath = join(currentDirectoryPath, directoryEntry.name);
49
+ if (directoryEntry.isDirectory()) {
50
+ if (SCAN_SKIP_DIRECTORY_NAMES.has(directoryEntry.name)) {
51
+ continue;
52
+ }
53
+ if (isInsideSkippedDirectory(childPath, repositoryRootPath)) {
54
+ continue;
55
+ }
56
+ yield* walkDirectoryEntries(childPath, repositoryRootPath);
57
+ continue;
58
+ }
59
+ if (directoryEntry.isFile()) {
60
+ yield childPath;
61
+ }
62
+ }
63
+ }
64
+
65
+ export function isScannableFile(absoluteFilePath) {
66
+ const fileExtension = extname(absoluteFilePath).toLowerCase();
67
+ if (DEFAULT_CSS_FILE_EXTENSIONS.has(fileExtension)) {
68
+ return true;
69
+ }
70
+ const baseName = absoluteFilePath.split(/[\\/]/).pop();
71
+ return TOKEN_CONFIG_FILE_NAMES.has(baseName);
72
+ }
73
+
74
+ export function collectScannableFilePaths(repositoryRootPath, scanRoots) {
75
+ const collectedFilePaths = [];
76
+ for (const scanRoot of scanRoots) {
77
+ const absoluteScanRoot = join(repositoryRootPath, scanRoot);
78
+ if (!existsSync(absoluteScanRoot)) {
79
+ continue;
80
+ }
81
+ const scanRootStat = statSync(absoluteScanRoot);
82
+ if (!scanRootStat.isDirectory()) {
83
+ continue;
84
+ }
85
+ for (const candidateFilePath of walkDirectoryEntries(absoluteScanRoot, repositoryRootPath)) {
86
+ if (!isScannableFile(candidateFilePath)) {
87
+ continue;
88
+ }
89
+ collectedFilePaths.push(candidateFilePath);
90
+ }
91
+ }
92
+ return collectedFilePaths.sort();
93
+ }
94
+
95
+ export function lineNumberFromIndex(sourceText, charIndex) {
96
+ let lineNumber = 1;
97
+ for (let scanIndex = 0; scanIndex < charIndex && scanIndex < sourceText.length; scanIndex += 1) {
98
+ if (sourceText[scanIndex] === '\n') {
99
+ lineNumber += 1;
100
+ }
101
+ }
102
+ return lineNumber;
103
+ }
@@ -0,0 +1,70 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * Font-family parsing for the typography/palette anti-repeat audit. CSS
5
+ * `font-family:` declarations and `@font-face` blocks are the inputs;
6
+ * normalized lowercase family tokens are the outputs.
7
+ */
8
+
9
+ export const FONT_FAMILY_DECLARATION_PATTERN = /font-family\s*:\s*([^;}]+)[;}]/gi;
10
+ export const FONT_FACE_FAMILY_PATTERN = /@font-face\s*\{[^}]*font-family\s*:\s*([^;]+);/gi;
11
+
12
+ export function normalizeFontFamilyToken(rawFamilyToken) {
13
+ return String(rawFamilyToken || '')
14
+ .replace(/^["']|["']$/g, '')
15
+ .trim()
16
+ .toLowerCase();
17
+ }
18
+
19
+ export function splitFontFamilyDeclaration(declarationValue) {
20
+ return String(declarationValue || '')
21
+ .split(',')
22
+ .map((familyEntry) => normalizeFontFamilyToken(familyEntry))
23
+ .filter((familyEntry) => familyEntry.length > 0 && !familyEntry.startsWith('var('));
24
+ }
25
+
26
+ export function extractFontFamiliesFromText(sourceText) {
27
+ /** @type {{ family: string, index: number }[]} */
28
+ const familyOccurrences = [];
29
+
30
+ for (const matchPattern of [FONT_FAMILY_DECLARATION_PATTERN, FONT_FACE_FAMILY_PATTERN]) {
31
+ matchPattern.lastIndex = 0;
32
+ let regexMatch;
33
+ // eslint-disable-next-line no-cond-assign
34
+ while ((regexMatch = matchPattern.exec(sourceText)) !== null) {
35
+ const declarationValue = regexMatch[1];
36
+ const declarationStartIndex = regexMatch.index;
37
+ for (const familyEntry of splitFontFamilyDeclaration(declarationValue)) {
38
+ familyOccurrences.push({ family: familyEntry, index: declarationStartIndex });
39
+ }
40
+ }
41
+ }
42
+
43
+ return familyOccurrences;
44
+ }
45
+
46
+ export function extractLedgerTypographyFamilies(antiRepeatLedger) {
47
+ const previousTypographyChoices = Array.isArray(antiRepeatLedger?.previousTypographyChoices)
48
+ ? antiRepeatLedger.previousTypographyChoices
49
+ : [];
50
+
51
+ const ledgerFamilies = new Map();
52
+ for (const ledgerEntry of previousTypographyChoices) {
53
+ const summaryString = String(ledgerEntry?.summary || '');
54
+ if (!summaryString) {
55
+ continue;
56
+ }
57
+ // Summaries are emitted as "role: value; role: value". Each value is a
58
+ // font family that the previous design shipped.
59
+ for (const summaryPart of summaryString.split(';')) {
60
+ const colonSplitIndex = summaryPart.indexOf(':');
61
+ const familyValue = colonSplitIndex >= 0 ? summaryPart.slice(colonSplitIndex + 1) : summaryPart;
62
+ const normalizedFamilyValue = normalizeFontFamilyToken(familyValue);
63
+ if (normalizedFamilyValue.length === 0) {
64
+ continue;
65
+ }
66
+ ledgerFamilies.set(normalizedFamilyValue, ledgerEntry);
67
+ }
68
+ }
69
+ return ledgerFamilies;
70
+ }