Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,62 @@ Think about what didn't help — don't write that.
Write what you know that the code doesn't say.
<!-- END:TEMPLATE:testament -->

<!-- BEGIN:TEMPLATE:instructions -->
## 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.
<!-- END:TEMPLATE:instructions -->

<!-- BEGIN:REPO:current-state -->
## Current State

Expand Down
21 changes: 21 additions & 0 deletions .claude/testament/2026-04-14.md
Original file line number Diff line number Diff line change
@@ -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<string | symbol, ServiceIdentifier<T>>`. Nothing else in the package touches the metadata API.
2 changes: 2 additions & 0 deletions packages/core-di/changes.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
1 change: 0 additions & 1 deletion packages/core-di/inject.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/core-di/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
17 changes: 14 additions & 3 deletions packages/core-di/src/private/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import '@abraham/reflection';
import type { MetadataType, SourceType } from '../types';

export const getMetadata = <T extends SourceType>(key: string, obj: object): MetadataType<T> | undefined => Reflect.getMetadata(key, obj);
export const defineMetadata = <T extends SourceType>(key: string, metadata: MetadataType<T>, obj: object) => Reflect.defineMetadata(key, metadata, obj);
const store = new WeakMap<object, Map<string, unknown>>();

export const getMetadata = <T extends SourceType>(key: string, obj: object): MetadataType<T> | undefined => {
return store.get(obj)?.get(key) as MetadataType<T> | undefined;
};

export const defineMetadata = <T extends SourceType>(key: string, metadata: MetadataType<T>, obj: object) => {
let inner = store.get(obj);
if (inner === undefined) {
inner = new Map<string, unknown>();
store.set(obj, inner);
}
inner.set(key, metadata);
};
1 change: 0 additions & 1 deletion packages/core-di/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
8 changes: 0 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading