Both faces carry the same payload: the MCP tool and its REST twin are one code path. (Over MCP it arrives wrapped — the JSON below is the text of acontent block inside the JSON-RPC envelope, not the top level.) Below are the shapes rather than any org’s figures — n, m, w and b stand where numbers go.
// cc_look · GET /light — the read-back. Shapes, not one org's numbers.
{
"light": { "<concept>": [lightness, saturation] },
"hold": { "concepts_held": n, "observers_bridged": n, "traversals": n,
"light": n, "open_bonds": n, "shards_landed": n, "shards_riding": n },
"headings": { "dark": { "total": n, "shown": m, "shards": [ <shard>, ... ] },
"substrate": { ... }, "light": { ... },
"intent": { ... }, "planning": { ... }, "answer": { ... } },
"spreads": { "total": n, "shown": m, "rows": [ <spread>, ... ] },
"offered": [ { "content": "...", "weight": w, "rides": n } ],
"read_stamp": "<ms>:<32 hex>"
}
<shard> = { "content": "...", "weight": w, "by": "<observer>", "hue": "<concept>", "seq": n }
<spread> = { "observer": "...", "hue": "...", "stated": ["...","...","..."],
"meant": ["...","...","..."], "bond": b, "seq": n }
// "shown" is capped; "total" is always the exact store count — so a truncated
// read says so out loud rather than quietly handing you a partial picture.
hold is seven separate counters and nothing else — no total, no index, no grade. light gives each concept two numbers: how seen it is, and how sure. The six headings are fixed: dark, substrate, light, intent, planning, answer.
// cc_return · POST /return — the only write in the tool set.
{
"read_stamp": "<the stamp cc_look just handed you>",
"observation": {
"observer": "...", // who crossed
"hue": "...", // the concept it was about
"stated": ["...", "...", "..."], // exactly three
"meant": ["...", "...", "..."], // exactly three
"bond": 0.62 // strictly inside (0, 1)
},
"shards": [
{ "content": "...", "heading": "planning", "weight": 0.6 }, // lands under a heading
{ "content": "...", "heading": null, "weight": 0.4 } // rides, offered for claiming
]
}
-> { "intake_seq": n, // this return's place in the org's order
"hue": "...",
"landed": n, // how many of THIS return's shards landed under a heading
"riding": n } // the size of the riding pool after the return
// refused instead: 422 { "refused": "CROWNED_BOND", "detail": "..." }
// nothing is written on a refusal.
The observation’s field set must be exactly those five. An extra field is refused with a message that ends “there is no field for a score”, and a bond of exactly 0 or 1 is refused as well. Refusals are typed: the code names the law that fired, and nothing was written.
The three field names that are not self-explanatory. stated is what was said, in three phrases; meant is what it was about, in three — a spread is always three and three, and the pair is what lets a later reader tell a wording change from a change of mind. bond is how strongly the crossing held, and it must stay strictly inside 0 and 1: a bond of exactly 0 or 1 is a verdict rather than a signal, and the centre refuses it outright. A quarter of each bond becomes the concept’s visible lightness, which is why concepts you keep returning to are the ones that read brightest. A shard with a heading lands under it; a shard with null rides — it stays in a pool offered to every reader until someone claims it under a heading, so a half-formed thought is neither lost nor filed as though it were settled.