Skip to content

Walkthrough: The Yellow Wallpaper

This guide takes one short literary text through the complete TNGS workflow: ingest → graph review → GraphML export → narrative transforms → atom revision → re-export GraphML → render revised prose.

The source is "The Yellow Wallpaper" by Charlotte Perkins Gilman (1892), a first-person journal narrative well-suited to POV, reliability, and mood analysis. The text is public domain via Project Gutenberg.


The source text

We work with a four-section curated excerpt, saved as yellow_wallpaper.md. Section headings have been added to mark the natural narrative breaks; the prose is Gilman's verbatim.

# The Yellow Wallpaper

## The House

It is very seldom that mere ordinary people like John and myself secure
ancestral halls for the summer. A colonial mansion, a hereditary estate,
I would say a haunted house, and reach the height of romantic felicity—but
that would be asking too much of fate! Still I will proudly declare that
there is something queer about it.

John laughs at me, of course, but one expects that in marriage. John is
practical in the extreme. He has no patience with faith, an intense horror
of superstition, and he scoffs openly at any talk of things not to be felt
and seen and put down in figures.

John is a physician, and perhaps—(I would not say it to a living soul, of
course, but this is dead paper and a great relief to my mind)—perhaps that
is one reason I do not get well faster. You see, he does not believe I am
sick!

## The Room

I don't like our room a bit. I wanted one downstairs that opened on the
piazza and had roses all over the window, and such pretty old-fashioned
chintz hangings! But John would not hear of it.

It is a big, airy room, the whole floor nearly, with windows that look all
ways, and air and sunshine galore. The paint and paper look as if a boys'
school had used it. It is stripped off—the paper—in great patches all
around the head of my bed, about as far as I can reach, and in a great
place on the other side of the room low down. I never saw a worse paper
in my life.

The color is repellant, almost revolting; a smouldering, unclean yellow,
strangely faded by the slow-turning sunlight. It is a dull yet lurid
orange in some places, a sickly sulphur tint in others.

## The Pattern

I lie here on this great immovable bed—it is nailed down, I believe—and
follow that pattern about by the hour. It is as good as gymnastics, I
assure you. I know a little of the principle of design, and I know this
thing was not arranged on any laws of radiation, or alternation, or
repetition, or symmetry, or anything else that I ever heard of.

The whole thing goes horizontally, too, at least it seems so, and I exhaust
myself in trying to distinguish the order of its going in that direction.
It makes me tired to follow it. I will take a nap, I guess.

## The Figure

Behind that outside pattern the dim shapes get clearer every day. It is
always the same shape, only very numerous. And it is like a woman stooping
down and creeping about behind that pattern. I don't like it a bit.
I wonder—I begin to think—I wish John would take me away from here!

At night in any kind of light, in twilight, candlelight, lamplight, and
worst of all by moonlight, it becomes bars! The outside pattern I mean,
and the woman behind it is as plain as can be.

Step 1 — Start the stack

mkdir -p secrets
echo "neo4j/your-password" > secrets/neo4j_auth.txt
docker compose up -d --build

# Wait for readiness
curl http://localhost:8000/v1/health/ready
# → {"status":"ok","neo4j":"connected"}

Step 2 — Ingest the Markdown

Post the file with format: "markdown" so the segmenter treats ## headings as scene boundaries rather than prose atoms.

NARRATIVE_ID="yw-gilman-001"

curl -X POST http://localhost:8000/v1/notes/import \
  -H "Content-Type: application/json" \
  -d "{
    \"title\": \"The Yellow Wallpaper\",
    \"narrative_id\": \"$NARRATIVE_ID\",
    \"format\": \"markdown\",
    \"source_ref\": \"gutenberg.org/ebooks/1952\",
    \"text\": $(cat yellow_wallpaper.md | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))')
  }"

Response:

{
  "narrative_id": "yw-gilman-001",
  "scene_count": 4,
  "atom_count": 22,
  "event_count": 14,
  "character_count": 2,
  "pattern_count": 0,
  "flagged_count": 1
}

Four scenes were created — one per ## heading. The ## The House heading text became scene.summary for Scene 1; its prose became atoms. The heading line itself is not an atom. One atom was flagged for review (below the confidence threshold — likely a sentence fragment).

Why format: \"markdown\" matters

Without it, the segmenter treats every paragraph as a separate scene and heading lines become atoms in Scene 1. With it, the four ## headings become the four scene boundaries and scene summaries.


Step 3 — Review the graph in Neo4j

Open the Neo4j Browser at http://localhost:7474 and run:

// See the four scenes and their summaries
MATCH (n:Narrative {id: "yw-gilman-001"})-[:HAS_SCENE]->(s:Scene)
RETURN s.sequence AS seq, s.summary AS heading, s.id AS scene_id
ORDER BY s.sequence
seq heading scene_id
1 The House s-…
2 The Room s-…
3 The Pattern s-…
4 The Figure s-…
// See atoms for Scene 1
MATCH (n:Narrative {id: "yw-gilman-001"})-[:HAS_SCENE]->(s:Scene {sequence: 1})
MATCH (s)-[:CONTAINS]->(a:Atom)
RETURN a.surface_order AS ord, a.text AS text, a.kind AS kind
ORDER BY a.surface_order
ord text kind
0 It is very seldom that mere ordinary people… descriptive
1 A colonial mansion, a hereditary estate… descriptive
2 Still I will proudly declare that there is something queer about it. descriptive
3 John laughs at me, of course, but one expects that in marriage. descriptive
4 John is practical in the extreme. descriptive
// Find characters extracted across the narrative
MATCH (n:Narrative {id: "yw-gilman-001"})-[:HAS_SCENE]->(s)-[:CONTAINS]->(e:Event)
MATCH (c:Character)-[:PARTICIPATES_IN]->(e)
RETURN DISTINCT c.name AS character, c.role AS role

The extractor finds John (multiple mentions → higher confidence) and the narrator's references to her own agency.


Step 4 — Export baseline GraphML

Export the graph before any transforms to establish a baseline tension map.

curl -sX POST http://localhost:8000/v1/render/yw-gilman-001 \
  -H "Content-Type: application/json" \
  -d '{"type": "graphml"}' \
  | jq -r '.content' > yw_baseline.graphml

Open yw_baseline.graphml in yEd (File → Open), then apply Layout → Hierarchical. At this point, most edges are grey (#A0A0A0) or steel blue (#4682B4) — structural containment and temporal sequence only. No high-tension crimson edges appear yet because no mood, code, or causal transforms have been applied.


Step 5 — Apply a POV transform to Scene 1

The narrator is explicitly subjective and unreliable. Tag Scene 1 to reflect that — internal focalization through the narrator, reliability ambiguous.

First, retrieve Scene 1's ID:

SCENE_1=$(curl -s http://localhost:8000/v1/narratives/yw-gilman-001 \
  | jq -r '.scenes[0].id // empty')

# Or query directly from the JSON render:
curl -sX POST http://localhost:8000/v1/render/yw-gilman-001 \
  -H "Content-Type: application/json" \
  -d '{"type":"json"}' \
  | jq -r '.content | fromjson | .narrative.scenes[0].id'

Apply the POV transform:

curl -X POST http://localhost:8000/v1/transforms/apply \
  -H "Content-Type: application/json" \
  -d "{
    \"scene_id\": \"$SCENE_1\",
    \"axis\": \"pov\",
    \"parameters\": {
      \"focalizer\": \"narrator\",
      \"distance\": \"internal\",
      \"reliability\": \"unreliable\"
    },
    \"operator\": \"analyst\"
  }"

The graph now has a Perspective node attached to Scene 1: focalizer=narrator, distance=internal, reliability=unreliable.


Step 6 — Apply mood across all four scenes (bulk transform)

The story escalates from mild unease to paranoid fixation. Apply a single bulk mood transform that sets the baseline affective register across the entire narrative, then refine per scene if needed.

curl -X POST http://localhost:8000/v1/transforms/apply-bulk \
  -H "Content-Type: application/json" \
  -d '{
    "narrative_id": "yw-gilman-001",
    "axis": "mood",
    "parameters": {
      "label": "dread",
      "valence": -0.75,
      "arousal": 0.6
    },
    "operator": "analyst"
  }'

Response:

{
  "narrative_id": "yw-gilman-001",
  "applied_count": 4,
  "results": [
    { "scene_id": "s-…", "axis": "mood", "status": "accepted",  },
    { "scene_id": "s-…", "axis": "mood", "status": "accepted",  },
    { "scene_id": "s-…", "axis": "mood", "status": "accepted",  },
    { "scene_id": "s-…", "axis": "mood", "status": "accepted",  }
  ]
}

All four scenes now carry MoodState { label: "dread", valence: -0.75, arousal: 0.6 }. The high-arousal negative mood will raise the tension score on edges from these scenes.

Optionally, sharpen Scene 4 (the climax) further:

curl -X POST http://localhost:8000/v1/transforms/apply \
  -H "Content-Type: application/json" \
  -d "{
    \"scene_id\": \"$SCENE_4\",
    \"axis\": \"mood\",
    \"parameters\": {
      \"label\": \"paranoia\",
      \"valence\": -0.95,
      \"arousal\": 0.9
    },
    \"operator\": \"analyst\"
  }"

Step 7 — Tag a key atom with a hermeneutic code

The sentence "There are things in that paper that nobody knows but me, or ever will" is the pivot of the story's mystery. Tag it as hermeneutic (+0.4 tension) to mark it as the central enigma.

Find the atom ID from the JSON render:

curl -sX POST http://localhost:8000/v1/render/yw-gilman-001 \
  -H "Content-Type: application/json" \
  -d '{"type":"json"}' \
  | jq -r '.content | fromjson | .narrative.scenes[].atoms[]
    | select(.text | contains("nobody knows but me"))
    | {id, text}'

Apply the code overlay:

curl -X POST http://localhost:8000/v1/transforms/apply \
  -H "Content-Type: application/json" \
  -d "{
    \"scene_id\": \"$SCENE_3\",
    \"axis\": \"code_overlay\",
    \"parameters\": {
      \"atom_id\": \"$ENIGMA_ATOM\",
      \"code\": \"hermeneutic\",
      \"label\": \"The secret knowledge — what does she see that others cannot?\"
    },
    \"operator\": \"analyst\"
  }"

Step 8 — Revise an atom's prose text

The original sentence in Scene 4 reads:

"I wonder—I begin to think—I wish John would take me away from here!"

For a closer third-person rewrite of this passage, revise the atom directly:

ATOM_ID="<atom-id-from-json-render>"

curl -X PATCH http://localhost:8000/v1/atoms/$ATOM_ID \
  -H "Content-Type: application/json" \
  -d '{
    "text": "She wondered, and then began to think, and then knew with certainty that she wanted to leave.",
    "operator": "editor",
    "reason": "shift from first-person journal to close third-person for comparison"
  }'

Response:

{
  "atom_id": "atom-…",
  "revision_id": "rev-…",
  "text": "She wondered, and then began to think, and then knew with certainty that she wanted to leave."
}

The original text is preserved in the graph under HAS_REVISION. To inspect the full revision chain:

curl http://localhost:8000/v1/atoms/$ATOM_ID/revisions
{
  "atom_id": "atom-…",
  "revisions": [
    {
      "id": "rev-…",
      "text": "She wondered, and then began to think, and then knew with certainty that she wanted to leave.",
      "revised_at": "2026-04-26T14:32:00",
      "operator": "editor",
      "reason": "shift from first-person journal to close third-person for comparison"
    }
  ]
}

Step 9 — Re-export GraphML and compare tension

curl -sX POST http://localhost:8000/v1/render/yw-gilman-001 \
  -H "Content-Type: application/json" \
  -d '{"type": "graphml"}' \
  | jq -r '.content' > yw_transformed.graphml

Open yw_transformed.graphml in yEd alongside the baseline. The changes are immediately visible in the edge colors:

Edge Baseline After transforms
Scene containment (HAS_SCENE) Grey #A0A0A0 Grey #A0A0A0
Temporal sequence (PRECEDES) Steel blue #4682B4 Steel blue #4682B4
Mood edges from scenes with dread Orange #FF8C00
Hermeneutic code atom → events Crimson #DC143C
Scene 4 with paranoia mood Dark red #8B0000

The escalation from Scene 1 to Scene 4 is now visible as a color gradient from orange through crimson to dark red — the graph shows the story's tension arc spatially.


Step 10 — Export revised prose

Render the narrative back to Markdown with all transforms and the atom revision applied:

curl -sX POST http://localhost:8000/v1/render/yw-gilman-001 \
  -H "Content-Type: application/json" \
  -d '{"type": "prose"}' \
  | jq -r '.content' > yw_revised.md

Output structure:

# The Yellow Wallpaper

## The House

> POV: narrator (internal, unreliable) | Mood: dread

It is very seldom that mere ordinary people like John and myself secure
ancestral halls for the summer. A colonial mansion, a hereditary estate,
I would say a haunted house, and reach the height of romantic felicity—but
that would be asking too much of fate! Still I will proudly declare that
there is something queer about it.


## The Room

> Mood: dread

I don't like our room a bit. …

## The Pattern

> Mood: dread

I lie here on this great immovable bed—it is nailed down, I believe—and
follow that pattern about by the hour. …

## The Figure

> Mood: paranoia

Behind that outside pattern the dim shapes get clearer every day. …
She wondered, and then began to think, and then knew with certainty
that she wanted to leave.

At night in any kind of light, in twilight, candlelight, lamplight, and
worst of all by moonlight, it becomes bars! …

Key things to notice:

  • Section headings come from scene.summary (the ## headings in the source Markdown), not from scene sequence numbers.
  • Scene 1 shows both POV and Mood context because both transforms were applied.
  • Scenes 2–3 show only Mood context.
  • Scene 4 shows the sharpened paranoia mood.
  • The revised atom text ("She wondered, and then began to think…") appears in Scene 4 in place of the original first-person phrasing. The original is still in the graph, reachable via GET /v1/atoms/{id}/revisions.

Transform audit trail

Every operation is recorded. Query the full history for the narrative:

MATCH (n:Narrative {id: "yw-gilman-001"})-[:HAS_SCENE]->(s:Scene)
MATCH (t:Transform)-[:APPLIED_TO]->(s)
RETURN s.summary AS scene,
       t.axis     AS axis,
       t.operator AS operator,
       t.applied_at AS when
ORDER BY t.applied_at ASC
scene axis operator when
The House pov analyst 2026-04-26T14:00:…
The House mood analyst 2026-04-26T14:01:…
The Room mood analyst 2026-04-26T14:01:…
The Pattern mood analyst 2026-04-26T14:01:…
The Figure mood analyst 2026-04-26T14:01:…
The Pattern code_overlay analyst 2026-04-26T14:02:…
The Figure mood analyst 2026-04-26T14:03:…

Summary

Step What happened
Ingest with format: "markdown" 4 scenes created from ## headings; heading text stored as scene.summary
Neo4j review Verified scene structure, atoms, characters
Baseline GraphML Structural-only edges; low tension throughout
POV transform (Scene 1) Internal, unreliable narrator tagged
Bulk mood transform dread applied across all 4 scenes in one call
Scene 4 mood refinement Sharpened to paranoia at the climax
Hermeneutic code overlay Central enigma atom tagged; +0.4 tension
Atom revision First-person sentence rewritten to close third-person non-destructively
Transformed GraphML Tension gradient visible — orange → crimson → dark red
Prose render Chapter headings restored; annotations visible; revised atom text in place