diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 52ca379..88e466b 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -49,6 +49,62 @@ Think about what didn't help — don't write that. Write what you know that the code doesn't say. + +## Prompt Instructions + +Your prompt declares which patterns and phases are active. This section explains what they mean. + +### Stage approval + +Stage only the files you modified. Use explicit `git add` paths — never `git add .` or `git add -A`. Do not commit. Propose a short commit message for the supervisor. The commit is the supervisor's approval of the work. + +### Preflight + +Verify the repo is in a clean state before starting. Run the preflight script, confirm the branch and working tree. If the prompt includes a branch name, create it from `origin/main`. + +### Red + +Write failing tests against stub implementations. The stub must compile but not pass the tests — that is the goal. Do not implement anything beyond the stub. The tests are the contract for the next phase. + +### Green + +Implement to make the red tests pass. Do not modify tests unless absolutely necessary — if you do, document what changed and why in your testament. If you need to run a formatter or fixer, scope it to the files you changed. + +### Code + +General implementation phase. Same discipline as Green but without a test contract. Follow the prompt's instructions prescriptively. + +### Ship + +Load the ship agent. Distil the testament — rewrite it for whoever comes after, not as a log of what you did. Then open the PR. + +Read your testament. Read previous testaments. Think about what helped you, what didn't. This is an opportunity to rewrite your testament — the testament is by you, for you, no one else will read it. + +### Investigation + +Explore the codebase and write a findings report. Trace how things actually work — data flow, ownership, what calls what. Present what you found, not what you think should change. Do not recommend — the SC decides direction. + +### System Design + +This is not code design. Do not produce classes, methods, or type signatures — that is class design. Think about the system: who owns the data, how does it flow, where are the boundaries, how does control move between components. If the user will see it, account for how it reaches the screen. + +Each design must be complete. If data reaches the user, account for how it gets to the screen. If state changes mid-session, account for that. A design that defers a critical path is not a design — it is a sketch that will collapse when the deferred part becomes the task. + +Produce two or three distinct options that differ in ownership, boundaries, or data flow — not variations on the same code structure. State the trade-offs for each. No recommendation — the SC decides direction. + +### Class Design + +The system-level direction has already been decided. Now produce the blueprint: interfaces, type signatures, method signatures, how new classes wire into existing code. Match existing codebase patterns — read what's there before designing anything new. The implementation phases build exactly what you specify here. + +### Codebase Discovery + +Verify assumptions and fill in implementation detail. Your findings feed the next phase via your testament, not a separate file. + +### Code Review + +Review the implementation for quality. You have full access to the codebase. Read the diff, the prompt, and the surrounding code. Report what you find. + + ## Current State diff --git a/.claude/testament/2026-04-14.md b/.claude/testament/2026-04-14.md new file mode 100644 index 0000000..fce5874 --- /dev/null +++ b/.claude/testament/2026-04-14.md @@ -0,0 +1,21 @@ +# 05:30 + +## Remove @abraham/reflection from core-di + +### Not just metadata.ts + +The prompt says to update `metadata.ts`. Three files actually needed touching: + +1. `src/private/metadata.ts` - the WeakMap replacement +2. `inject.ts` at the package root - this was the tsup inject target (prepended to every bundle). It imported `@abraham/reflection` to ensure the polyfill loaded before any bundle code. After the dependency was removed, this file needed its import cleared. The file still exists and `tsconfig.json` still references it as the inject entry; leaving it empty is correct and harmless. +3. `package.json` / `pnpm-lock.yaml` - via `pnpm remove` + +Missing the `inject.ts` change would have left a dead import and a broken build. + +### WeakMap semantics are equivalent here + +`@abraham/reflection` `getMetadata` walked the prototype chain (`ordinaryGetMetadata`). The WeakMap replacement does not. This is fine: both callers (`dependsOn.ts` and `ServiceProvider.ts`) always call with the class constructor directly, never with an instance or subclass. The prototype walk was never exercised by this codebase. + +### Where metadata flows + +`dependsOn.ts` writes when a `@dependsOn` decorator fires. `ServiceProvider.ts` reads when resolving dependencies. The key is always `'design:dependencies'`, the value is `Record>`. Nothing else in the package touches the metadata API. diff --git a/packages/core-di/changes.jsonl b/packages/core-di/changes.jsonl index 0e4d0e0..0edbc77 100644 --- a/packages/core-di/changes.jsonl +++ b/packages/core-di/changes.jsonl @@ -62,3 +62,5 @@ {"description":"Fixed GHSA-mw96-cpmx-2vgc: rollup arbitrary file write via path traversal.","category":"security","metadata":{"ghsa":"GHSA-mw96-cpmx-2vgc"}} {"description":"Updated all dependencies to latest versions.","category":"changed"} {"type":"release","version":"3.1.6","date":"2026-02-28","tag":"3.1.6"} +{"description":"Replace @abraham/reflection with a global WeakMap for metadata storage","category":"changed"} +{"description":"Remove @abraham/reflection dependency","category":"removed"} diff --git a/packages/core-di/inject.ts b/packages/core-di/inject.ts deleted file mode 100644 index 8eb5cd1..0000000 --- a/packages/core-di/inject.ts +++ /dev/null @@ -1 +0,0 @@ -import '@abraham/reflection'; diff --git a/packages/core-di/package.json b/packages/core-di/package.json index 4eeb6bb..1d59ce7 100644 --- a/packages/core-di/package.json +++ b/packages/core-di/package.json @@ -49,7 +49,6 @@ "watch": "tsup --watch" }, "devDependencies": { - "@abraham/reflection": "^0.13.0", "@js-joda/core": "^5.7.0", "@shellicar/typescript-config": "workspace:^", "@types/node": "^25.3.2", diff --git a/packages/core-di/src/private/metadata.ts b/packages/core-di/src/private/metadata.ts index 75a0e6a..96fbe9f 100644 --- a/packages/core-di/src/private/metadata.ts +++ b/packages/core-di/src/private/metadata.ts @@ -1,5 +1,16 @@ -import '@abraham/reflection'; import type { MetadataType, SourceType } from '../types'; -export const getMetadata = (key: string, obj: object): MetadataType | undefined => Reflect.getMetadata(key, obj); -export const defineMetadata = (key: string, metadata: MetadataType, obj: object) => Reflect.defineMetadata(key, metadata, obj); +const store = new WeakMap>(); + +export const getMetadata = (key: string, obj: object): MetadataType | undefined => { + return store.get(obj)?.get(key) as MetadataType | undefined; +}; + +export const defineMetadata = (key: string, metadata: MetadataType, obj: object) => { + let inner = store.get(obj); + if (inner === undefined) { + inner = new Map(); + store.set(obj, inner); + } + inner.set(key, metadata); +}; diff --git a/packages/core-di/tsup.config.ts b/packages/core-di/tsup.config.ts index 5c15603..9333113 100644 --- a/packages/core-di/tsup.config.ts +++ b/packages/core-di/tsup.config.ts @@ -10,7 +10,6 @@ const commonOptions = (config: Options) => options.chunkNames = 'chunks/[name]-[hash]'; options.entryNames = '[name]'; }, - inject: ['inject.ts'], keepNames: true, minify: false, platform: 'node', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index edeab0d..0840e1a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1162,9 +1162,6 @@ importers: packages/core-di: devDependencies: - '@abraham/reflection': - specifier: ^0.13.0 - version: 0.13.0 '@js-joda/core': specifier: ^5.7.0 version: 5.7.0 @@ -1363,9 +1360,6 @@ importers: packages: - '@abraham/reflection@0.13.0': - resolution: {integrity: sha512-sQYhhraGPPRP6zoqpqpSaoYL+XY5KNmQCcoB3IuQh1z1aWmkHXcxwyTxRkTJ60L2aUjrbiqIqoYkrB/1c7kjQA==} - '@ardatan/relay-compiler@13.0.1': resolution: {integrity: sha512-afG3YPwuSA0E5foouZusz5GlXKs74dObv4cuWyLyfKsYFj2r7oGRNB28v18HvwuLSQtQFCi+DpIe0TZkgQDYyg==} peerDependencies: @@ -7096,8 +7090,6 @@ packages: snapshots: - '@abraham/reflection@0.13.0': {} - '@ardatan/relay-compiler@13.0.1(graphql@16.13.2)': dependencies: '@babel/runtime': 7.29.2