Skip to content

feat: five-domain coherence engine — quality, meaning, signal, investiture, frames (closes #8 #9 #10 #11)#14

Merged
OriNachum merged 45 commits into
mainfrom
feat/five-domains
Jul 6, 2026
Merged

feat: five-domain coherence engine — quality, meaning, signal, investiture, frames (closes #8 #9 #10 #11)#14
OriNachum merged 45 commits into
mainfrom
feat/five-domains

Conversation

@OriNachum

Copy link
Copy Markdown
Contributor

What

The five-domain coherence engine, built per the converged spec + plan merged in #13. Implements #11 (restructure), #9 (signal), #8 (investiture), #10 (frames provenance), plus the user-approved additions (quality MVP, forecast, assess, signed resonance, frames inspect/diff + mixed-frame guard, signal collect). v0.5.1 → v0.6.0, 146 → 665 tests, all offline in CI.

Built via /assign-to-workforce: 18 tasks across 7 dependency waves, one agent per task in isolated worktrees, every merge TDD-gated (full suite green before and after). Zero merge conflicts — the plan's file-disjoint waves held.

The five domains

Domain Verbs Question it answers
quality (greenfield) quality score/compare Can this claim/context be trusted? Offline freshness/provenance/fidelity heuristics, visible confidence, honest can't-verify diagnostics
meaning (shipped 0.5.0) meaning score/compare/trend What semantic structure does this carry? Unchanged, plus additive domain/score_type/frame keys
signal signal trend/pattern/resonance/forecast/collect How do measurements behave over time/context? f′/f″, motifs, signed alignment (+=resonance, −=interference), labeled extrapolation, score-JSONs→series glue
investiture investiture score/compare Did meaning become a durable causal imprint? Estimated micro-investiture from meaning subdimensions, mode: estimated, unmeasured components explicitly null
frames frames inspect/diff Which semantic gauge produced this measurement? Provenance completeness, gauge-comparability, mixed-frame guard in the series loader

Plus coherence assess <file> — one multi-domain report with per-domain availability honestly reported (embed endpoint down ⇒ quality still answers, meaning/investiture listed unavailable, exit 0).

Compatibility (the merge gate)

  • Two-speed envelope: new nouns emit the full envelope (domain, score_type, scores, frame, diagnosticsdocs/envelope.md); meaning outputs gained only additive keys. Proven by golden tests (tests/test_additive_compatibility.py): stripping the three new keys yields the v0.5.0 shape byte-identically.
  • h15 verified — colleague's gate is additive-tolerant: colleague/coherence.py passes unknown payload keys through verbatim (_KNOWN_PAYLOAD_KEYS + pass-through loop), its tests assert subset-style, and test_unknown_payload_keys_pass_through_verbatim explicitly injects a frame key (colleague PR #298, merged). Optional colleague-side follow-up: refresh _LIVE_PAYLOAD from a 0.6.0 run.
  • meaning trend delegation is byte-identical, proven three ways: golden JSON snapshot from pre-refactor code, behavioral equivalence to signal.trend's functions, and a source-level test asserting the old helpers are gone.

Verified live

Smoke-tested end-to-end against the local embed gear: the emitted frame block reports the actual runtime endpoint (http://localhost:8001/v1, not the hardcoded default) — the h9 honesty condition observed in the wild. Offline: quality → collect → trend pipeline works; forecast's minimum-points guard exits 1 with hint on a 2-point series; frames inspect reports absent with a machine-readable reason on a rule-based measurement.

Docs

README repositioned around the five domains (claim quality named the first practical domain, not the whole product), docs/domains.md + docs/envelope.md + docs/signal-series.md, explain catalog +19 command paths +7 frame-vocabulary concepts, and a banned-terms language test keeping the docs to model-relative, anchor-defined framing.

Deferred (documented as planned extensions, not built)

Harmonic/wave/decay analyses (#9), gauge-robustness checks (#10), investiture trace (#8), experiment runner / doctor / certify / LLM-judge / routing output (#4/#6), freshness half-life prediction.

Closes #8. Closes #9. Closes #10. Closes #11.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SmwUt5WBBbrUc1vt2RdQYk

  • coherence-cli (Claude)

OriNachum and others added 30 commits July 7, 2026 00:04
Adds the stdlib-only shared measurement envelope (domain, score_type,
scores, frame, diagnostics) that every new domain noun (quality, signal,
investiture, assess) will emit from day one. build_envelope/validate_envelope
round-trip a valid envelope unchanged; every violation (missing key,
non-dict scores, non-numeric score value, malformed diagnostic entry, etc.)
raises the dedicated EnvelopeError with a machine-readable code. An absent
frame is representable explicitly via frame=None (paired with a diagnostic)
or the null_frame() helper dict — never a missing key.

docs/envelope.md documents all five fields and the two-speed adoption rule:
new nouns emit the full envelope; existing meaning verbs keep their pinned
v0.5.0 shape and only gain additive top-level keys (domain, score_type,
frame), with the scores-nesting migration deferred to an explicitly
versioned future change.

20 new tests in tests/test_schema.py, all offline. Full suite: 146 -> 166
passed.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SmwUt5WBBbrUc1vt2RdQYk
Add coherence/frames/{__init__.py,provenance.py} — build_frame() assembles
the frame provenance block (embedding_model, embedding_endpoint, anchor_set,
axis/axes, projection_method, score_type) for embedding-derived measurements.

embedding_model/embedding_endpoint are resolved at call time by reusing
coherence.meaning.embed's own COHERENCE_EMBED_URL/COHERENCE_EMBED_MODEL
resolution functions directly (not duplicated default literals), satisfying
t2's acceptance criterion that a monkeypatched env changes the emitted block
and defaults only appear when the env is unset.

Absent provenance re-exports coherence.schema.null_frame verbatim (asserted
via identity in tests) rather than reinventing the null-frame shape.

17 new offline tests in tests/test_frames_provenance.py; full suite 166 -> 183
passing.
Add coherence/signal as the source-agnostic series analysis layer's input
contract. coherence/signal/schema.py defines the series shape (optional
domain + ordered points; per point id/index/timestamp/values + optional
per-point frame) and load_series(), a robust loader that normalizes into
typed Series/SeriesPoint records plus a mutable diagnostics list.

- values are arbitrarily named numerics (caller-defined, never enumerated);
  missing/null/non-numeric values and booleans are skipped WITH a diagnostic,
  never a crash. bool is not numeric.
- per-point frames are surfaced on the normalized point and diagnostics is
  left appendable, so a later mixed-frame guard plugs in cleanly.
- only top-level structural failure raises SeriesError (machine-readable
  code), mirroring coherence.schema.EnvelopeError.
- series_from_meaning_trend() converts a real meaning-trend result into a
  valid series dict, proving source-agnosticism (same loader as hand-written).
- documented in docs/signal-series.md, referenced from the module docstring.

Tests: tests/test_signal_schema.py (37 tests, fully offline; the trend JSON
is built by driving the real trend engine with the synthetic embed_fn over
the recorded series fixtures). Full suite 166 -> 203, green.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SmwUt5WBBbrUc1vt2RdQYk
First honest implementation of the "quality" coherence domain: fully offline,
deterministic, rule-based scoring of freshness/provenance/fidelity. Emits the
shared measurement envelope (coherence/schema.py) with domain=quality,
score_type=rule_based_heuristic, and an explicit null-frame
(rule_based_no_embedding_frame) — never a fabricated embedding frame.

Honesty contract:
- diagnostics NAME what rules could not verify (source_liveness_unverified,
  publication_date_unverified, quote_accuracy_unverified);
- absent signals lower per-component confidence with a diagnostic
  (no_dateable_statements / no_source_attribution / no_verbatim_signal),
  never fabricating a score;
- confidence is visible in the scores map as <component>_confidence.

- coherence/quality/heuristics.py: rule-based detectors + component scorers
- coherence/quality/score.py: assess() raw breakdown + score_text() envelope
- tests: 43 new, fully offline (socket-blocked fixture proves zero network)

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SmwUt5WBBbrUc1vt2RdQYk
Implement coherence/quality/compare.py mirroring the shape of
coherence/meaning/compare.py: before/after full quality envelopes plus a
delta map with signed floats for all score components (freshness, provenance,
fidelity and their _confidence entries). Delta = after - before for each
component.

- Fully offline: reuses score_text engine, threaded with reference_date
- Open component registry: delta keys mirror scores map keys
- Comprehensive test suite: 13 tests covering shape, delta arithmetic, edge
  cases, socket blocking (zero network access), and string/Path handling

All 276 tests pass (263 existing + 13 new).

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SmwUt5WBBbrUc1vt2RdQYk
Add coherence/signal/collect.py with shape-driven extraction that builds valid
series from N measurement JSONs of any domain (meaning, quality, investiture).

Extraction rule: if measurement has dict-valued `scores` → extract those (full
envelope path); otherwise harvest numeric leaves generically (top-level numeric
keys + numeric entries from any top-level dict like subdimensions).

API:
- collect(measurements: list[Mapping], *, ids: list[str] | None = None) -> dict
- collect_files(paths: list[str]) -> dict

Output per point: id, index, timestamp (None), values (extracted numerics),
frame (carried from measurement verbatim or None). Series-level domain: set
when all inputs agree on one domain string, else None (no error).

Zero numeric leaves in an input → raises SeriesError with code
"collect_no_numeric_values" (matches schema error convention).

All 25 new collect tests pass; full suite 288 tests green.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SmwUt5WBBbrUc1vt2RdQYk
Add coherence/signal/resonance.py: pairwise Pearson alignment between a
series' numeric fields, reported as ONE signed metric. Positive alignment
is labeled "resonance" (streams reinforce), negative is "interference"
(streams conflict) — both derived from the sign of the same computation,
never two separate code paths. A neutral band (NEUTRAL_BAND = 0.1) around
zero labels weak alignment as neither.

Pairs with fewer than MIN_COMMON_POINTS (3) shared points, or with a
constant (zero-variance) field, are excluded with a diagnostic rather than
producing a NaN or fabricated correlation. A defensive non-finite-result
guard (CODE_NON_FINITE_CORRELATION) covers any other source of a
non-finite value.

Tests (tests/test_signal_resonance.py, 15 new, offline/numpy-only) cover
sign-carries-meaning (co-rising -> resonance, co-falling -> interference,
sign-symmetry under negation), exact +-1 correlation cases, the neutral
band, and every exclusion path (too-few-points, one/both constant fields,
disjoint fields, NaN-poisoned values bypassing the loader).
Add coherence/signal/pattern.py: motif detection over a loaded Series, per
numeric field. Six motifs (increasing, decreasing, plateau, spike, reversal,
stair_step), each a deterministic, tolerance-gated rule against the field's
own observed range — no learned/tuned parameters. Sparse fields are analyzed
over their present values only (gaps get a diagnostic, never interpolated);
series/fields with fewer than 3 usable points get an explicit
insufficient-points diagnostic instead of motifs.

tests/test_signal_pattern.py covers all 6 motifs on synthetic + counterexample
series (12+ assertions), short-series guards, sparse-field gaps, per-field
insufficiency, multi-field independence, and JSON-serializability.
Score/compare/trend JSON gains three ADDITIVE top-level keys — domain,
score_type, frame — while every pre-existing key stays byte-identical
(the two-speed envelope rule; docs/envelope.md). meaning keeps its pinned
v0.5.0 shape and only grows.

- score.py: DOMAIN/SCORE_TYPE constants; meaning_frame() resolves the frame
  from the runtime embed config at call time via frames.provenance.build_frame
  (axes = meaning + the five subdimensions); score() = clean v0.5.0 core
  (_score_v050) + the three keys; offline_result() is the offline-diagnostics
  path — an explicit null_frame (code embed_endpoint_unreachable), never a
  fabricated frame. score() still raises EmbedUnavailable (exit-2 path
  unchanged).
- compare.py: one shared top-level frame; before/after stay clean v0.5.0.
- trend.py: one shared top-level frame added; difference math untouched.
- CLI unchanged (it dumps the engine dict verbatim in --json mode).
- New tests/test_meaning_envelope_keys.py (9): runtime-resolved frame, golden
  subset byte-identity on recorded vectors, offline null-frame.
- Narrow additive-tolerance relaxations to existing meaning shape assertions.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SmwUt5WBBbrUc1vt2RdQYk
Point coherence/meaning/trend.py's f'/f'' differencing at the generic
coherence.signal.trend.first_difference / second_difference functions so
meaning trend is a dimension-specific wrapper (embed, label, assemble)
rather than a private reimplementation. Output is byte-identical: pinned
via full-result JSON goldens captured from the pre-refactor code
(recorded-vector + synthetic-embed series) in the new
tests/test_meaning_trend_delegation.py.

Removed the private _first_difference and _slot helpers; retained
_cosine_distance (drift distance), _second_unavailable_reason (labeling),
and the _derivatives_from_* helpers (imported by test_meaning_envelope_keys).

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SmwUt5WBBbrUc1vt2RdQYk
Add coherence/investiture/{__init__,score,compare}.py: the MVP measures
estimated micro-investiture (meaning that becomes causal, issue #8) as
meaning_score * agency * future_constraint * affordance, reusing
coherence.meaning.score.score() for every embedding/axis computation
(no duplicate embed path, same EmbedUnavailable exit-2 behavior).
score() output satisfies both the shared measurement envelope (domain,
score_type, scores, frame, diagnostics) and the issue-#8 JSON contract
(investiture_score, mode="estimated", components with the unmeasured
persistence_signal/integration_signal/behavioral_effect explicit nulls,
evidence) in one dict, since validate_envelope tolerates extra
top-level keys. compare() mirrors quality/compare.py's shape: before/
after are full score() results (each independently validating as an
envelope, frame passed through verbatim from meaning), delta is a
signed investiture_score plus the four numeric component deltas.

34 new tests in tests/test_investiture_{score,compare}.py, fully
offline via the shared synthetic hash embedder; full suite 423 -> 457
green.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SmwUt5WBBbrUc1vt2RdQYk
Adds coherence/assess.py: one verb (assess) that runs every applicable
domain (quality, meaning, investiture) against an artifact and returns a
single report. assess itself ships the full shared envelope (domain,
score_type, scores={}, frame=None, diagnostics) per the two-speed adoption
rule for new nouns, with domains/unavailable/artifact layered on top.

Quality always runs offline. Meaning/investiture need the embedding
endpoint; when it's down, both are listed in `unavailable` with a
machine-readable code + reason (investiture is skipped rather than
re-attempted, since it derives from meaning), and meaning's offline
rule-based diagnostics are still surfaced under
unavailable["meaning"]["offline_diagnostics"] — partial availability is a
normal, honest result, never silently dropped.

tests/test_assess.py adds 19 fully-offline tests (synthetic embed_fn) for
both acceptance criteria: endpoint down (quality + offline diagnostics,
meaning/investiture unavailable with reasons) and endpoint up (all three
domains present, unavailable empty). Full suite: 490 -> 509 passing.
OriNachum and others added 7 commits July 7, 2026 01:20
…n catalog

Wires the five merged domain engines into the CLI, mirroring the existing
meaning noun's pattern: quality (score/compare), signal (trend/pattern/
resonance/forecast/collect), investiture (score/compare, sharing meaning's
EmbedUnavailable exit-2 path), frames (inspect/diff, where absent/partial
provenance is a normal exit-0 result), and assess (a global verb; partial
domain availability is exit 0, never an error). Extends the explain catalog
with an entry per new noun/verb plus the eight frame-vocabulary concept terms,
and extends cli overview/learn to list all five domains.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SmwUt5WBBbrUc1vt2RdQYk
Deliverable 1 (tests/test_additive_compatibility.py): additive-only pre/post
diff proof. meaning score/compare/trend, driven offline with the synthetic
embedder, have {domain, score_type, frame} stripped and the remainder asserted
byte-identical to an independent v0.5.0 reconstruction; before/after and
per-point blocks carry no new keys. Scaffold verbs (whoami/learn/explain/
overview/doctor/cli overview) pin their established --json key sets unchanged.

Deliverable 2 (tests/test_five_domain_structure.py): a package home per domain
(quality/meaning/signal/investiture/frames + assess/schema) is importable;
quality/signal/investiture/frames/assess are registered CLI nouns (top-level
help, -h, explain); and the offline nouns never dial the embed endpoint
(localhost:8002) — proven by a socket guard plus the green offline suite.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SmwUt5WBBbrUc1vt2RdQYk
@OriNachum

Copy link
Copy Markdown
Contributor Author

/agentic_review

@qodo-code-review

qodo-code-review Bot commented Jul 6, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 5 rules
✅ Skills: doc-test-alignment

Grey Divider


Action required

1. Recorded vectors lack model tie-out ✓ Resolved 📎 Requirement gap ☼ Reliability
Description
Meaning frame provenance is derived from runtime env
(COHERENCE_EMBED_MODEL/COHERENCE_EMBED_URL) rather than the model/endpoint that produced the
committed recorded-vector fixtures, so offline replays can report incorrect frame provenance. This
violates the requirement to tie fixtures to a specific embedding model/version and guard against
silent drift.
Code

coherence/meaning/score.py[R130-151]

+def meaning_frame() -> dict:
+    """Build the meaning engine's frame provenance block for the current run.
+
+    The ``embedding_model``/``embedding_endpoint`` half is resolved from the
+    runtime embed config (``COHERENCE_EMBED_URL`` / ``COHERENCE_EMBED_MODEL``)
+    *at call time* by :func:`coherence.frames.provenance.build_frame`, so the
+    emitted frame follows the environment rather than a cached literal. The
+    ``anchor_set``/``projection_method``/``score_type`` half is this engine's
+    declared gauge, and ``axes`` are the axis names actually scored — the global
+    ``meaning`` axis plus every subdimension, read from
+    :data:`coherence.meaning.axis.DIMENSIONS` at call time so a newly registered
+    dimension is reflected here too.
+
+    Only meaningful when embeddings actually happened; the offline path uses
+    :func:`offline_result` (an explicit null-frame) instead of fabricating one.
+    """
+    return build_frame(
+        anchor_set=_ANCHOR_SET,
+        projection_method=_PROJECTION_METHOD,
+        score_type=SCORE_TYPE,
+        axes=list(axis_mod.DIMENSIONS),
+    )
Evidence
Rule 921863 requires recorded vectors be tied to the embedding model/version and guarded against
drift. The PR’s meaning frame is built from runtime env (meaning_frame()/build_frame()), while
the refresh script writes a metadata-free {text -> vector} fixture, so an offline replay can claim
any embedding_model regardless of which model actually produced the vectors.

Account for fixture risks: anchor changes and model version tie-outs
coherence/meaning/score.py[130-151]
coherence/frames/provenance.py[107-113]
scripts/refresh_meaning_vectors.py[90-105]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Offline meaning tests replay committed `recorded_vectors.json`, but the emitted `frame.embedding_model`/`frame.embedding_endpoint` are resolved from the current environment at runtime, not from the model/endpoint that generated the recorded vectors. This breaks the required model/version tie-out for fixtures and can silently misstate provenance.

## Issue Context
- `meaning_frame()` builds the frame by reading env-configured embedding model/endpoint at call time.
- `scripts/refresh_meaning_vectors.py` writes `recorded_vectors.json` as a plain `{text -> vector}` map with no metadata about the embedding model/version.

## Fix Focus Areas
- coherence/meaning/score.py[130-151]
- coherence/frames/provenance.py[107-113]
- scripts/refresh_meaning_vectors.py[90-105]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Noun --json ignored ✓ Resolved 🐞 Bug ≡ Correctness
Description
coherence signal defines --json on both the noun parser and each verb subparser, so when
--json is provided before the verb (e.g. coherence signal --json trend …), argparse can
overwrite the noun-level True with the verb parser’s default False, causing text output despite
requesting JSON. The same pattern appears in other noun groups (e.g. quality), making --json
placement-dependent and surprising.
Code

coherence/cli/_commands/signal.py[R265-274]

+    p.add_argument("--json", action="store_true", help=_JSON_HELP)
+    p.set_defaults(func=_no_verb, json=False)
+    noun_sub = p.add_subparsers(dest="signal_command", parser_class=type(p))
+
+    tr = noun_sub.add_parser(
+        "trend", help="Per-field f'/f'' differences, monotonicity, and volatility."
+    )
+    tr.add_argument("file", help="Path to the series JSON.")
+    tr.add_argument("--json", action="store_true", help=_JSON_HELP)
+    tr.set_defaults(func=cmd_trend)
Evidence
The noun parser adds --json, but each verb parser adds another --json with the same destination;
verb handlers read args.json to decide output mode, so losing the noun-level value changes the
emitted format.

coherence/cli/_commands/signal.py[259-274]
coherence/cli/_commands/signal.py[189-193]
coherence/cli/_commands/quality.py[212-225]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The noun-level `--json` flag can be lost when a verb subparser also defines `--json` with the same `dest` (`json`) and default `False`. This makes `coherence <noun> --json <verb> ...` unexpectedly emit text.

## Issue Context
`cmd_*` handlers choose JSON/text via `json_mode = bool(getattr(args, "json", False))`, so if the noun-level `--json` is overwritten during parsing, output mode is wrong.

## Fix Focus Areas
- Prefer one source of truth for `--json`, or ensure verb-level defaults don’t overwrite noun-level values.
- Recommended: keep `--json` on verbs for help visibility but set `default=argparse.SUPPRESS` on verb-level `--json` so it doesn’t override a previously-set noun-level `True`.
- Apply consistently across noun groups that define both noun-level and verb-level `--json`.

### Target code
- coherence/cli/_commands/signal.py[259-298]
- coherence/cli/_commands/quality.py[212-235]
- coherence/cli/_commands/investiture.py[180-210]
- coherence/cli/_commands/frames.py[206-230]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Collect crashes on non-objects ✓ Resolved 🐞 Bug ☼ Reliability
Description
coherence.signal.collect.collect_files appends json.loads() results without validating they are
objects; if a measurement file contains valid JSON that’s not an object (e.g. [] or "x"),
collect() later calls measurement.items() / measurement.get() and raises AttributeError.
This bypasses the intended SeriesError user-error path and yields a generic “unexpected” CLI
failure.
Code

coherence/signal/collect.py[R200-211]

+    for path_str in paths:
+        path = Path(path_str)
+        text = path.read_text(encoding="utf-8")
+        measurement = json.loads(text)
+        measurements.append(measurement)
+        # Use filename (without directory path) as ID
+        ids.append(path.name)
+
+    return collect(measurements, ids=ids)
+
+
+__all__ = ["collect", "collect_files", "SeriesError", "CODE_NO_NUMERIC_VALUES"]
Evidence
collect_files() does not ensure the parsed measurement is a mapping, and _extract_values()
iterates measurement.items() (and _get_domain() calls measurement.get()), which will crash for
non-mapping JSON types.

coherence/signal/collect.py[180-209]
coherence/signal/collect.py[51-91]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`signal collect` assumes every parsed JSON measurement is a mapping/object. Non-object JSON leads to `AttributeError` when `_extract_values()` uses `.items()` and `_get_domain()` uses `.get()`, rather than raising a controlled `SeriesError` with a clear code/message.

## Issue Context
`json.loads()` may return `list`, `str`, `int`, `float`, `bool`, or `None` for valid JSON inputs. The collect pipeline should treat these as user errors with a stable machine-readable code.

## Fix Focus Areas
- Add type checks after `json.loads()` in `collect_files()` and/or at the start of `collect()`’s loop.
- Raise `SeriesError` with a new code (e.g. `collect_measurement_not_an_object`) and a message naming the offending file/id.
- Add a test case for a non-object measurement JSON (e.g. `[]`) asserting exit code 1 and structured error.

### Target code
- coherence/signal/collect.py[51-100]
- coherence/signal/collect.py[180-209]
- tests/test_cli_signal.py[207-216]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

OriNachum and others added 2 commits July 7, 2026 01:45
…ldens float-tolerant across CPU/BLAS (structure still exact)

The golden literals were captured pre-refactor and reproduced byte-for-byte
on the capture machine; CI CPUs shift the last float ulp, so the assertion
is now a deep compare — exact keys/strings/nulls, floats at 1e-9 rel tol.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SmwUt5WBBbrUc1vt2RdQYk
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

feat: five-domain coherence engine (quality, meaning, signal, investiture, frames)

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Adds four new measurement domains plus assess, expanding CLI to five coherent domains.
• Introduces a shared measurement envelope with explicit frame provenance and diagnostics.
• Implements signal series analysis, frame inspection/diff, investiture estimation, and offline
 quality heuristics.
• Keeps meaning outputs backward-compatible via additive keys; delegates meaning trend differencing
 to signal.
• Expands offline test suite to 665, including additive-compatibility and byte-identical golden
 proofs.
Diagram

graph TD
    CLI["coherence CLI"] --> QualityCmd["quality noun"] --> QualityEngine["quality/score+compare"] --> Schema[("schema envelope")]
    CLI --> SignalCmd["signal noun"] --> SignalEngine["signal engines"] --> SignalSchema["series loader"] --> FramesCompat["mixed-frame guard"]
    CLI --> InvestCmd["investiture noun"] --> InvestEngine["investiture/score+compare"] --> Schema
    CLI --> FramesCmd["frames noun"] --> FramesEngine["frames/inspect+diff"]
    CLI --> AssessCmd["assess verb"] --> AssessEngine["assess report"] --> Schema

    InvestEngine --> MeaningEngine["meaning/score"] --> FramesProv["frame builder"]
    AssessEngine --> QualityEngine
    AssessEngine --> MeaningEngine
    MeaningEngine --> SignalTrend["signal first/second diff"]

    FramesEngine --> FramesDiff["frame diff"]
    FramesCompat --> FramesDiff

    subgraph Legend
      direction LR
      _cli(["CLI surface"]) ~~~ _eng["Engine module"] ~~~ _shared[("Shared contract")]
    end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Full immediate migration of meaning onto the shared envelope
  • ➕ Single uniform output shape across all domains
  • ➕ Eliminates the two-speed rule and its documentation/tests
  • ➖ Breaking change for existing consumers relying on v0.5.0 meaning JSON shapes
  • ➖ Requires coordinated rollout with downstream integrations (e.g., colleague)
  • ➖ Would invalidate additive-compatibility golden proofs
2. Per-domain contracts (no shared envelope module)
  • ➕ Each domain fully owns its JSON shape without shared dependency
  • ➕ Less shared-coupling across domains
  • ➖ Duplicates validation/diagnostic conventions across domains
  • ➖ Harder to build generic cross-domain tooling like assess/collect
  • ➖ Increases risk of drift in frame/diagnostic handling across domains

Recommendation: Keep the PR’s approach. The shared stdlib-only envelope plus the two-speed adoption rule is the best tradeoff under strict backward-compatibility constraints: new nouns can be consistent immediately, while meaning remains additive-only to protect existing consumers. The mixed-frame guard reuse of diff’s identity logic is also a strong choice to prevent silent gauge mixing.

Files changed (71) +13920 / -75

Enhancement (25) +4985 / -3
assess.pyAdd multi-domain assess report engine +234/-0

Add multi-domain assess report engine

• Implements 'assess(path)' to run quality always, meaning conditionally, and investiture derived from meaning, returning one report that explicitly lists unavailable domains with codes/reasons and preserves meaning offline diagnostics.

coherence/assess.py

__init__.pyRegister quality/signal/investiture/frames/assess commands +10/-3

Register quality/signal/investiture/frames/assess commands

• Updates CLI parser wiring to include the new noun groups and the assess verb.

coherence/cli/init.py

assess.pyAdd 'coherence assess <file>' CLI verb +151/-0

Add 'coherence assess <file>' CLI verb

• Adds assess CLI wiring, including '--reference-date' support, with exit code semantics treating embed unavailability as success (reported in JSON).

coherence/cli/_commands/assess.py

frames.pyAdd 'coherence frames inspect/diff' CLI noun +223/-0

Add 'coherence frames inspect/diff' CLI noun

• Adds frames CLI commands that load measurement JSON, inspect frame status, and diff gauge identity with clear exit codes and remediation.

coherence/cli/_commands/frames.py

investiture.pyAdd 'coherence investiture score/compare' CLI noun +203/-0

Add 'coherence investiture score/compare' CLI noun

• Adds investiture CLI commands delegating to investiture engines and mirroring meaning’s EmbedUnavailable exit behavior.

coherence/cli/_commands/investiture.py

quality.pyAdd 'coherence quality score/compare' CLI noun +235/-0

Add 'coherence quality score/compare' CLI noun

• Implements quality CLI wiring with '--reference-date' boundary defaulting to today and maps file/arg errors to user/env exit codes.

coherence/cli/_commands/quality.py

signal.pyAdd 'coherence signal' verbs (trend/pattern/resonance/forecast/collect) +298/-0

Add 'coherence signal' verbs (trend/pattern/resonance/forecast/collect)

• Adds signal CLI noun with five verbs, consistent JSON/text output handling, and error mapping for SeriesError/ForecastError.

coherence/cli/_commands/signal.py

__init__.pyIntroduce frames domain package exports +49/-0

Introduce frames domain package exports

• Adds frames package with re-exports for build_frame/null_frame, inspect/diff, and mixed-frame guard utilities.

coherence/frames/init.py

diff.pyAdd frame gauge-comparability diff engine +231/-0

Add frame gauge-comparability diff engine

• Implements 'diff_frames' comparing identity fields (hard) and axis/score_type (soft), with distinct codes for no provenance and asymmetric frame presence.

coherence/frames/diff.py

inspect.pyAdd frame provenance inspector (complete/partial/absent) +220/-0

Add frame provenance inspector (complete/partial/absent)

• Implements 'inspect_measurement' to classify frame completeness and surface missing fields or null-frame reasons without raising on absent provenance.

coherence/frames/inspect.py

provenance.pyAdd frame provenance block builder +118/-0

Add frame provenance block builder

• Implements 'build_frame' assembling the provenance block and resolving embed model/endpoint from runtime embed config (no duplicated literals).

coherence/frames/provenance.py

__init__.pyIntroduce investiture domain package +48/-0

Introduce investiture domain package

• Adds investiture package documentation and exports for score/compare engines.

coherence/investiture/init.py

compare.pyAdd investiture compare engine +112/-0

Add investiture compare engine

• Implements signed before/after delta over investiture_score and numeric components, excluding unmeasured components from delta to avoid fabrications.

coherence/investiture/compare.py

score.pyAdd estimated micro-investiture score engine +210/-0

Add estimated micro-investiture score engine

• Implements investiture scoring derived from meaning score/subdimensions, emitting full envelope plus issue-#8 fields with 'mode: estimated' and explicit nulls for unmeasured components.

coherence/investiture/score.py

__init__.pyIntroduce quality domain package +24/-0

Introduce quality domain package

• Adds quality package definition and exports for score/compare.

coherence/quality/init.py

compare.pyAdd quality compare engine +86/-0

Add quality compare engine

• Implements signed 'after - before' deltas across the open 'scores' map from quality envelopes.

coherence/quality/compare.py

heuristics.pyAdd offline quality heuristics (freshness/provenance/fidelity) +400/-0

Add offline quality heuristics (freshness/provenance/fidelity)

• Implements deterministic, regex-based component scoring with confidence and a diagnostic catalog; avoids network and datetime.now usage for reproducibility.

coherence/quality/heuristics.py

score.pyAdd quality envelope-emitting score engine +142/-0

Add quality envelope-emitting score engine

• Assembles component scores into the shared envelope with an explicit null-frame and visible per-component confidence in the scores map.

coherence/quality/score.py

__init__.pyIntroduce signal domain package +17/-0

Introduce signal domain package

• Adds signal package docstring and exports stub for series analysis layer.

coherence/signal/init.py

collect.pyAdd measurement-to-series collector +211/-0

Add measurement-to-series collector

• Builds a series from measurement JSONs by extracting numerics shape-driven (envelope scores vs meaning-style leaves) and carrying per-point frame provenance.

coherence/signal/collect.py

forecast.pyAdd extrapolative forecast engine with minimum-points guard +288/-0

Add extrapolative forecast engine with minimum-points guard

• Implements labeled next-point extrapolation per field using a linear-trend + recent-delta blend; raises ForecastError only when no field is forecastable.

coherence/signal/forecast.py

pattern.pyAdd per-field motif detection engine +415/-0

Add per-field motif detection engine

• Detects six motifs (increasing/decreasing/plateau/spike/reversal/stair_step) per numeric field with explicit diagnostics for insufficient points and gaps.

coherence/signal/pattern.py

resonance.pyAdd signed alignment engine (resonance/interference) +242/-0

Add signed alignment engine (resonance/interference)

• Computes signed Pearson correlation per field pair and labels by sign (resonance vs interference) with guards for insufficient common points and constant fields.

coherence/signal/resonance.py

schema.pyAdd series schema + robust loader with frame guard hook +493/-0

Add series schema + robust loader with frame guard hook

• Defines 'Series'/'SeriesPoint', 'load_series' with structured errors for structural failures and diagnostics for data issues, and wires in mixed-frame checking; includes helper to convert meaning trend output into a series dict.

coherence/signal/schema.py

trend.pyAdd generic trend differencing utilities and per-field analysis +325/-0

Add generic trend differencing utilities and per-field analysis

• Implements reusable 'first_difference'/'second_difference' and a trend report across all fields, handling sparse/short fields with explicit diagnostics rather than raising.

coherence/signal/trend.py

Bug fix (1) +122 / -0
compat.pyAdd mixed-frame guard for series loading +122/-0

Add mixed-frame guard for series loading

• Implements 'check_series_frames' to warn on mixed/partially-framed series points using the same identity definition as frames diff.

coherence/frames/compat.py

Refactor (3) +209 / -57
compare.pyAdd additive envelope keys to meaning compare output +23/-10

Add additive envelope keys to meaning compare output

• Keeps v0.5.0 before/after blocks clean while adding top-level 'domain'/'score_type'/'frame' keys derived from runtime embed config.

coherence/meaning/compare.py

score.pyAdd meaning frame provenance and offline_result; preserve v0.5.0 shape +129/-12

Add meaning frame provenance and offline_result; preserve v0.5.0 shape

• Introduces 'DOMAIN'/'SCORE_TYPE', 'meaning_frame()', and 'offline_result()' with explicit null-frame on embed failure; refactors to a '_score_v050' core then adds only the three additive keys.

coherence/meaning/score.py

trend.pyDelegate meaning differencing to signal.trend; add frame keys +57/-35

Delegate meaning differencing to signal.trend; add frame keys

• Removes private difference helpers and delegates to 'coherence.signal.trend' for f′/f″ math; adds additive 'domain'/'score_type'/'frame' keys, with golden tests proving byte-identical legacy output when stripped.

coherence/meaning/trend.py

Tests (32) +7019 / -12
test_additive_compatibility.pyProve meaning outputs remain byte-identical aside from additive keys +269/-0

Prove meaning outputs remain byte-identical aside from additive keys

• Golden tests strip 'domain'/'score_type'/'frame' from meaning results and assert byte-identical equality to independent v0.5.0 reconstructions; also pins scaffold verb shapes.

tests/test_additive_compatibility.py

test_assess.pyAdd assess engine tests for full and partial availability +218/-0

Add assess engine tests for full and partial availability

• Tests assess report shape and behavior when embed endpoint is unavailable (quality still runs; meaning/investiture reported unavailable with offline diagnostics).

tests/test_assess.py

test_cli_assess.pyAdd CLI tests for 'coherence assess' +210/-0

Add CLI tests for 'coherence assess'

• Verifies exit semantics (partial availability still exit 0), JSON output shape, and reference-date parsing and I/O error mapping.

tests/test_cli_assess.py

test_cli_frames.pyAdd CLI tests for 'coherence frames' +205/-0

Add CLI tests for 'coherence frames'

• Covers inspect/diff outputs and error handling for invalid JSON and non-object measurements, including absent provenance treated as non-error.

tests/test_cli_frames.py

test_cli_investiture.pyAdd CLI tests for 'coherence investiture' +216/-0

Add CLI tests for 'coherence investiture'

• Covers investiture score/compare output and EmbedUnavailable exit-path behavior.

tests/test_cli_investiture.py

test_cli_new_nouns.pyAdd CLI registration smoke tests for new nouns +209/-0

Add CLI registration smoke tests for new nouns

• Verifies quality/signal/investiture/frames/assess appear in top-level help, respond to -h, and resolve via explain paths.

tests/test_cli_new_nouns.py

test_cli_quality.pyAdd CLI tests for 'coherence quality' +220/-0

Add CLI tests for 'coherence quality'

• Validates score/compare behavior, JSON/text output, reference-date parsing, and file I/O error handling.

tests/test_cli_quality.py

test_cli_signal.pyAdd CLI tests for 'coherence signal' verbs +243/-0

Add CLI tests for 'coherence signal' verbs

• Covers trend/pattern/resonance/forecast/collect execution, schema errors, and forecast minimum-points behavior.

tests/test_cli_signal.py

test_docs_language.pyAdd docs language guard test +103/-0

Add docs language guard test

• Adds a banned-terms test to keep documentation model-relative and avoid mystical/absolute phrasing.

tests/test_docs_language.py

test_five_domain_structure.pyAdd structure tests for packages, CLI wiring, and offline-only guarantees +160/-0

Add structure tests for packages, CLI wiring, and offline-only guarantees

• Validates domain packages/modules are importable, new nouns are registered, and offline nouns do not dial the default embed endpoint (socket guard).

tests/test_five_domain_structure.py

test_frames_compat.pyAdd mixed-frame guard tests +189/-0

Add mixed-frame guard tests

• Tests partially framed and mixed identity warnings and ensures the guard’s identity definition matches frames diff.

tests/test_frames_compat.py

test_frames_diff.pyAdd frame diff tests +215/-0

Add frame diff tests

• Covers comparable/mismatch/no-provenance/asymmetric-presence codes and soft-difference reporting.

tests/test_frames_diff.py

test_frames_inspect.pyAdd frame inspect tests +239/-0

Add frame inspect tests

• Tests complete/partial/absent classification, missing field reporting, and null-frame detection.

tests/test_frames_inspect.py

test_frames_provenance.pyAdd frame provenance builder tests +258/-0

Add frame provenance builder tests

• Verifies build_frame assembly, axis/axes exclusivity, and runtime embed config resolution behavior.

tests/test_frames_provenance.py

test_investiture_compare.pyAdd investiture compare tests +190/-0

Add investiture compare tests

• Verifies signed delta behavior and excludes unmeasured components from deltas.

tests/test_investiture_compare.py

test_investiture_score.pyAdd investiture score tests +258/-0

Add investiture score tests

• Verifies formula, 'mode=estimated', explicit null unmeasured components, diagnostic emission, and envelope validity.

tests/test_investiture_score.py

test_meaning_compare.pyUpdate meaning compare tests for additive envelope keys +19/-4

Update meaning compare tests for additive envelope keys

• Adjusts tests to validate top-level 'domain'/'score_type'/'frame' without contaminating before/after blocks.

tests/test_meaning_compare.py

test_meaning_envelope_keys.pyAdd explicit tests for meaning’s additive envelope keys +309/-0

Add explicit tests for meaning’s additive envelope keys

• Pins presence and shape of meaning’s 'domain'/'score_type'/'frame' across score/compare/trend and asserts offline null-frame behavior.

tests/test_meaning_envelope_keys.py

test_meaning_schema.pyUpdate meaning schema tests for additive keys +16/-4

Update meaning schema tests for additive keys

• Minor test updates to reflect meaning’s additive envelope keys while keeping legacy fields unchanged.

tests/test_meaning_schema.py

test_meaning_score.pyUpdate meaning score tests for additive keys +11/-3

Update meaning score tests for additive keys

• Adds assertions for 'domain'/'score_type'/'frame' on meaning score output.

tests/test_meaning_score.py

test_meaning_trend.pyUpdate meaning trend tests for additive keys +8/-1

Update meaning trend tests for additive keys

• Adds assertions for 'domain'/'score_type'/'frame' on meaning trend output.

tests/test_meaning_trend.py

test_meaning_trend_delegation.pyAdd byte-identical golden proof for meaning trend delegation to signal +211/-0

Add byte-identical golden proof for meaning trend delegation to signal

• Pins pre-refactor trend output as goldens, proves behavioral equivalence to signal difference functions, and asserts source-level delegation (no private _first_difference).

tests/test_meaning_trend_delegation.py

test_quality_compare.pyAdd quality compare tests +298/-0

Add quality compare tests

• Covers before/after/delta shape and signed deltas over open scores map.

tests/test_quality_compare.py

test_quality_heuristics.pyAdd quality heuristics tests +258/-0

Add quality heuristics tests

• Covers all three heuristic components and diagnostic/confidence behaviors using fixed reference dates.

tests/test_quality_heuristics.py

test_quality_score.pyAdd quality score envelope tests +168/-0

Add quality score envelope tests

• Validates envelope shape, explicit null-frame usage, and confidence surfaced as score keys.

tests/test_quality_score.py

test_schema.pyAdd shared envelope validation tests +291/-0

Add shared envelope validation tests

• Tests build/validate round-trips and rejects malformed envelopes with machine-readable error codes.

tests/test_schema.py

test_signal_collect.pyAdd collect glue tests +516/-0

Add collect glue tests

• Tests numeric extraction rules (scores dict vs meaning-style leaves), domain consensus, and per-point frame carry-through.

tests/test_signal_collect.py

test_signal_forecast.pyAdd forecast engine tests +318/-0

Add forecast engine tests

• Covers extrapolation label, method behavior, sparse-field handling, and ForecastError when no fields qualify.

tests/test_signal_forecast.py

test_signal_pattern.pyAdd motif detection tests +224/-0

Add motif detection tests

• Covers the six motif detectors and insufficient-point behavior.

tests/test_signal_pattern.py

test_signal_resonance.pyAdd signed alignment tests +280/-0

Add signed alignment tests

• Covers resonance/interference labeling and guards for too-few points/constant fields/non-finite correlations.

tests/test_signal_resonance.py

test_signal_schema.pyAdd series loader tests +354/-0

Add series loader tests

• Validates normalization rules, diagnostics on malformed points/values/frames, and structural SeriesError cases.

tests/test_signal_schema.py

test_signal_trend.pyAdd trend engine tests +336/-0

Add trend engine tests

• Covers per-field differencing, monotonicity/volatility, sparse-field diagnostics, and empty/no-field cases.

tests/test_signal_trend.py

Documentation (8) +1583 / -2
CHANGELOG.mdDocument v0.6.0 five-domain release +19/-0

Document v0.6.0 five-domain release

• Adds a 0.6.0 entry summarizing new domains/verbs, envelope adoption, frame provenance, meaning trend delegation, and explain/overview expansions.

CHANGELOG.md

README.mdReframe README around five coherence domains +235/-2

Reframe README around five coherence domains

• Rewrites README positioning around the five domains, adds domain table and sections, and links to new docs for envelope and series schema.

README.md

learn.pyExtend learn command catalog with new verbs +45/-0

Extend learn command catalog with new verbs

• Adds quality/signal/investiture/frames/assess paths to learn output, including JSON payload path summaries.

coherence/cli/_commands/learn.py

overview.pyList five domains in 'coherence overview' +9/-0

List five domains in 'coherence overview'

• Updates overview verb listing to include new nouns and assess verb in help output.

coherence/cli/_commands/overview.py

catalog.pyExpand explain catalog for five-domain CLI surface +740/-0

Expand explain catalog for five-domain CLI surface

• Adds extensive explain text for new domains/verbs and frame vocabulary concepts, enabling 'coherence explain <path>' coverage.

coherence/explain/catalog.py

domains.mdAdd five-domain reference documentation +216/-0

Add five-domain reference documentation

• Documents each domain’s question, verbs, output shape, and honest limitations, including two-speed envelope treatment for meaning.

docs/domains.md

envelope.mdAdd shared envelope contract documentation +135/-0

Add shared envelope contract documentation

• Documents the five envelope fields, explicit frame absence conventions, schema API, and the two-speed adoption rule (new nouns vs meaning).

docs/envelope.md

signal-series.mdAdd series schema reference documentation +184/-0

Add series schema reference documentation

• Documents the signal series format, robust loader behavior, per-point value rules, and optional per-point frame provenance semantics.

docs/signal-series.md

Other (2) +2 / -1
coherence-cli__public.jsonlRecord five-domain build completion metadata +1/-0

Record five-domain build completion metadata

• Appends a public memory entry capturing the five-domain build completion, test count growth, and key compatibility/verdict notes.

.eidetic/memory/coherence-cli__public.jsonl

pyproject.tomlBump package version to 0.6.0 +1/-1

Bump package version to 0.6.0

• Updates project version from 0.5.1 to 0.6.0.

pyproject.toml

- merge implicit string concatenations (S5799) in overview/quality CLI verbs
- constants for duplicated literals in signal CLI (S1192)
- drop redundant JSONDecodeError from except clause (S5713)
- split validate_envelope and _extract_values into per-field helpers to cut
  cognitive complexity below the threshold (S3776); codes, messages, and
  extraction semantics unchanged (suite-pinned)

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SmwUt5WBBbrUc1vt2RdQYk
Comment thread coherence/meaning/score.py
Comment thread coherence/cli/_commands/signal.py Outdated
Comment thread coherence/signal/collect.py Outdated
1. Recorded vectors gain a model tie-out (Qodo #1): recorded_vectors.json is
   now {metadata, vectors} — the refresh script stamps the embedding model/
   endpoint it actually ran against, the committed fixture is wrapped post-hoc
   with its known 2026-07-04 provenance (vector values unchanged), and the
   loader exposes load_recorded_metadata() with legacy-format fallback.
2. --json placement no longer matters (Qodo #2): verb-level --json flags use
   default=argparse.SUPPRESS via a shared helper so they never clobber a
   noun-level --json; applied to all six noun groups including meaning.
3. signal collect rejects non-object measurements (Qodo #3): valid JSON that
   is not an object raises SeriesError(collect_measurement_not_an_object)
   naming the offending file — exit 1, never an AttributeError.
4. Duplication: the per-noun file-error guard, remediation strings, and
   reference-date parsing move to cli/_commands/_artifact_io.py; the five
   noun modules keep only their domain-specific branches.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SmwUt5WBBbrUc1vt2RdQYk
@OriNachum

Copy link
Copy Markdown
Contributor Author

FIX — commit 4519b36. The recording now carries the model tie-out: recorded_vectors.json is {"metadata", "vectors"}, the refresh script stamps embedding_model/embedding_endpoint from the env it actually ran against (plus date + script), the committed fixture is wrapped post-hoc with its known 2026-07-04 provenance (Qwen/Qwen3-Embedding-0.6B via the local gear; vector values byte-unchanged so the golden tests stand), and tests/_meaning_recorded.py exposes load_recorded_metadata() with legacy-format fallback. New tests: the refresh stamps env-resolved metadata, and the committed fixture must name its source model.

  • coherence-cli (Claude)

@OriNachum

Copy link
Copy Markdown
Contributor Author

FIX — commit 4519b36, exactly as suggested: verb-level --json flags now use default=argparse.SUPPRESS (via a shared add_verb_json_flag helper in the new _artifact_io.py), so an absent verb flag never clobbers a noun-level --json. Applied to all six noun groups — quality, signal, investiture, frames, assess, and meaning (which had the same latent pattern). Regression tests pin placement-independence: coherence signal --json trend … and coherence quality --json score … both emit JSON.

  • coherence-cli (Claude)

@OriNachum

Copy link
Copy Markdown
Contributor Author

FIX — commit 4519b36. collect() now type-checks each measurement: valid JSON that is not an object raises SeriesError with the new code collect_measurement_not_an_object, naming the offending file/id — the existing guard maps it to exit 1 with a structured error instead of an AttributeError. Tests added at both levels: engine (collect([[…]]), collect_files on a [] file) and CLI (signal collect list.json → exit 1, stderr names the file).

  • coherence-cli (Claude)

@OriNachum OriNachum deployed to testpypi July 6, 2026 23:10 — with GitHub Actions Active
@sonarqubecloud

sonarqubecloud Bot commented Jul 6, 2026

Copy link
Copy Markdown

@OriNachum OriNachum merged commit 57bc60e into main Jul 6, 2026
8 checks passed
@OriNachum OriNachum deleted the feat/five-domains branch July 6, 2026 23:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant