i used to make IGRIS better by opening it up and changing whatever sat between the new idea and the running system. a command needed a route. an agent needed a list. a new tool needed another harness config. the change worked, but it left another place that had to be remembered forever.
it was like adding a socket by rewiring the building. possible, occasionally necessary, and a bad default.
so i gave the OS declared extension points. the useful part is not that everything became “one file” or that wiring disappeared. neither is true. the useful part is that each kind of addition now has a named door, a contract, and a check.
01 / THE CORRECTIONThe rule got too neat
the first version of this note had a cleaner line: declare the new thing, discovery finds it, done.
that line compressed several different mechanisms into one. skills, agents, MCP servers, and hooks go through a CLI-managed projection path. OS modules are found from frontmatter and compiled into an index. project document types have their own catalog generator. cognition extractors join an open registry through a barrel. a new harness centres on a descriptor, but can still require adapter work when its native format or read-path is new.
those are not implementation details around one universal plugin system. they are separate extension contracts for separate layers.
what stayed true is the architectural rule: i should not invent a private route every time i add something. i identify what kind of thing it is, then use the extension point that owns that kind.
02 / SURFACESFour surfaces use the front door
for user-facing surfaces, the front door is explicit:
1igris add <skill|agent|mcp|hook> <name>that command does more than write a declaration. it materializes the surface, projects it into the native files or configuration of the harnesses that support it, then checks the result for drift. personal additions live in the loadout overlay. core additions write the IGRIS source, mirror the runtime copy, project it, and verify it.
projection is capability-shaped, not universal. skills and MCP are distributed across the declared harness roster. agent files go only to harnesses whose descriptor exposes a static agent surface. hooks go only where the descriptor says hooks are supported. an unsupported surface is supposed to be absent on purpose, not quietly missed.
this matters because “write once” does not mean “land identically everywhere.” a skill may become a symlink in one harness and a wrapper in another. an MCP server may be merged into JSON here and TOML there. a hook is a configuration merge, not a copied markdown file. igris add owns those differences and fails when an add reaches zero targets, which is much more useful than a cheerful no-op.
igris add is the common front door for four material surfaces. it is not the extension mechanism for every part of the OS.03 / DISCOVERYDiscovery belongs to the parts that describe themselves
OS modules use a different path. a module in core/os/ declares its layer, load tier, scope, summary, and when it should be consulted. the index generator scans that frontmatter and rebuilds the model-facing map. the generated index is not another registry to maintain; it is an output.
project context document types follow the same principle in their own subsystem. a type declaration names its target file and states when the document applies, when an agent should consult it, and what kind of change makes it stale. a generator turns those declarations into the catalog used by the grounding and promotion workflows.
both are genuinely discovered. neither means “drop any file anywhere.” the location and metadata schema are part of the contract, and the generated index still has to be rebuilt. declaration removes scattered registration edits. it does not remove validation, generation, or ownership.
this is the narrower version of self-description that i trust: a part carries the facts needed to place it, and one subsystem-specific reader turns those facts into an index its consumers can use.
04 / COGNITIONThe host stays open; the bundle keeps one list
the cognition layer is close to the original story, but it has one honest seam.
an extractor implements the cognition instance contract: its identity, configuration, context builder, prompt builder, response parser, persistence function, and input accounting. the registry is an open map, so the engine iterates instances instead of branching on names such as perception, subconscious, or synapse.
adding an extractor does not require an engine edit. it does require the extractor file and an entry in the extractors barrel. that barrel exists because the engine ships as a bundle; a runtime filesystem glob is not the deployment shape. discovery reads the exported instance list, registers each instance, and hands the same host to all of them.
that distinction is small in code and large in an article. “zero host change” is evidenced. “zero registration” is not. the extensibility test for synapse checks both sides: synapse appears in discovery, and neither the engine nor registry contains a synapse-specific branch.
05 / HARNESSESA new harness is a descriptor plus whatever reality demands
harness onboarding is the least honest place to promise “one file, no wiring.” the descriptor is the centre of the design, not the entire job.
it records the harness id used by distribution tools, whether static agent files exist, how MCP configuration is shaped, how permissions are granted, whether hooks are supported, and which delegation model applies. the compiler and drift checker derive per-surface participation from those fields instead of consulting separate hand-kept harness lists.
then reality gets a vote. if the harness uses a wire format IGRIS already knows, the descriptor reuses an existing emitter. if its agent or MCP format is genuinely new, a thin emitter is added. agent and hook read-paths are proved with marker files rather than inferred from a familiar directory name. dynamic delegation gets a harness-specific context adapter; a harness with no subagent mechanism can execute the role inline instead.
there are still two hand-kept type unions for a new harness id. there can be operator-gated permission work. onboarding is only done after compile, drift checks, parity checks, and throwaway additions prove that future skills, agents, MCP servers, and hooks reach the surfaces the harness actually exposes.
that last phrase is the contract. Claude, Codex, Gemini, OpenCode, Antigravity, and Cursor do not expose equal surfaces. the manifest says so. static agents are absent where they are absent. blocking gates soften where projected hooks do not exist. shared behavior is the goal; fictional parity is not.
06 / THE RULEDeclare the difference, then verify the path
“declaration, not editing” was never supposed to mean that code stops changing. a new subsystem may span several layers. a new wire format needs an adapter. a contract change still requires a consumer sweep. the repository itself keeps a map for that maintenance work.
what declaration changes is where the decision lives. the new part states what it is. the owning subsystem decides how that kind is discovered, projected, generated, or adapted. verification proves that the path did what the declaration claimed.
so the rule i use now is less magical and more useful:
- if it is a skill, agent, MCP server, or hook, add it through the surface command.
- if it is an OS module or document type, give it the required metadata and regenerate its catalog.
- if it is a cognition extractor, fill the instance contract and register it through the barrel without teaching the host its name.
- if it is a harness, describe its capabilities, add only the adapters its real shape requires, and prove every supported path.
- if it crosses layers, stop pretending it is one extension and decompose it.
IGRIS grows by making the extension point explicit, not by pretending every extension is the same. the narrow door is still the point. now the label on each door is accurate.
End of file. Filed 2026.09.03 from the current self-extension contracts.