Skip to content

Instantly share code, notes, and snippets.

@r17x
Last active August 22, 2026 00:05
Show Gist options
  • Select an option

  • Save r17x/90eb2f7be93932b5693753aedb09c01a to your computer and use it in GitHub Desktop.

Select an option

Save r17x/90eb2f7be93932b5693753aedb09c01a to your computer and use it in GitHub Desktop.

Design Thinking

X → Graph → Effect<A, E, R>
│              │   │  │  │
│              │   │  │  └─ what each node needs     (§5)
│              │   │  └──── where the graph breaks   (§4)
│              │   └─────── what flows through nodes  (§2)
│              │
│              └─ nodes = functions, edges = data flow
│
└─ the problem: what you’re trying to build

§1  Shapes       the nouns: records, IDs, variants, errors
§2  A            happy path as call graph
§3  Cardinality  one-shot (Effect) or many (Stream)
§4  E            break points: retry, escape, die
§5  R            dependencies: compile-time proof
§6  Boundary     Schema: unknown → trusted at edges
§7  Behavior     .pipe() wraps without changing the graph
§8  Scope        acquire/release tied to lifecycle
§9  Test         swap R, same graph shape
§10 Code         gen body = A, .pipe() = E

Read the problem. Draw the data flow as a call graph. Write code that IS the graph. If the code doesn't match the graph, something is wrong.

1. Name the shapes

What are the things? Before drawing a graph, define the domain language.

  • Records -- entities that flow through nodes. A User, a Product, an Order.
  • IDs -- identity of things. Branded, constrained, never a bare string.
  • Variants -- internal state transitions. A step is Continue or Finished. A status is Pending, Active, or Cancelled.
  • Errors -- named failure modes. Not strings. Tagged, structured, carrying context.

These are the nouns. The graph is the verbs. You cannot draw the graph until you know what flows through it.

2. Think A first

Map the happy path as a call graph before writing any code.

F1(A) -> F2(A) -> F3(A)

What is the success data flow? What goes in, what comes out, what transforms happen in between? This graph IS your program structure. Draw it first. The code follows.

3. One or many?

Is each node one-shot or a flow?

  • One value -- the node runs, produces A, done. This is Effect.
  • Many values over time -- the node emits A repeatedly. Events, subscriptions, paginated pulls. This is Stream.
  • Time-bounded -- the result is valid for a window. Cache it. Deduplicate concurrent lookups.

Same three channels (A, E, R) in all cases. Different cardinality. Mark it on the graph so the code matches.

4. Think E second

Mark where the graph can break. Each break point is one of three things:

  • Retry -- transient failure, try again. Network timeout, rate limit, connection reset.
  • Escape Hatch -- recoverable, return an alternative. Fallback value, cached result, default.
  • Die -- defect, invariant violation, programmer bug. NOT a domain error. Something is fundamentally wrong.

Errors are VALUES in the E channel until you truly cannot handle them. They flow through the graph like data. You decide at each node: retry, escape, or let it propagate. Only die when the program's assumptions are violated.

5. Think R third

Mark what each node in the graph needs to exist. "We cannot do X if we don't have Y."

R is compile-time proof that dependencies are satisfied. Every node declares what it requires -- a database connection, a config value, an HTTP client. R shrinks as you provide layers. When R is empty (never), the program can run. If R is not empty, the compiler tells you exactly what's missing.

6. Trust at boundary

Where does untrusted data enter the graph? HTTP requests, file reads, environment variables, user input, third-party API responses.

Schema converts unknown -> trusted at the edges. One definition = type + validator + transformer. Define it once, use it everywhere.

Trust nothing at the boundary. Trust everything inside. The boundary is the only place you parse. After that, the types guarantee shape.

7. Layer behavior

What cross-cutting concerns wrap nodes? Retry policies, timeouts, logging, tracing, caching.

These wrap via .pipe() WITHOUT changing the core graph. The happy path stays clean -- you can read it without wading through retry logic and timeout configuration. Behavior is composed orthogonally. The graph says WHAT happens. The layers say HOW it behaves under pressure.

8. Scope resources

What nodes acquire resources? Database connections, file handles, WebSocket connections, child processes.

Acquire/release is a type guarantee. If a node opens a connection, scope ensures it closes -- even on error, even on interrupt. Cleanup is structural, not a TODO comment you hope someone remembers.

9. Swap R to prove it

The call graph doesn't change between production and tests. Only R changes.

Same graph shape. Same A flowing through. Same E possible. Different layer behind R. If the graph can't run with a test R, the design has hidden dependencies. If you have to mock the world to test one node, the node is doing too much.

This is the payoff of separating A, E, and R -- you prove the graph correct by swapping what's behind R.

10. gen is A, pipe is E

The separation of A (step 2) and E (step 4) maps directly to Effect code structure:

  • Effect.gen body -- the happy path. Every yield* is an A flowing through the graph. No error handling inside.
  • .pipe() after gen -- the complete E enumeration. Before writing the pipe, read the actual E type of every yielded effect. The pipe catches, retries, or transforms every E that the gen body can produce.

This is not a stylistic preference. If error handling lives inside the gen body, the A path and E path are tangled -- you cannot read the happy path without wading through catches and retries. The gen body IS the call graph from step 2. The pipe IS the error annotation from step 4.

E scoping at graph layers

Each layer in the call graph scopes its own E before passing to the next layer:

Services  →  Auth  →  Handlers
E=SqlError    E=DatabaseError    E=AuthError    E=RPC errors
  ↓ scope       ↓ scope            ↓ scope
DatabaseError  AuthError          RPC errors

A service catches SqlError and produces DatabaseError. Auth catches DatabaseError and produces AuthError. The handler catches AuthError and produces the RPC-declared error. Each layer's pipe is a complete enumeration of what IT received -- not what originated three layers down. Consumers never see implementation errors from deeper layers.

Divergent strategies

When two effects in the same gen body need different E handling -- one should fail hard, the other should fall back gracefully -- that is a divergent strategy. Handle each effect's E inline within the gen body, because the outer pipe cannot distinguish which yield produced which error.

This is the ONE exception to "no error handling in gen." It exists because the graph has a fork: two branches with different failure semantics sharing a node. Mark it clearly -- it should be rare.


The Pipeline

PROBLEM
  -> "What are the shapes?"                       -> define the domain language
  -> "What is the happy path?"                    -> draw the call graph (A)
  -> "Is each node one-shot or a flow?"           -> mark cardinality
  -> "Where can it break?"                        -> annotate errors on the graph (E)
  -> "What does each node need?"                  -> annotate requirements on the graph (R)
  -> "Where does untrusted data enter?"           -> Schema at graph boundaries
  -> "What wraps nodes without changing them?"    -> pipe behavior orthogonally
  -> "What resources need cleanup?"               -> scope lifecycle
  -> "Can I swap R and the graph still works?"    -> verify with test layers
  -> "Does my code separate A from E structurally?" -> gen body = A, pipe = E
  -> CODE                                         -> the code IS the graph

If the code doesn't match the call graph, the implementation is wrong.

Call Graph Output

When showing call graphs, execution flows, or architecture traces, use this format:

Production:

HTTP handlers
   ComponentA
     ComponentA.layerX
       ComponentB
         ComponentC

Tests:

HTTP handlers
   ComponentA
     componentMemoryLayer
       ComponentA.layer
         ComponentB.layerMemory
  • Plain text only, no rendered diagrams
  • Indented arrows for hierarchy
  • ts code block
  • Production and Tests as separate sections when they differ
  • Include call graphs in project overviews, architecture summaries, and code explanations

Design Graph

The same method, turned on the interface. A screen is not a picture — it is a node with three channels, and the layout you draw is the graph the user walks.

Design Thinking answers what flows through the program. Design Graph answers what the person in front of it can be looking at. Same three-channel discipline: one channel for the content, one for every way the content can be absent, one for what the surface needs before it may exist.

Job → Flow → Surface<C, V, N>
│      │      │  │  │
│      │      │  │  └─ what the surface needs to exist   (§5)
│      │      │  └──── every way the content is absent   (§4)
│      │      └─────── what the person reads and does    (§2)
│      │
│      └─ nodes = surfaces, edges = the moves between them
│
└─ the job: what the person came here to get done

§1  Surfaces     the nouns: screens, panes, rows, fields, controls
§2  C            happy path as flow graph
§3  Cardinality  one record (Detail) or many (List / live)
§4  V            void states: empty, loading, partial, error, denied
§5  N            needs: data, permission, prior step, viewport
§6  Boundary     validate at the field, not at submit
§7  Behavior     motion and feedback wrap a surface, never reshape it
§8  Scope        attention is acquired and must be released
§9  Proof        swap N, same flow shape
§10 Craft        markup = C, state styles = V

Read the job. Draw the moves as a flow graph. Build an interface that IS the graph. If a surface can render a state the graph cannot name, the interface is lying.

1. Name the surfaces

What are the things a person can look at? Before opening a layout tool, define the interface language.

  • Surfaces -- the addressable places. A rail, a list, a detail pane, a drawer. Each one has a name and an owner.
  • Units -- the repeated shape inside a surface. A row, a field, a metric. Named once, reused everywhere.
  • States -- what a unit can be. Rest, hover, selected, disabled, stale. Enumerated, not improvised at build time.
  • Moves -- what the person can do to get somewhere else. Open, select, dismiss, commit, undo.

These are the nouns. The moves are the verbs. A design system that names colours but not surfaces has no vocabulary — only a palette.

2. Think C first

Map the successful job as a flow graph before drawing a single frame. Nodes are surfaces, edges are the moves between them.

Rail(scope) -> List(C) -> Detail(C) -> Commit

This graph is the information architecture. Every screen that does not appear on it is a screen nobody asked for. If two nodes need the same surface, that surface is a component; if a node has one edge in and one edge out, it may not need to be a screen at all.

3. One or many?

Decide the cardinality of each node before its layout. Getting this wrong is the most expensive mistake in the graph, because it changes the shape of the surface, not its styling.

  • One record -- a detail surface. Reading measure, full metadata, one primary action.
  • Many records -- a list surface. Scannable, uniform rows, selection as a wash. Must survive 0, 1, and 10,000.
  • Live -- values that change while being read. Show the timestamp, never move the row under the cursor.

Do not let a list masquerade as a card, and never let a live value pretend to be static. A surface designed for the average case has no design for the real one.

4. Think V second

V is the void channel: every way the content can fail to be there. Each node enumerates its own, and each one is a designed surface, not a fallback.

  • Empty -- nothing yet, and that is fine. One grey sentence saying what will appear here.
  • Loading -- the shape is known, the values are not. A flat skeleton in the layout the content will occupy.
  • Partial -- some of it arrived. Show what you have and mark what is missing — never block the whole surface for one field.
  • Error -- it broke, and the person can act. Say what failed, in their words, next to the thing that failed.
  • Denied -- they may not see it. Prefer never routing them here over explaining the refusal.

A surface with one designed state and four undesigned ones is 20% designed. The void states are where the interface earns trust, because they are the states the person meets on their worst day.

5. Think N third

Mark what each surface needs before it is allowed to exist. "We cannot show X if we do not have Y."

N is the interface's version of compile-time proof. A surface declares its needs — a signed-in user, a selected record, a permission, a minimum viewport, a completed prior step — and the graph must satisfy them on the edge that reaches it.

  • Data -- a record must be selected. No selection is a different node, not an empty pane.
  • Permission -- if they cannot act, do not draw the affordance. A disabled button is a last resort.
  • Prior step -- step 3 requires step 2. Unreachable is better than reachable and broken.
  • Viewport -- three panes need width. Below it, the graph re-routes — the detail becomes a drawer, not a squeeze.

If a screen can be reached without its needs met, the flow graph has a hole. That hole is a bug in the design, not a case for the engineer to handle.

6. Trust at boundary

Where does the person's own input enter the graph? Fields, uploads, pastes, drags, URLs they typed themselves.

Validate at the field, not at submit. The boundary of the interface is the control they are touching — that is where unknown becomes trusted, and where the message belongs. One definition of a field gives the label, the constraint and the error sentence together.

Trust nothing at the boundary. Trust everything past it. A form that only fails on submit has moved its boundary to the wrong place, and made the person pay for the move.

7. Layer behavior

What wraps a surface without changing what it says? Motion, focus, feedback, density, keyboard affordance, reduced-motion.

These are layers around a node, never edits to it. A wash on hover, a reveal on entry, a ring on focus — the content graph is identical with them and without them. If removing the animation changes what the person can learn from the screen, the animation was carrying content and the layout was underbuilt.

The graph says WHAT the surface is. The layers say HOW it responds to being used.

8. Scope attention

Which surfaces acquire the person's attention? Modals, drawers, menus, toasts, anything that takes the viewport or the focus ring.

Attention is a resource with acquire and release. Whatever takes focus returns it to where it came from — on confirm, on cancel, on escape, on interrupt. Whatever covers the graph must name the single move that uncovers it.

An overlay with no defined release is a leak. The person is left holding a surface the graph has forgotten about.

9. Swap N to prove it

The flow graph does not change between the demo and the first real account. Only N changes.

Same nodes. Same C flowing through. Same V possible. Different context behind N. Run the graph four times and see whether it still holds:

  • Day one -- nothing in the account. Every list is empty; the graph must still be legible.
  • Year three -- 40,000 records, titles twice as long as the mock. Nothing may reflow into nonsense.
  • Least access -- the read-only member. Which affordances vanish, and does the layout survive their absence?
  • Small and slow -- one hand, poor network, reduced motion on. The graph re-routes; it does not degrade.

If the design only works with ideal data, it is not a design — it is a screenshot. This is the payoff of separating C, V and N: you prove the interface by swapping what is behind N and watching the graph hold its shape.

10. Markup is C, state styles are V

The separation of C (§2) and V (§4) maps directly to how the interface is built:

  • the component tree -- the happy path. Every element is content flowing through the graph. No state branching inside.
  • the recipe variants -- the complete V enumeration. Before writing them, read the actual states the surface can be in. Every one is named and styled — none is left to the browser default.

This is not a stylistic preference. If state handling lives inside the tree, the content path and the void path are tangled — you cannot read the layout without wading through conditionals, and no one can tell which states were designed and which were merely reached.

The tree IS the flow graph from §2. The variants ARE the void enumeration from §4.

One method, two materials

The mapping is exact by construction. Anywhere the two columns disagree, one of the two graphs is wrong — most often the interface, because it is the one that gets drawn before it is thought.

§ DESIGN THINKING DESIGN GRAPH
X the problem to build the job to get done
graph functions and data flow surfaces and moves
1 records, IDs, variants, errors surfaces, units, states, moves
2 A — what flows C — what is read and done
3 Effect or Stream detail, list, or live
4 E — retry, escape, die V — empty, loading, partial, error, denied
5 R — dependencies, proven N — needs, satisfied on the edge
6 parse at the transport edge validate at the field
7 pipe wraps the node motion wraps the surface
8 scope the resource scope the attention
9 swap R in tests swap N in review
10 gen body = A, pipe = E tree = C, variants = V

The Craft Pipeline

JOB
  -> "What are the surfaces?"                      -> define the interface language
  -> "What is the successful path?"                -> draw the flow graph (C)
  -> "One record, many, or live?"                  -> mark cardinality per surface
  -> "How can the content be absent?"              -> annotate void states (V)
  -> "What must be true to show this?"             -> annotate needs on the edges (N)
  -> "Where does their input enter?"               -> validate at the field
  -> "What wraps a surface without reshaping it?"  -> layer motion and feedback
  -> "What takes attention, and how is it given back?" -> scope every overlay
  -> "Does it hold on day one and year three?"     -> swap N and re-walk the graph
  -> "Does the build separate C from V?"           -> tree = C, variants = V
  -> INTERFACE                                     -> the interface IS the graph

If a surface can render a state the graph cannot name, the design is wrong.

Graph Protocol

The same method, turned on the orchestration. A task is not a checklist — it is a graph with nodes, edges, and channels, and the delegation you write is the subgraph the worker walks.

Design Thinking answers what flows through the program. Graph Protocol answers what flows through the orchestration. Same three-channel discipline: one channel for the work, one channel for where delegation breaks, one channel for what each agent needs.

X → Graph → Delegation<A, E, R>
│              │       │  │  │
│              │       │  │  └─ what each agent needs            (§5)
│              │       │  └──── where the delegation breaks      (§4)
│              │       └─────── what flows through agents        (§2)
│              │
│              └─ nodes = tasks, edges = data dependencies
│
└─ the problem: what you're trying to build

§1  Task Nodes    the nouns: files, modules, domains, workers
§2  A             happy path as execution graph
§3  Cardinality   one worker (SMALL) or many (MEDIUM/LARGE)
§4  E             break points: wrong context, missing input, misinterpretation
§5  R             requirements: subgraph, design method, verification, WHY
§6  Boundary      subgraph in, implemented graph out — structured at edges
§7  Behavior      observe wraps delegation without changing the graph
§8  Scope         worker attention is acquired and must be released
§9  Proof         compare delegated subgraph vs implemented graph
§10 Structure     prompt = subgraph, return = implemented graph

Read the problem. Draw the task flow as an execution graph. Delegate work that IS the subgraph. If the implemented graph does not match the delegated subgraph, the delegation is wrong.

1. Name the task nodes

What are the units of work? Before drawing an execution graph, define the orchestration language.

  • Nodes -- the discrete units of work. A service change, a component update, a config edit. Each has a domain and a worker.
  • Domains -- who owns the node. Each domain maps to a worker with the right expertise. One node, one owner.
  • Edges -- data dependencies between nodes. Node C needs the output of nodes A and B. No edge means independent.
  • Waves -- groups of independent nodes that run in parallel. Edges between waves create gates.

These are the nouns. The execution is the verbs. You cannot delegate until you know what depends on what.

2. Think A first

Map the successful task as an execution graph before spawning any worker.

wave1[A∥B] → gate → wave2[C] → wave3[D]

What is the happy path through the task? Which nodes are independent (parallel)? Which depend on prior results (sequential)? This graph IS your delegation plan. Draw it first. The spawning follows.

3. One or many?

Is the task one worker or many?

  • One worker -- a small or trivial task. Single node, no execution graph needed. Inline the task in the prompt.
  • Many workers -- a medium or large task. Multiple nodes, draw the execution graph. Each worker gets its subgraph.
  • Waves -- independent nodes in the same wave spawn in parallel. The gate between waves waits for all prior nodes.

Same three channels (A, E, R) in all cases. Different cardinality. Mark it on the graph so the delegation matches.

4. Think E second

Mark where the delegation can break. Each break point is one of three things:

  • Wrong context -- the worker does not understand the problem as deeply as the coordinator. Missing files, missing WHY, missing constraints.
  • Missing input -- a node in wave 2 needs the output of wave 1, but the coordinator did not pass it. The edge is invisible.
  • Misinterpretation -- the worker implements the LETTER of the delegation, not the INTENT. The subgraph was ambiguous.

Delegation failures are not worker failures — they are coordinator failures. The coordinator controls the prompt. If the worker misunderstands, the prompt was wrong.

5. Think R third

Mark what each worker needs before it can do the work. "The worker cannot do X if it does not have Y."

R is the delegation prompt completeness check. Every worker needs:

  • Subgraph -- what to implement, in the notation the worker's domain expects
  • Design method -- which methodology applies to this worker's domain, and which sections govern the work
  • Verification command -- how the worker proves the work is correct
  • WHY -- the reason this node exists, not just what to change

If any R is missing from the prompt, the worker will guess. Guessing is the source of misinterpretation (§4).

6. Trust at boundary

Where does unstructured data cross the orchestration boundary? The coordinator sends a prompt. The worker sends a result.

Structure at the boundary. The coordinator includes the subgraph in the prompt — not prose, not "fix the thing," but the graph the worker must implement. The worker returns the implemented graph in its result — not a bare signal, but the graph it actually built.

Trust nothing at the boundary. Trust everything inside. The boundary is the only place the graph is transferred. After that, the worker owns its subgraph.

7. Layer behavior

What wraps the delegation without changing the graph? Observations, session tracking, phase transitions.

The coordinator records the delegated subgraph BEFORE spawning. The worker records the implemented graph AFTER implementing. These observations wrap the delegation without changing what the worker does. The happy path stays clean — you can read it without wading through session tracking.

8. Scope attention

A worker is a resource. It is acquired (spawned) and must be released (completed with structured data).

The coordinator acquires a worker's attention by spawning it. The worker releases attention by completing with the implemented graph. Between acquire and release, the worker owns its subgraph. The coordinator does not interfere — it waits at the gate.

If a worker is spawned without a clear subgraph, its attention is wasted. If a worker completes without reporting its implemented graph, the data is lost. Acquire with structure. Release with data.

9. Compare to prove it

The delegated subgraph and the implemented graph must match. If they do not, something is wrong.

After the worker completes, the coordinator reads both records:

  • The delegated subgraph (what was asked)
  • The implemented graph (what was built)

Same nodes. Same A flowing through. Same E possible. If the implemented graph has nodes the delegated subgraph did not mention, the worker went off-script. If the delegated subgraph has nodes the implemented graph did not cover, the worker missed something. This is the payoff of structuring at the boundary — you prove the delegation correct by comparing graphs.

10. Prompt is subgraph, return is implemented graph

The separation of delegation (§6) and return (§7) maps directly to orchestration structure:

  • The prompt -- carries the subgraph. The graph the worker must implement, in the notation the domain expects, with context and verification.
  • The return -- carries the implemented graph. The graph the worker actually built, structured so the coordinator can compare it against the delegated subgraph.

This is not a stylistic preference. If the subgraph lives in prose and the return is a bare string, the delegation and verification are tangled — you cannot read what was asked without parsing natural language, and you cannot verify what was built without reading the entire diff. The prompt IS the delegated graph. The return IS the implemented graph.


The Protocol Pipeline

TASK
  -> "What are the task nodes?"                       -> define nodes, domains, edges
  -> "What is the happy path?"                        -> draw the execution graph (A)
  -> "One worker or many?"                            -> mark cardinality
  -> "Where can the delegation break?"                -> annotate break points (E)
  -> "What does each worker need?"                    -> annotate requirements (R)
  -> "Where does the graph cross boundaries?"         -> subgraph in, implemented graph out
  -> "What wraps delegation without changing it?"     -> observe before and after
  -> "What resources need release?"                   -> scope worker attention
  -> "Does the implemented graph match?"              -> compare delegated vs implemented
  -> "Does my prompt separate subgraph from prose?"   -> prompt = subgraph, return = graph
  -> DELEGATION                                       -> the delegation IS the subgraph

If the implemented graph does not match the delegated subgraph, the delegation is wrong.

@yosephbernandus

Copy link
Copy Markdown

Ini gus @r17x hasilnya pake codex, kalo claude aman tinggal bilang Adopt this <URL>

Screenshot 2026-08-08 at 00 38 18

Pas codex Adopt this <URL> Hasilnya salah kaya yang X kanan, kemudian saya anu2in pake ini baru hasilnya bener

Build a Call-Graph Skill for Codex

You are Codex acting as implementer. Create a small repository-scoped skill that makes call-graph answers consistent, then add a short trigger rule to repository AGENTS.md.

Create files, validate them, report result, then stop.

Sources

Read both files completely:

  1. DESIGN_THINKING.md
  2. ECALL_GRAPH_IN_YOUR_AGENTS.md

Pinned raw files:

  1. DESIGN_THINKING.md raw
  2. ECALL_GRAPH_IN_YOUR_AGENTS.md raw

Scope

Build only:

<repo-root>/
  AGENTS.md
  .agents/skills/call-graph/
    SKILL.md
    agents/openai.yaml
    references/output-format.md

Use Git root, or current directory when no Git root exists.

Do not build hooks, plugin, marketplace entry, global configuration, .brain integration, or recursive Codex evals. Those are separate follow-up work.

Procedure

  1. Read nearest existing AGENTS.md.
  2. Inspect existing .agents/skills/call-graph and worktree status.
  3. Preserve unrelated changes.
  4. Use $skill-creator and its current scaffold and validation workflow.
  5. Create or update call-graph; never duplicate it.
  6. Keep SKILL.md concise. Put detailed grammar in references/output-format.md.
  7. Merge short trigger rule into AGENTS.md; never replace existing content.
  8. Run finite local validation and review final diff.

Skill trigger

Trigger for call graph, execution flow, request path, architecture trace, function callers, upstream/downstream behavior, “How does X work?”, “What calls X?”, “Where does X go?”, and production/test flow comparisons.

Do not trigger for trivial facts, port/version questions, simple definitions, text edits, rename-only changes, or unrelated visual diagrams.

Support explicit $call-graph invocation.

Skill workflow

  1. Determine requested scope.
  2. Read repository instructions.
  3. Find real entry point, callers, and callees with focused source navigation.
  4. Inspect production wiring.
  5. Inspect tests only when test graph may differ.
  6. Build hierarchical graph using real symbol names.
  7. Verify every node and add path:line evidence.
  8. Explain only conditions, retries, errors, and gotchas graph cannot show.
  9. Stop when requested scope is covered.

Prefer language-server navigation, rg, ast-grep, compiler, type checker, and targeted tests. Never guess paths, symbols, callers, test topology, or line numbers.

Output contract

Store this contract and examples in references/output-format.md:

graph: <short title>

Production:
```ts
EntryPointComponentAComponentA.method
      → [condition] ComponentB
        → {queue_or_store}
```

Tests:
```ts
TestEntryPointComponentATestLayerComponentA.methodComponentBTestLayer
```

src:
  EntryPoint → path/to/file.ts:LINE
  ComponentA → path/to/component.ts:LINE
  ComponentA.method → path/to/component.ts:LINE
  ComponentB → path/to/other.ts:LINE

Rules:

  • Plain text only; no Mermaid.
  • Use ts fence and two-space-indented children.
  • Root has no arrow; indentation represents call hierarchy.
  • Use actual functions, methods, services, jobs, queues, and stores.
  • Show Production always; show Tests only when graph differs.
  • Never invent test graph.
  • Include verified evidence for every unique node.
  • Skip graph for trivial single-fact questions.
  • Mark planned nodes [new]; never invent future line numbers.

AGENTS.md rule

Merge this section once:

## Call graph answers

For flow, path, trace, caller, architecture, and how-it-works questions,
use the `call-graph` skill when available. Lead with a plain-text hierarchical
call graph in a `ts` fence. Use two-space-indented `` children. Show Production
always and Tests only when they differ. Include verified `path:line` evidence
for every node. Skip graph for trivial single-fact questions.

Validation

  1. Run skill validator from $skill-creator.
  2. Validate YAML and Markdown structure.
  3. Confirm SKILL.md has only required name and description frontmatter.
  4. Confirm agents/openai.yaml matches skill.
  5. Confirm AGENTS.md has one call-graph section.
  6. Review diff for unrelated changes.

Do not launch another Codex session. Provide these manual tests instead:

$call-graph How does authentication work?
How does token refresh work?
What calls <known function>?
Show production and test flow for <known component>.
What port does <known service> use?

First four should graph when source contains multi-step flow. Port question should answer directly.

Stop condition

Stop after skill files exist, AGENTS.md rule is merged, local validation passes, diff is reviewed, and manual tests are provided.

Do not add hooks or plugin. Do not retry implicit invocation recursively. Do not continue exploring after these checks pass.

Final response

Report files changed, validation results, explicit invocation example, manual tests, and skipped checks. Nothing else.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment