ワンクリックで
di
Dependency Injection (DI) skill for the ikigai project
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Dependency Injection (DI) skill for the ikigai project
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Automated quality check loops with escalation and fix sub-agents
JSON-based end-to-end test format, runner, and mock provider
Jujutsu (jj) skill for the ikigai project
How to write effective Ralph goals for Ikigai-driven workflows
Create and manage Ralph goals from Ikigai using the real ralph-pipeline scripts
Create repositories using the real ralph-pipeline repo-create script
| name | di |
| description | Dependency Injection (DI) skill for the ikigai project |
Dependency Injection - Pass dependencies to a module from the outside rather than having the module create or locate them internally.
Instead of:
void repl_init() {
config = load_config(); // Hidden dependency - where does it load from?
db = connect_database(); // Hidden dependency - what connection string?
llm = create_llm_client(); // Hidden dependency - which API key?
}
Do this:
void repl_init(config_t *cfg, db_t *db, llm_t *llm) {
// Dependencies are explicit - passed from caller
// This function doesn't know or care where they came from
}
Explicit Dependencies - Function signature reveals what it needs. No hidden file I/O, global state, or magic.
Testability - Pass mock/fake implementations in tests. Real implementations in production.
Composability - Modules become building blocks. Wire them together differently for different contexts.
No Global State - Eliminates hidden coupling. Multiple instances can coexist with different configs.
Clear Ownership - Caller controls lifecycle. Knows when dependencies are created and destroyed.
Main creates all dependencies, passes them down:
// From v1-architecture.md
int main(void) {
void *root_ctx = talloc_new(NULL);
// Load config
ik_cfg_t *cfg = NULL;
TRY(ik_cfg_load(root_ctx, "~/.ikigai/config.json", &cfg));
// Initialize database with config
ik_db_ctx_t *db = NULL;
TRY(ik_db_init(root_ctx, cfg->db_connection_string, &db));
// Initialize LLM client with config
ik_llm_client_t *llm = NULL;
TRY(ik_llm_init(root_ctx, cfg->openai_api_key, &llm));
// Initialize REPL - receives all dependencies
ik_repl_ctx_t *repl = NULL;
TRY(ik_repl_init(root_ctx, cfg, db, llm, &repl));
// Run
ik_repl_run(repl);
talloc_free(root_ctx);
return 0;
}
Key insight: main() is the composition root. It knows the full dependency graph. Everything else just receives what it needs.
First parameter is always the talloc context:
// Config loading
res_t ik_cfg_load(TALLOC_CTX *ctx, const char *path, ik_cfg_t **out_cfg);
// Database init
res_t ik_db_init(TALLOC_CTX *ctx, const char *conn_str, ik_db_ctx_t **out_db);
// REPL init
res_t ik_repl_init(TALLOC_CTX *ctx, ik_cfg_t *cfg, ik_db_ctx_t *db,
ik_llm_client_t *llm, ik_repl_ctx_t **out_repl);
Benefits:
REPL context holds injected dependencies:
struct ik_repl_ctx_t {
ik_cfg_t *cfg; // Injected config
ik_db_ctx_t *db; // Injected database
ik_llm_client_t *llm; // Injected LLM client
ik_term_ctx_t *term; // Created internally
ik_scrollback_t *scrollback;// Created internally
ik_input_buffer_t *input; // Created internally
// ... other fields
};
Two types of dependencies:
// production main.c
int main(void) {
ik_llm_client_t *llm = ik_llm_init(ctx, real_api_key);
ik_repl_init(ctx, cfg, db, llm, &repl);
// Uses real OpenAI API
}
// test_repl.c
void test_repl_handles_llm_error(void) {
ik_llm_client_t *mock_llm = create_mock_llm_that_fails();
ik_repl_init(ctx, cfg, db, mock_llm, &repl);
// Uses mock that returns errors - no real API calls
}
Key: Same ik_repl_init() function works with real or mock dependencies.
Service Locator - Global registry of dependencies:
// BAD - hidden dependency
void repl_init() {
llm_client_t *llm = service_locator_get("llm"); // Where did this come from?
}
Singleton - Global static instance:
// BAD - global state
static config_t *g_config = NULL;
void repl_init() {
const char *key = g_config->api_key; // Hidden dependency on global
}
Constructor Does I/O - Function creates its own dependencies:
// BAD - hidden file I/O
void repl_init() {
config = load_config_from_disk(); // Which file? Can't test without filesystem
}
Null Checking Injected Deps - Don't do this:
// BAD - dependency is required, why allow NULL?
void repl_init(config_t *cfg, db_t *db, llm_t *llm) {
if (!cfg || !db || !llm) {
PANIC("Missing dependencies"); // If they're required, assert it
}
}
Use assertions for required dependencies:
// GOOD - document requirements
void repl_init(config_t *cfg, db_t *db, llm_t *llm) {
assert(cfg != NULL);
assert(db != NULL);
assert(llm != NULL);
// Proceed knowing dependencies exist
}
No interfaces - C doesn't have interfaces. Use function pointers in structs for polymorphism:
typedef struct {
res_t (*send)(void *ctx, message_t *msg);
res_t (*stream)(void *ctx, message_t *msg, chunk_callback_t cb);
} llm_provider_vtable_t;
typedef struct {
llm_provider_vtable_t *vtable;
void *impl_ctx; // OpenAI context, Anthropic context, etc.
} llm_client_t;
No frameworks - No Spring, no Guice. Composition happens in main(). Simple and explicit.
Manual wiring - You write the composition logic. More code, but totally transparent.
Dependency Injection = Pass dependencies in, don't create them internally.
Benefits:
In ikigai: main() creates config, database, LLM client. Passes them to REPL. REPL doesn't know where they came from. Easy to test with mocks. No hidden file I/O or global state.