other
539 beliefs (424 IN, 115 OUT)
-
IN
absence-has-consistent-dual-semantics
Absence has deliberate, defined semantics throughout the system at two levels: structural absence (no justifications) creates premise behavior via vacuous truth over empty lists, while referential absence (missing nodes) follows conservative/permissive asymmetry — both forms of absence produce predictable behavior rather than errors or undefined state. -
IN
access-control-enforced-at-read-not-write
Access control (`_is_visible`) is enforced at read/query boundaries (`show_node`, `explain_node`, `trace_assumptions`) via `PermissionError`, but write operations (`add_node`, `retract_node`, `assert_node`) do not check visibility. -
IN
access-control-is-transitive-subset-gated
Access control enforces transitive subset-based authorization: visibility requires the caller's tags to be a superset of the node's tags, derived nodes inherit the sorted union of all ancestor tags transitively, and enforcement occurs at read boundaries only — write operations are unrestricted. -
IN
access-tags-subset-gate
A tagged node is visible only when its `access_tags` are a subset of the caller's `visible_to` set; partial overlap (intersection without containment) is insufficient for access. -
IN
access-tags-union-inheritance
A derived node's `access_tags` is the sorted, deduplicated union of all antecedent tags across all justifications, merged with any explicit tags on the node itself. -
IN
active-inactive-relay-pair
Each imported agent gets exactly two infrastructure nodes: `agent:active` (premise, starts IN) and `agent:inactive` (derived via SL with `outlist=[active_id]`, starts OUT); every imported belief includes `inactive_id` in its outlist -
IN
active-not-in-antecedents
The `active` premise is deliberately excluded from imported beliefs' antecedents; if it were an antecedent, it would provide a second always-valid justification path that defeats per-belief retraction semantics -
IN
active-premise-not-in-antecedents
Covered by existing `active-not-in-antecedents` and `kill-switch-uses-outlist-not-antecedent` -
IN
add-justification-achieves-consistent-propagation
Adding a justification to an existing node produces a fully consistent network state through guaranteed-terminating multi-dimensional propagation: truth values cascade via BFS through dependents, the reverse index is updated, and access tags recompute transitively — all within a single operation whose termination is guaranteed by BFS traversal and stop-on-unchanged semantics. -
IN
add-justification-is-fully-propagating
Adding a justification triggers complete multi-dimensional propagation: truth values cascade through dependents via BFS, the dependents reverse index is updated on both antecedent and outlist nodes, and access tags flow downstream transitively through all dependent chains. -
IN
add-justification-propagates-tags-downstream
Calling `add_justification` on an existing node recomputes `access_tags` for the target and all its transitive dependents, enabling retroactive tag propagation. -
IN
add-justification-registers-dependents
`add_justification` updates the `dependents` set on both antecedent and outlist nodes so that future retraction/restoration propagation reaches the target node. -
IN
add-justification-returns-change-dict
`Network.add_justification` returns a dict with keys `node_id`, `old_truth_value`, `new_truth_value`, and `changed` (list of all nodes whose truth value changed). -
IN
add-justification-triggers-propagation
Adding a justification that changes a node's truth value triggers BFS propagation that cascades to all transitive dependents, including restoring OUT nodes whose justifications become valid. -
IN
add-node-evaluates-justification-at-insertion
When `add_node` is called with justifications, the node's initial truth value is computed immediately from the current state of its antecedents — a derived node added when its antecedent is OUT starts OUT -
IN
add-nogood-always-records
`add_nogood` appends a `Nogood` record unconditionally before checking whether the contradiction is active, so nogoods are preserved even when not all member nodes are currently IN -
IN
add-nogood-fallback-uses-dependent-count
When `find_culprits` returns no candidates (all nogood nodes are premises with no justification chains), the fallback retracts the nogood member with the fewest direct dependents -
IN
any-mode-creates-per-premise-justifications
When `any_mode=True` and multiple antecedents are given, each antecedent gets its own SL justification (OR semantics: node is IN if *any* antecedent is IN), rather than the default single multi-antecedent justification (AND semantics). -
OUT
any-mode-expansion-is-evaluation-invisible
Any-mode expansion from conjunctive to disjunctive justifications is invisible to truth evaluation: the expanded justifications propagate completely through the same BFS mechanisms, and truth evaluation produces identical results regardless of whether justifications arrived via original specification or any-mode expansion — a consequence of transformation invariance. -
IN
any-mode-expansion-propagates-completely
When any_mode expands a conjunctive justification into per-premise disjunctive justifications, each resulting justification inherits the complete outlist specification (conjunction semantics, absence tolerance, persistence across save/load), and all resulting truth-value changes propagate completely to every affected dependent — but only when outlist nodes are tracked in the dependents index, ensuring outlist-mediated effects are not silently dropped. -
IN
any-mode-is-structural-expansion
Duplicates existing belief `any-mode-creates-per-premise-justifications` which already captures that any_mode expands N premises into N single-premise SL justifications. -
IN
any-mode-outlist-preserved
When `any_mode` expands `sl="a,b" unless="enemy"`, each resulting single-premise justification inherits `"enemy"` in its outlist. -
IN
any-mode-preserves-full-outlist-semantics
When any_mode expands a single multi-antecedent justification into per-premise justifications (OR semantics), each resulting justification preserves the original outlist entries — ensuring non-monotonic defeat works correctly under disjunctive expansion with no semantic loss. -
IN
api-add-justification-requires-justification-arg
`api.add_justification` raises `ValueError` if none of `sl`, `cp`, or `unless` is provided — at least one justification specification is mandatory. -
IN
api-cascade-symmetry-tested
Test coverage claim; the underlying behavioral invariant (symmetric retract/restore cascades) is already covered by existing beliefs including `reasoning-engine-is-deterministic-and-reversible`. -
OUT
api-enforces-typed-preconditions
API functions enforce preconditions at the system boundary with typed exceptions: duplicate node IDs raise ValueError, missing justification arguments raise ValueError, and unauthorized single-node access raises PermissionError — establishing a consistent error contract at every entry point. -
IN
api-fts-search-bounds-query-calls
`_fts_search` makes at most 51 internal `_fts_query` calls regardless of input query length, preventing combinatorial explosion from progressive relaxation on long queries. -
IN
api-functions-return-dicts
Every public API function returns a `dict` (or `str` for markdown/compact), never a `Network` or `Node` object, ensuring JSON-serializability at the boundary for CLI, HTTP, and tool-call consumers. -
IN
api-idempotent-retract-assert
Already exists as an accepted belief with the same ID and content. -
IN
api-layer-ensures-atomic-isolated-mutations
The API layer enforces mutation safety through four mechanisms: context-managed load/save, per-function transaction scope, write-flag gating to prevent unintended persistence, and dict-only returns that prevent callers from holding live network references. -
IN
api-list-negative-filters-hallucinated-ids
`list_negative()` discards any node IDs returned by the LLM that don't exist in the database, preventing hallucinated IDs from appearing in results. -
IN
api-mutating-ops-use-before-after-diffing
Mutating operations (`retract_node`, `assert_node`, `what_if_retract`, `what_if_assert`) snapshot all truth values before the operation and diff afterward to classify changes into `went_out`/`went_in` lists. -
IN
api-retract-cascade-is-transitive
`api.retract_node()` propagates OUT to all transitively dependent SL-derived nodes, not just direct children — retracting a root premise retracts the entire downstream chain. -
IN
api-superseded-nodes-excluded-from-gated
`list_gated()` omits nodes that have been superseded via `api.supersede()`, even if they still have active blockers — stale conclusions don't pollute the blocker view. -
IN
api-tests-black-box
Test methodology claim, not a behavioral invariant about the codebase. Developers learn this from reading the tests, not from a belief registry. -
IN
api-tests-cover-subset
Test coverage inventory, not a behavioral claim about the codebase. Coverage gaps are better tracked as project-level issues. -
IN
api-tests-use-real-sqlite
All API tests run against a real SQLite database with FTS5; storage is never mocked, ensuring the API contract includes correct SQL and full-text search behavior. -
IN
api-uses-lazy-imports
Heavy modules (`derive`, `compact`, `export_markdown`, `check_stale`, `import_beliefs`, `import_agent`) are imported inside function bodies in `api.py`, not at module level, to keep the module fast to import for callers that only need a subset of operations. -
IN
api-uses-with-network-context-manager
`api.py` uses a `_with_network` context manager to ensure load-operate-save atomicity for all network mutations. -
IN
api-visible-to-filters-both-result-and-prompt
Already exists as `api-visible-to-filters-both-result-and-prompt` -
IN
apply-dedup-plan-collects-errors-not-raises
`apply_dedup_plan` collects errors into `result["errors"]` rather than raising, allowing partial application — one missing node does not block processing of the remaining dedup plan -
IN
architecture-enforces-structural-and-operational-safety
Architectural safety is enforced along two independent dimensions: structurally, the central network dependency is contained within clean three-layer boundaries preventing cross-layer corruption; operationally, every mutation path is atomic and isolated preventing within-layer partial state — neither dimension alone is sufficient, but together they eliminate both classes of corruption. -
OUT
architecture-has-no-hidden-fragility
The system's architectural safety is robust end-to-end: structural containment via clean layer boundaries and operational atomicity via context-managed mutations leave no hidden consistency hazards across the persistence boundary. -
IN
ask-agentic-loop-is-bounded
The tool-call loop in `ask()` has a maximum iteration count; an LLM that perpetually requests more searches is terminated and the raw response is returned. -
IN
ask-always-returns-string
`ask()` returns a string on every code path — LLM response, raw search results, or fallback; it never raises an exception to the caller. -
IN
ask-dual-requires-sources-db
Calling `ask(..., dual=True)` without providing a `sources_db` path raises `ValueError`. -
IN
ask-falls-back-to-raw-search
When LLM synthesis fails for any reason (timeout, missing CLI, non-zero exit), `ask()` returns the raw FTS5 search results as fallback. -
IN
ask-has-tiered-query-modes
The ask module supports tiered query modes with graceful degradation: full LLM synthesis with a bounded 3-iteration tool loop, no-synth mode that bypasses the LLM entirely, and automatic fallback from LLM failure to raw FTS5 search results. -
IN
ask-is-fault-tolerant-and-bounded
The ask module is fault-tolerant (always returns a string, catches LLM failures, falls back to raw FTS5 search) and execution-bounded (tool loop capped at 3 iterations), ensuring reliable bounded knowledge retrieval regardless of LLM availability. -
OUT
ask-mcp-achieves-accurate-bounded-tool-use
MCP tool integration in ask() achieves both bounded safety (iteration caps, error tolerance, transport timeouts at two layers) and accurate tool discovery (catalog reflects current server capabilities rather than a stale snapshot). -
IN
ask-mcp-errors-non-fatal
When an MCP bridge's `call_tool` raises an exception during the ask loop, `ask()` catches the error, feeds it back to the LLM as context, and continues the tool loop rather than propagating to the caller. -
IN
ask-mcp-integration-is-safely-bounded
MCP tool calls in `ask()` are both error-tolerant (exceptions caught and fed back as context for alternative tool selection) and iteration-bounded (5 tool-call rounds max), preventing both crashes from MCP server failures and runaway tool loops. -
IN
ask-mcp-is-defense-in-depth-bounded
MCP-backed ask queries are bounded at two independent layers: application-level iteration caps with error-tolerant fallback at the ask layer, plus per-call and connection timeouts at the MCP bridge transport layer — no single timeout failure can cause unbounded execution. -
IN
ask-mcp-iteration-limit-is-five
When `mcp_servers` is non-empty, `ask()` allows up to 5 tool-call iterations before forcing a final LLM response (6 total invocations), compared to the lower limit without MCP servers. -
OUT
ask-mcp-tool-use-has-current-catalog
Ask's MCP tool integration achieves full reliability — errors caught, iterations bounded — with the tool catalog always reflecting the MCP server's current capabilities rather than a stale connection-time snapshot. -
IN
ask-natural-mode-strips-metadata-and-cite
When `natural=True`, all belief metadata (`**Status:**`, `### ` headers, `**Source:**`) is stripped from the prompt context and the "Cite belief IDs" instruction is replaced with "plain natural language". -
IN
ask-prompt-no-template-placeholders
`build_ask_prompt` never leaves `{{` or `}}` template markers in the generated prompt string — all placeholders are resolved before output. -
IN
ask-sources-db-failure-silently-degrades
If the `sources_db` SQLite file is missing or corrupt, `_search_source_chunks` catches `OperationalError`/`DatabaseError` and returns empty string, degrading to belief-only mode without user-visible errors. -
IN
ask-stop-word-fallback-ensures-nonempty-query
`_search_source_chunks` strips stop words from the question before building the FTS5 query, but falls back to all words longer than 1 character if every word is a stop word — ensuring the query is never empty -
IN
ask-strips-claudecode-env
`_invoke_claude` removes the `CLAUDECODE` environment variable to prevent recursive invocation when running inside Claude Code. -
IN
ask-tool-loop-capped-at-three
The LLM synthesis loop runs at most `MAX_ITERATIONS` (3) rounds, with `FINAL_TURN_INSTRUCTION` appended on the last iteration to force a final answer. -
IN
ask-uses-text-based-tool-protocol
`ask.py` implements tool use by parsing JSON lines with a `"tool"` key from LLM text output, not Claude's native tool-use API, because it invokes `claude -p` (pipe mode) which doesn't support function calling. -
IN
atomicity-is-backend-independent
Both storage backends enforce atomic isolated operations through backend-appropriate mechanisms: the SQLite backend uses context-managed load/save with write-flag gating and per-function transaction scope, while PostgreSQL uses per-method transactions with composite-key multi-tenancy — achieving the same transactional guarantee at different architectural levels -
IN
auto-retract-respects-dry-run
The `--auto-retract` flag in the `review-beliefs` CLI is gated by `--dry-run`: when dry-run is active, findings are displayed but no database mutation occurs, even for beliefs flagged as invalid. -
OUT
autonomous-convergence-preserves-trust-boundaries
The system simultaneously achieves autonomous self-maintenance (converging to deterministic stable states while actively detecting and resolving inconsistencies) AND comprehensive boundary enforcement (architectural trust through self-containment and information flow control through authorization and budget constraints) — convergence never requires relaxing defensive controls, and boundary enforcement never prevents convergence. -
OUT
autonomous-convergence-produces-documented-equilibria
The system's autonomous convergence to evaluation-invariant equilibria generates consistently identifiable artifacts with deterministic traceable history at every step — every equilibrium state is not merely stable and transformation-invariant but fully explainable through its documented convergence path. -
OUT
backend-agnostic-operational-assurance
The system's comprehensive operational assurance — spanning temporal self-correction, end-to-end reliability, and external control — holds identically across both storage backends with equivalent safety guarantees, provided PgApi achieves full API parity with the SQLite path. -
IN
backtracking-retracts-least-entrenched
`add_nogood` resolves contradictions via dependency-directed backtracking: `find_culprits` traces to premises, scores by `_entrenchment`, and retracts the least-entrenched premise to minimize disruption. -
OUT
belief-currency-is-actively-managed
The system actively manages belief currency bidirectionally: the production-ready derive pipeline safely introduces new beliefs through defensive validation, while the staleness CI gate detects drift in existing beliefs against source material — together preventing both unsafe additions and undetected obsolescence. -
IN
belief-text-truncated-at-200-chars
`format_beliefs_for_contradiction_check` truncates any belief text longer than 200 characters, appending `...` as a suffix, to keep LLM prompts bounded. -
IN
bootstrap-bypasses-incremental-propagation
Both persistence loading and import construct the full node graph before truth maintenance — load trusts stored truth values and skips propagation entirely, while import adds all nodes then propagates via recompute_all — sharing a bulk-construction pattern that avoids per-node incremental propagation. -
OUT
budget-enforcement-is-efficient-across-pipeline
All budget-constrained operations — compact output distillation and derive belief allocation — achieve computationally efficient tracking with representation-safe minimum bounds, ensuring budget enforcement never becomes a performance bottleneck. -
IN
budget-floor-is-five
`_build_beliefs_section` guarantees local beliefs get at least 5 slots regardless of agent count, enforced by `max(5, max_beliefs - count)` -
IN
build-prompt-validates-custom-templates
`build_prompt` raises `ValueError("unknown placeholder")` for unrecognized `{fields}` and `ValueError("malformed braces")` for unclosed braces in custom prompt templates -
IN
build-tools-section-always-includes-search-beliefs
`_build_tools_section` always includes the built-in `search_beliefs` tool in its output regardless of whether MCP bridges are provided — it is the baseline tool present in every ask prompt. -
OUT
canonical-equilibria-are-negation-transparent
The system converges to canonical evaluation-invariant equilibria where negative semantics are fully transparent — the final stable state is determined solely by the logical content of justifications, independent of both the transformation path taken and whether beliefs were established through positive assertion or negative defeat. -
IN
central-dependency-is-safely-contained
Despite `network.py` being imported by virtually every module in the codebase, the three-layer architecture with clean boundaries ensures this central coupling does not create cross-cutting mutation paths — layer separation contains the dependency's blast radius so that the hub topology does not compromise architectural integrity. -
IN
challenge-converts-premises-to-justified
When a premise (node with no justifications) is challenged, it is converted to a justified node with an SL justification containing empty antecedents and the challenge in the outlist. -
IN
challenge-destroys-premise-identity
When a premise is challenged, it loses its defining characteristic: premise identity emerges from absence of justifications, but challenge adds a justification (converting the premise to a justified node), meaning the target's truth value becomes conditional on the challenge node being OUT rather than unconditionally held — challenge reclassifies the target in the node type system. -
IN
challenge-id-auto-generation
Auto-generated challenge IDs follow the pattern `challenge-{target}`, then `challenge-{target}-2`, `-3`, etc.; explicit IDs that collide raise `ValueError` rather than auto-deduplicating. -
IN
challenge-is-outlist-injection
The challenge mechanism creates a new premise node and adds it to the target's outlist; all truth-value changes flow through normal BFS propagation, not direct mutation -
IN
challenge-uses-outlist-mechanism
`challenge` works by creating an IN premise node and adding it to the target's outlist in every justification, reusing the same non-monotonic mechanism as `supersede`. -
IN
check-stale-and-hash-sources-mutate-in-place
Both `check_stale` (with `upgrade_hashes=True`) and `hash_sources` modify `node.source_hash` directly on the Network object; neither persists — the caller must save. -
IN
check-stale-exits-nonzero
`cmd_check_stale` calls `sys.exit(1)` when any stale nodes are found, making it usable as a CI or pre-commit gate. -
IN
check-stale-is-read-only
`check_stale` never mutates the network; it returns a list of stale-node dicts and leaves all nodes unchanged — staleness detection is separated from staleness resolution. -
IN
check-stale-never-raises-on-missing-files
`check_stale()` returns a structured `source_deleted` dict for nodes whose source files don't exist on disk; it never raises `FileNotFoundError` or any exception. -
OUT
check-stale-report-only
Duplicate of existing belief `check-stale-is-read-only`. -
IN
check-stale-result-schema-uniform
All `check_stale` result dicts share the same 6-key schema (`node_id`, `old_hash`, `new_hash`, `source`, `source_path`, `reason`) regardless of reason type, so consumers can iterate results without type-checking. -
IN
check-stale-results-sorted-by-node-id
The list returned by `check_stale` is sorted ascending by `node_id`, providing deterministic output for consumers. -
IN
check-stale-skips-out-nodes
Only nodes with `truth_value == "IN"` are checked for staleness; retracted (OUT) nodes are ignored even if their source file has changed. -
IN
cli-backend-kwargs-controls-storage
`_backend_kwargs(args)` is the single chokepoint that determines whether a command runs against SQLite or PostgreSQL; every `cmd_*` function must spread its return value into the corresponding `api.*` call -
IN
cli-dispatch-is-flat-dict-lookup
CLI dispatch uses a flat `commands` dict mapping subcommand strings to `cmd_*` handler functions — no plugin system or subclass hierarchy. -
IN
cli-errors-use-stderr-success-uses-stdout
CLI error diagnostics are written to stderr and success output to stdout; tests consistently assert on the correct stream. -
IN
cli-exit-1-on-error
All CLI error paths catch specific exceptions from the API (`KeyError`, `ValueError`, `PermissionError`, `FileNotFoundError`), print to stderr, and call `sys.exit(1)`. -
IN
cli-exit-code-contract-is-binary
Every CLI subcommand returns exit code 0 for success and 1 for any user-facing error; no other exit codes are used or tested. -
IN
cli-flags-override-env-vars
`_backend_kwargs` gives precedence to CLI `--pg`/`--project-id` flags over `REASONS_PG_CONNINFO`/`REASONS_PROJECT_ID` environment variables when both are present -
OUT
cli-is-pure-delegation-layer
The CLI is a pure delegation layer: every handler dispatches through a flat dict lookup to API functions with no business logic, producing binary exit codes and correct stream separation — a complete separation of formatting from computation. -
IN
cli-is-pure-formatter
Every cmd_* function delegates to api.* and only formats the returned dict for terminal output; no business logic lives in the CLI layer. -
IN
cli-is-verified-end-to-end
The CLI is verified through hermetic end-to-end integration tests (isolated databases per test, full argv-parsing pipeline, deterministic stream-correct output) — unless cmd_propagate bypasses the API layer, leaving one code path's safety guarantees unverifiable through the standard integration testing harness. -
OUT
cli-is-verified-pure-delegation
The CLI is both structurally pure (every handler delegates to API functions with no business logic) and end-to-end verified (hermetic integration tests confirm delegation produces correct output through the full argv-parsing pipeline). -
IN
cli-plan-review-apply-pattern
Several commands (`derive`, `deduplicate`, `contradictions`) follow a three-phase workflow: (1) generate proposals to a file, (2) human reviews/edits the file, (3) `--accept FILE` parses and applies the reviewed plan — with `--auto` collapsing all three phases -
IN
cli-sqlite-only-commands-exist
Commands `derive`, `ask`, `review-beliefs`, `deduplicate`, and `contradictions` are guarded by `_require_sqlite()` and exit with an error if `--pg` is set — they do not support PostgreSQL -
IN
cli-tests-are-black-box-integration
All CLI tests invoke `main()` through the full argv-parsing pipeline via the `run_cli` harness rather than calling internal APIs, with one exception (`TestPropagateWithChanges` directly mutates storage to create inconsistent state). -
IN
cli-uses-lazy-imports-for-heavy-modules
`asyncio`, `derive`, `ask`, and `Storage` are imported inside function bodies rather than at module level, keeping `reasons --help` fast. -
OUT
closed-loop-is-origin-agnostic
The minimality-sustained closed maintenance loop operates identically across all belief origins — external beliefs achieve full integration parity within the same forward-computation and backward-revision cycle as internally-derived beliefs, making the maintenance loop source-agnostic. -
IN
cluster-and-sample-are-mutually-exclusive
The `--cluster` and `--sample` flags in `cmd_derive` are mutually exclusive, enforced with an explicit check and `sys.exit(1)` — they represent competing strategies for belief subset selection. -
IN
cluster-auto-k-heuristic
Auto cluster count is computed as `len(beliefs) // 5`, clamped between 2 and `min(budget // 3, 20)`, targeting approximately 5 beliefs per cluster with at least 3 beliefs per cluster given the budget. -
IN
cluster-beliefs-respects-budget
`cluster_beliefs` never returns more IDs than the `budget` parameter; each cluster's allocation is individually capped by `min(alloc, len(members))`. -
IN
cluster-beliefs-returns-exact-budget
`cluster_beliefs` returns exactly `budget` belief IDs when the input set is larger than the budget, and all items when the input set is smaller. -
IN
cluster-cache-keys-include-content-hash
`ClusterCache` keys embeddings by `(node_id, sha256_prefix)`, so editing a belief's text with the same ID forces re-embedding rather than serving stale vectors. -
IN
cluster-cache-no-recompute
`ClusterCache.embed()` does not recompute embeddings for previously cached belief texts; cache size stays constant on repeated calls with the same input and grows by exactly the count of new texts on superset calls. -
IN
cluster-deps-are-optional
`sentence-transformers` and `scikit-learn` are optional dependencies behind a `HAS_CLUSTER_DEPS` gate; the module degrades to a clear `ImportError` with install instructions when they are absent. -
IN
cluster-deps-optional-with-graceful-skip
The clustering module (`reasons_lib.cluster`) is behind an optional `[cluster]` install extra; when `sentence-transformers` or `scikit-learn` are missing, `_require_cluster_deps` raises `ImportError` and all dependent tests skip cleanly. -
IN
cluster-remainder-favors-largest
When the budget doesn't divide evenly across clusters, extra slots are distributed one-per-cluster to the largest clusters first via descending size sort. -
IN
cluster-skips-ml-when-under-budget
When the number of beliefs is less than or equal to the budget, all ML work (embedding, clustering, sampling) is skipped and every belief is returned directly. -
IN
cluster-stats-sizes-sum-to-input
The `cluster_sizes` list in the stats dict returned by `cluster_beliefs` always sums to the total number of input beliefs, enforcing that every belief is assigned to exactly one cluster. -
OUT
cmd-propagate-bypasses-api
`cmd_propagate` is the only CLI handler that bypasses `api.py`, going directly to `Storage` → `Network.recompute_all()` → `Storage.save()` — a design inconsistency in the otherwise pure-presentation CLI layer. -
IN
colon-means-already-namespaced
`_resolve_namespace` treats a colon in a node ID as "already namespaced" and never double-prefixes; this is the convention for cross-namespace references. -
IN
commands-dict-must-mirror-subparsers
Adding a CLI subcommand requires entries in both the argparse subparser definitions and the `commands` dispatch dict in `main()`; omitting either silently breaks the command. -
IN
completeness-and-minimality-are-unified
The reasoning-and-revision architecture achieves completeness through minimality rather than despite it — both forward truth computation and backward belief revision derive from the same small set of primitives (outlist, disjunctive truth, vacuous validity), so completeness requires no feature accumulation beyond what minimality already provides. -
IN
completeness-determinism-and-minimality-are-unified
The reasoning-and-revision architecture achieves completeness through minimality, and that same minimality produces operational determinism — completeness and determinism are not independently established but co-derived from shared minimal foundations: uniform outlist primitives simultaneously enable complete revision coverage and deterministic evaluation, revealing a single architectural root for both properties. -
OUT
context-agnosticism-follows-from-minimality
Context-agnostic evaluation — producing identical results regardless of evaluation timing, attachment history, or structural origin — is a consequence of semantic minimality with operational determinism: because truth evaluation derives from uniform minimal rules with deterministic pure evaluation, it naturally cannot distinguish between contexts. -
IN
contradiction-dry-run-overrides-auto-apply
When both `--dry-run` and `--auto-apply` are passed to the `contradictions` CLI subcommand, no nogoods are recorded in the database (applied count is 0). -
IN
contradiction-in-only-filter
`detect_contradictions` excludes OUT nodes from LLM prompts even when explicitly listed in `belief_ids`. -
IN
contradiction-min-two-claims
`parse_contradiction_response` drops any nogood with fewer than 2 valid claim IDs; this is enforced both before and after `valid_ids` filtering. -
IN
contradiction-plan-round-trips-apply-entries
Writing a contradiction plan with `write_contradiction_plan` and parsing it back with `parse_contradiction_plan` preserves all `[APPLY]`-tagged NOGOOD entries with their IDs and claims, while discarding `[SKIP]`-tagged entries — enabling a human review workflow. -
IN
contradiction-resolution-is-minimal-disruption
The nogood resolution system minimizes network disruption through layered heuristics: the primary path traces justification chains back to premises and selects the least-entrenched for retraction, the fallback uses dependent count when no traceable chain exists, and all contradictions are unconditionally recorded regardless of resolution outcome. -
IN
contradiction-resolution-is-traceable-and-recoverable
Contradiction resolution provides complete operational support along two independent dimensions: it minimizes disruption with guided recovery (least-entrenched culprit selection plus surgical restoration hints for cascade victims), AND creates consistently identifiable artifacts (nogoods with durable collision-free IDs, challenge nodes with deterministic auto-IDs), enabling both forensic root-cause analysis and practical guided recovery. -
IN
contradiction-resolution-minimizes-disruption-and-guides-recovery
Contradiction resolution achieves both minimal impact and guided recovery: dependency-directed backtracking selects the least-entrenched culprit premise to minimize the retraction cascade, while restoration hints identify specific cascade victims that have surviving alternative premises — providing a complete resolve-and-recover pipeline -
IN
contradictions-belief-text-truncated-at-200-chars
`format_beliefs_for_contradiction_check` truncates each belief's text at 200 characters in the LLM prompt to avoid blowing context windows on large belief descriptions -
IN
contradictions-cross-batch-pairs-undetected
Batch boundaries are non-overlapping — each belief appears in exactly one batch per run — so contradictions between beliefs in different batches cannot be detected in a single run. -
IN
contradictions-min-two-claims-per-nogood
The contradiction parser enforces that every returned nogood has at least 2 valid claim IDs; single-claim or empty results are silently dropped. -
IN
contradictions-no-storage-dependency
`contradictions.py` operates on in-memory node dicts and has no import of `storage.py` or any database layer, making it testable in full isolation. -
IN
contradictions-only-checks-in-beliefs
`detect_contradictions` filters all input to `truth_value == "IN"` before processing; OUT beliefs are never sent to the LLM for contradiction checking. -
IN
contradictions-semantic-skips-singleton-clusters
`detect_contradictions_semantic` skips any cluster containing fewer than 2 beliefs, since no pairwise contradiction is possible within a singleton -
IN
contradictions-shuffles-before-batching
The `contradictions` command randomly shuffles all IN beliefs before partitioning into batches of 50, ensuring that repeated runs probabilistically cover cross-belief comparisons that fixed sequential batching would never surface. -
OUT
convergence-produces-evaluation-invariant-equilibria
The system converges to equilibrium states where truth evaluation is transformation-invariant: regardless of the mutation path taken — order of additions, retractions, challenges, imports — the converged state evaluates all beliefs identically, because autonomous convergence reaches deterministic stable states and truth evaluation is agnostic to both temporal context and structural origin. -
OUT
convergence-trajectories-are-permanently-documented
Every convergence trajectory toward an evaluation-invariant equilibrium — deterministic in path and consistently identifiable in its artifacts — is backed by a permanent, comprehensive audit trail covering all self-corrections along that trajectory, ensuring complete retrospective analysis of how the system reached any given stable state -
OUT
convergent-equilibria-are-documented-and-indefinitely-auditable
The system's convergent equilibria are simultaneously trajectory-documented (every path to equilibrium generates deterministic identifiable artifacts with negation-transparent final states) and indefinitely auditable (every invariant in the equilibrium state is independently verifiable without temporal degradation), providing complete operational transparency across both the convergence journey and the resulting stable state. -
IN
convert-to-premise-preserves-dependents-invariant
Converting a derived node to a premise correctly maintains the dependents index by removing the node from former antecedents' dependents sets — the same invariant maintained by every other network mutation. -
IN
convert-to-premise-removes-dependents
When a derived node is converted to a premise via `convert_to_premise`, it is removed from its former antecedents' `dependents` sets because the old justification edges are deleted. -
IN
count-accumulates-linearly
Documents the bug fix for issue #23 — already covered by existing `derive-agent-count-bug` which tracks this defect -
IN
cp-and-sl-evaluated-identically
CP and SL justifications use the same validity check in `_justification_valid`; the distinction is semantic (support vs. consistency), not computational. -
IN
cp-equals-sl
CP (conditional-proof) justifications are evaluated with the exact same logic as SL justifications, despite being a distinct type in Doyle's TMS — either an intentional simplification or incomplete implementation -
OUT
critical-operations-converge-to-fixed-points
The system's three critical reconciliation operations are all convergent: agent sync produces no changes on re-run with identical input, dependents index rebuilding yields identical results on repeated execution, and truth recomputation iterates to a fixpoint — ensuring the system reaches stable consistent state regardless of operation ordering. -
IN
dangling-dependent-guard-skips-missing-nodes
`_propagate` skips dependent IDs not present in `net.nodes` rather than raising `KeyError`, and emits a structured warning log entry for each (fix for issue #22). -
IN
dangling-dependents-are-safely-contained
Dangling dependent references are safely contained across all propagation dimensions: BFS skips missing nodes with structured warnings, the changed set never includes ghost IDs, and the visited set excludes dangling IDs so later-created nodes propagate normally. -
IN
dangling-dependents-log-not-crash
Covered by existing `propagate-assumes-dependents-exist` (documents the assumption) and `tms-core-is-crash-safe` (documents crash safety) -
IN
dangling-guard-is-continue-not-raise
When `_propagate` encounters a dependent ID not in `self.nodes`, it logs a structured warning and continues the BFS loop rather than raising `KeyError` or silently skipping. -
IN
dangling-ids-excluded-from-changed
The `changed` set returned by `retract()` and `assert_node()` never contains IDs that don't correspond to real nodes in the network. -
IN
dangling-ids-excluded-from-visited
The propagation visited set does not include dangling IDs, so a formerly-dangling ID that becomes a real node will propagate correctly on subsequent operations. -
IN
dangling-refs-excluded-from-changed-set
The `changed` set returned by `retract`/`assert_node` never contains node IDs that don't exist in the network. -
IN
dangling-refs-excluded-from-visited-set
Dangling dependent IDs are not added to the propagation visited set, so later-created nodes with the same ID propagate normally. -
IN
data-model-uses-string-enums
Both `Justification.type` ("SL"/"CP") and `Node.truth_value` ("IN"/"OUT") are plain strings, not Python enums; consumers must validate values themselves as invalid states like `"MAYBE"` or `"XYZ"` are representable. -
IN
dedup-auto-keeps-most-dependents
In auto mode, `deduplicate` retains the cluster member with the most dependents and retracts all others -
IN
dedup-keeps-most-connected-node
In auto-dedup mode, the node with the most dependents survives each cluster; ties break by lexicographic ID, and losers are retracted after dependents are rewired. -
IN
dedup-plan-is-user-editable
The dedup plan format uses KEEP/RETRACT markers that users can swap before applying, making deduplication decisions reviewable and overridable -
IN
dedup-rewrites-both-antecedents-and-outlist
When a duplicate is retracted via dedup, all justification references (both antecedent and outlist) across the network are rewritten to point at the kept node -
IN
defeat-reversal-is-automatic-with-guided-recovery
All outlist-based defeat mechanisms (challenge, kill-switch, supersession) not only reverse automatically through BFS propagation cascades — recovering all transitively dependent nodes — but also provide surgical recovery guidance through restoration hints that target cascade victims with surviving premises, enabling both automatic and manual recovery paths. -
IN
defeat-reversal-propagates-automatically
All outlist-based defeat mechanisms (challenge, kill-switch, supersession) not only reverse in principle but propagate recovery automatically through safe terminating BFS — when a defeating node is retracted, the outlist entry becomes satisfied, and propagation cascades truth-value restoration to all affected nodes without manual re-assertion -
IN
defeat-reversal-with-guided-recovery
All defeat mechanisms (challenge, kill-switch, supersession) are reversible through outlist semantics, and the system provides surgical restoration hints for cascade victims with viable recovery paths — enabling guided recovery from retraction cascades where multi-premise justifications have surviving premises. -
IN
defend-is-challenge-of-challenge
`defend` works by calling `challenge` on the challenge node itself, creating a recursive dialectical structure where truth values resolve automatically through the same outlist mechanism. -
IN
defend-is-recursive-challenge
Defense is implemented by calling `challenge()` on the challenge node itself, enabling arbitrarily deep dialectical chains using the same outlist mechanism recursively with no special-case code -
OUT
defense-in-depth-is-resource-efficient
The system's defense-in-depth across LLM and system boundaries — layered defenses including bounded execution, fail-soft error handling, process isolation, and referential integrity validation — achieves comprehensive protection within the same resource-efficient pipeline that spans packaging, startup, and runtime with zero external dependencies and lazy loading. -
IN
dependency-completeness-enables-accurate-dedup
Complete dependency tracking for all reference types — both antecedent and outlist entries maintained eagerly by every network mutation — ensures deduplication accurately reflects the complete network topology: survivor selection considers all incoming dependencies including outlist references, and reference rewiring targets both antecedent and outlist positions across all justifications, preventing dedup from creating dangling references or miscounting dependents. -
OUT
dependents-bidirectional-index
Each node maintains a `dependents` set (reverse of antecedent/outlist edges), eagerly maintained by `add_node`, `add_justification`, `supersede`, `challenge`, and `convert_to_premise`. -
OUT
dependents-index-derived-on-load
The `node.dependents` set is never persisted to SQLite; it is rebuilt by walking all justification antecedents and outlists during `load()`. -
OUT
dependents-index-is-fragile-denormalization
The dependents set is a manually-maintained denormalized reverse index that is never persisted and must be rebuilt on every load, creating a consistency obligation on all mutation paths -
OUT
dependents-is-manual-reverse-index
`Node.dependents` is a denormalized reverse pointer set that must be kept in sync by external code (primarily `network.py`); nothing in the data model enforces consistency. -
IN
dependents-survive-storage-roundtrip
After `Storage.save()` followed by `Storage.load()`, the loaded network's dependents index passes `verify_dependents()` with no errors. -
OUT
derived-belief-pipeline-achieves-code-enforced-quality
The derived belief pipeline — creation via defensive derivation with structural validation, Jaccard retraction guards, and environment isolation, followed by independent review with scope restricted to derived beliefs and mutation gated behind dry-run — achieves completely code-enforced quality assurance including logical soundness validation, only when inference soundness checking is implemented in code rather than relying solely on LLM prompt instructions. -
IN
dialectical-defeat-is-reversible-but-identity-is-permanent
The dialectical system exhibits a fundamental asymmetry between defeat and identity: the truth-value defeat caused by a challenge is fully reversible (defending or retracting the challenge node restores IN status via outlist semantics), but the premise-to-justified identity transformation is permanent — a challenged premise can never return to unjustified status because the added justification cannot be removed, only defeated. -
IN
dialectical-structure-is-recursive-outlist
The entire challenge/defend dialectical system is implemented as recursive outlist injection with no dedicated dialectical machinery -
IN
dialectical-transformation-is-fully-reliable
The irreversible premise-to-justified transformation during challenge is both semantics-preserving (the resulting node inherits complete outlist evaluation with conjunction, absence, and persistence semantics) and crash-safe (recursive dialectical chains terminate deterministically), making dialectical operations reliable despite their irreversibility. -
IN
dialectical-transformation-preserves-semantics
Challenging a premise irreversibly transforms its identity from unjustified to justified node, but the resulting dialectical structure inherits complete outlist semantics — conjunction over multiple outlists, absence-as-OUT permissiveness, and persistence survival — ensuring the transformation preserves well-defined evaluable behavior. -
IN
dialectics-achieve-forward-reliability-and-backward-recovery
Dialectical operations achieve complete bidirectional assurance: forward activation is deterministic, reliable, and semantically complete (challenge/defend evaluated uniformly with controlled irreversibility), while backward reversal is topology-complete with surgical guided recovery (recovery reaches all transitively dependent nodes, hints target only cascade victims with surviving premises) — the full dialectical cycle from engagement through resolution is assured in both directions. -
OUT
dialectics-are-atomic-and-transparent
Challenge/defend dialectics are both semantically transparent (indistinguishable from ordinary beliefs, evaluated by uniform outlist rules) and atomically safe (mutations follow the same context-managed load/save pipeline as all other operations), requiring no special transaction handling. -
IN
dialectics-are-dually-grounded-by-purity-and-uniformity
Dialectical operations achieve dual semantic grounding from independent sources: evaluation purity (uniform, deterministic, side-effect-free validity checking) enables richly-governed exception-safe dialectics, while uniform edge-case semantics transitively ground deterministic reliable dialectics through complete negative semantics — together ensuring dialectics are both governable and semantically well-founded from first principles. -
IN
dialectics-are-semantically-transparent
Challenge/defend dialectics are semantically indistinguishable from ordinary beliefs: they inherit fully-specified outlist semantics (conjunction, absence-as-OUT, persistence) and are evaluated by the same uniform pure rules that govern all truth maintenance — no dialectical special cases exist anywhere in the engine. -
IN
direct-access-raises-list-access-filters
API functions for single-node access (`show_node`, `explain_node`, `trace_assumptions`) raise `PermissionError` on forbidden nodes, while list/export functions (`list_nodes`, `search`, `export_network`) silently exclude them from results. -
IN
dual-quality-gates-are-complementary-and-non-mutating
The system enforces belief quality through dual non-mutating gates targeting complementary validity dimensions: review validates logical soundness of derived beliefs (scoped to justified nodes, dry-run gated auto-retraction), while staleness checking validates source currency of all IN beliefs (conservative CI gate with nonzero exit on drift) — neither gate can corrupt network state. -
IN
dual-storage-backends-are-interchangeable
Both SQLite and PostgreSQL backends can be used interchangeably for any operation, with identical safety guarantees through backend-appropriate mechanisms and complete API surface coverage, so applications can switch backends without behavioral changes. -
IN
duplicate-node-id-raises-valueerror
`api.add_node()` raises `ValueError` when given a node ID that already exists in the network — node IDs are unique. -
IN
each-cli-test-creates-isolated-db
Every CLI test method initializes a fresh SQLite database via `run_cli("init")` in a pytest `tmp_path`, ensuring zero shared state between tests. -
IN
edge-case-uniformity-follows-from-minimality
Uniform handling of all semantic edge cases — vacuous premises, asymmetric absence, empty antecedents — is a consequence of semantic minimality: because every edge case derives from the same primitives that drive deterministic core semantics, no special-case logic exists. -
IN
empty-antecedents-vacuously-valid
An SL justification with an empty antecedent list is valid (vacuous truth via `all([])`), allowing outlist-only justifications to function as "IN unless Y" — used by `challenge` and `supersede` for converted premises -
IN
entry-point-mapping
The `reasons` CLI command maps to `reasons_lib.cli:main` via `[project.scripts]` in `pyproject.toml` and is the only registered script entry point. -
OUT
equilibria-are-transparent-and-trajectory-documented
The system's convergent equilibria are simultaneously negation-transparent (the final stable state is uniquely determined by evaluation rules with complete propagation fidelity) and trajectory-documented (every convergence path generates deterministic traceable events backed by permanent durable audit trails) — convergence is not just mathematically guaranteed but operationally verifiable. -
IN
estimate-tokens-chars-div-4
`estimate_tokens` uses `len(text) // 4` with a minimum return value of 1; it never returns 0, even for empty strings. -
OUT
evaluation-is-traceable-and-context-agnostic
Every truth evaluation is simultaneously context-agnostic (producing identical results regardless of evaluation timing, attachment history, or belief origin) and fully traceable (every state change follows a deterministic path recorded in the system's operational history), enabling both reproducibility and post-hoc explanation of any evaluation outcome. -
IN
evaluation-is-uniformly-context-and-origin-agnostic
Truth evaluation produces identical results regardless of both attachment history (when/how a justification was added) and structural origin (ordinary belief vs. dialectical construct) — no belief receives special treatment based on provenance, timing, or role in the network. -
IN
evaluation-purity-grounds-agnosticism-and-minimality
Evaluation purity (uniform, deterministic, no metadata inspection) independently grounds both context-agnosticism (identical results regardless of timing/origin) and semantic minimality (no special-case logic), making them co-occurring consequences of the same architectural choice rather than causally related. -
IN
evaluation-purity-grounds-dialectics-through-minimal-architecture
Evaluation purity — uniform, deterministic, side-effect-free justification validity checking — enables the complete minimal architecture whose negative semantics ground deterministic dialectics, establishing a causal chain from the most fundamental computational property through architectural completeness to dialectical reliability. -
OUT
evaluation-traceability-persists-through-equilibria
Every truth evaluation is traceable and context-agnostic from individual computation through system-wide convergence: all structural transformations converge to documented equilibria with deterministic identifiable artifacts, and every evaluation along those convergence trajectories is deterministically reproducible regardless of timing or origin. -
OUT
every-mutation-reports-its-effects
All mutating operations report their effects as structured data: retract returns the full changed set, add_justification returns a change dict with old/new truth values, and API mutating operations use before/after truth-value diffing to capture deltas. -
IN
every-network-mutation-maintains-dependents
After any public mutation method on `Network` (`add_node`, `retract`, `assert_node`, `add_justification`, `supersede`, `challenge`, `defend`, `convert_to_premise`, `add_nogood`, `summarize`), `verify_dependents()` returns an empty list. -
IN
every-network-mutation-maintains-dependents-invariant
After any public Network mutation (add_node, retract, assert_node, add_justification, supersede, challenge, defend, convert_to_premise, add_nogood, summarize), the dependents index passes `verify_dependents()` — completeness and minimality are maintained incrementally -
IN
exhaust-implies-auto
In `_derive_one_round`, proposals are auto-applied when either `args.auto` or `args.exhaust` is true; the `--exhaust` flag does not require the user to also pass `--auto`. -
OUT
exhaustive-knowledge-expansion-within-controlled-boundaries
The system achieves exhaustive knowledge expansion — deterministic reversible reasoning combined with complete LLM-driven derivation with guaranteed termination — within multi-level information boundaries that gate authorization, constrain output size, and defensively validate all ingested beliefs, ensuring unbounded knowledge growth never escapes system controls. -
IN
expert-pipeline-extracts-per-document
The expert-agent-builder pipeline extracts beliefs per-document (summarize entire document → propose beliefs → record file path), not per-section — the connection between a belief and its source material is a file-level pointer, not a section-level one. -
IN
export-markdown-pg-reconstructs-network
The `export_markdown` PostgreSQL path reconstructs a full `Network` object from `export_network()` output — creating Node/Justification objects and wiring the dependents index — because the markdown exporter requires a wired dependency graph, not a flat dict. -
OUT
extensions-compose-transparently-on-core
Both extension systems — dialectical challenge/defend and multi-agent federation — compose transparently on the core TMS because each is evaluated by uniform outlist rules, propagated deterministically, reversed by the same primitive, and isolated from the other's namespace. -
IN
extract-tool-call-returns-first-match
When LLM output contains multiple JSON objects with a `"tool"` key, `extract_tool_call()` returns only the first valid match and ignores the rest; malformed JSON lines are silently skipped. -
IN
extras-map-one-to-one-to-modules
Each optional dependency group maps 1:1 to a specific module: `pg` extra gates `reasons_lib/pg.py`, `cluster` extra gates `reasons_lib/cluster.py`, and `test-pg` is a superset combining `pg` and `test`. -
OUT
format-resilient-boundaries-enforce-validated-trust
All system boundaries simultaneously tolerate format variation at every level — from LLM response parsing through schema migration to derive output versioning — while enforcing strict validated trust through typed exceptions, referential integrity checks, and hallucination filtering. -
IN
ftl-reasons-zero-runtime-deps
The core `reasons_lib` package has no mandatory runtime dependencies — all external packages (psycopg, sentence-transformers, scikit-learn, mcp) are gated behind optional install extras (`[pg]`, `[cluster]`, `[mcp]`). -
IN
fts-errors-silently-caught-in-search
FTS5 query errors in `_fts_search` are silently caught and return an empty list, falling back to substring matching — the only place in the API where errors are deliberately swallowed (FTS5 table may not exist). -
IN
fts-progressive-relaxation
When a multi-term FTS query returns no results, the search engine progressively drops terms until it finds matches or exhausts all subsets. -
IN
fts-relaxation-bounded
Progressive FTS query relaxation is bounded: a 20-term query produces at most 51 `_fts_query` invocations, preventing unbounded search expansion on long input queries. -
IN
fts-relaxation-budget-caps-at-50
Progressive FTS5 search relaxation — dropping query terms via combinations when the full-term query returns no results — is capped at `_MAX_RELAXATION_QUERIES` (50) to prevent combinatorial blowup on many-term queries. -
IN
fts-relaxation-capped-at-fifty-queries
`_fts_search` caps progressive term relaxation at 50 FTS5 queries to prevent combinatorial explosion on long search inputs, dropping terms one at a time via `combinations` down to `len(terms) // 2`. -
IN
fts-stop-words-filtered-before-query
FTS queries filter a `_STOP_WORDS` frozenset before querying FTS5; if all terms are stop words, the search falls back to terms longer than 1 character. -
OUT
full-user-stack-is-verified-atomic-delegation
The full user-facing stack forms a verified chain of atomic delegation: the CLI is structurally pure delegation verified through hermetic integration tests, and every mutation flowing through the API layer is atomic, audited, and produces observable before/after diffs — ensuring end-to-end traceability from user command to persisted state change. -
OUT
fully-characterized-loop-sustains-indefinitely
The fully characterized self-maintaining loop — origin-agnostic, fully observable, and minimality-sustained — can operate without temporal bound because its self-correction is resource-sustainable within a deterministic, structurally sound lifecycle; characterization completeness combined with resource sustainability yields indefinite operability. -
OUT
growth-preserves-universal-assurance
The system grows its knowledge base exhaustively — through deterministic reasoning and LLM-driven derivation with guaranteed termination — while simultaneously maintaining universal multidimensional operational assurance spanning temporal self-correction, end-to-end reliability, and information flow control; growth never compromises any assurance dimension. -
IN
hash-file-full-sha256
`hash_file` returns a full 64-character hex SHA-256 digest (per the fix in PR #40 that removed the earlier `[:16]` truncation). -
IN
hash-sources-idempotent-without-force
`hash_sources` with default `force=False` skips nodes that already have a `source_hash`, making repeated backfill calls safe; `force=True` rehashes unconditionally. -
IN
hash-sources-is-additive-by-default
`hash_sources` without `force=True` never overwrites an existing non-empty `source_hash`; it only backfills nodes with empty or missing hashes. -
IN
hash-sources-mutates-network
`hash_sources` writes directly to `node.source_hash` on the in-memory network, while `check_stale` is purely read-only — an intentional asymmetry. -
IN
hash-sources-no-overwrite-default
`hash_sources` with `force=False` (the default) only backfills missing hashes and will never modify a node that already has a `source_hash` value. -
OUT
hash-truncation-is-16-hex
Source hashes are SHA-256 truncated to the first 16 hex characters (64 bits), reducing collision resistance to ~32 bits for birthday attacks compared to the full 256-bit hash. -
IN
hints-exclude-directly-retracted-node
The node passed to `retract_node` never appears in the `restoration_hints` list — only cascade victims with surviving premises do. -
OUT
identity-transformation-is-semantically-invisible
Challenge creates an irreversible structural transformation (premise → justified node), yet the resulting dialectical structure receives identical evaluation to any other belief — the permanent identity change has no lasting semantic consequence because evaluation is uniformly origin-agnostic and context-independent. -
OUT
information-flow-is-authorization-and-budget-controlled
Information flow from the belief network is controlled along two independent dimensions: access tags gate which beliefs are visible to each caller (authorization control via transitive subset checks), while token budgets constrain how much of the visible network is emitted (volume control via priority-ordered truncation). -
OUT
information-flow-is-controlled-in-both-directions
Information flow is controlled at every system boundary: inbound data passes through production-hardened LLM integration (bounded execution, fail-soft handling, process isolation) and boundary-controlled information isolation (access tags, namespace partitioning), while outbound data is deterministic, authorized via transitive subset-gated access control, and budget-constrained — no uncontrolled data enters or leaves the belief network -
OUT
information-pipeline-is-resource-governed-and-access-controlled
The complete information pipeline is governed along two orthogonal axes: token budgets accurately constrain both input (proportional derive allocation) and output (compact distillation with budget enforcement), while access tags enforce transitive subset-based authorization at every read boundary — every piece of information is simultaneously resource-bounded and access-controlled. -
IN
init-db-refuses-existing-without-force
`api.init_db()` raises `FileExistsError` when the database file already exists, unless `force=True` is passed to allow overwrite. -
IN
init-is-pure-data-model
`reasons_lib/__init__.py` contains only dataclass definitions (`Node`, `Justification`, `Nogood`) with no behavior, validation, or I/O; it imports nothing from the project and sits at the bottom of the import graph. -
IN
initialization-and-reconciliation-converge-equivalently
Both initialization paths (stored-state bootstrap trusting persisted values, and deterministic reasoning computing from scratch) and reconciliation operations (dual import/sync modes with heterogeneous truth state handling) converge to equivalent correct belief states — the system reaches the same outcome regardless of how or when beliefs enter the network. -
IN
initialization-is-path-independent
Whether beliefs enter through stored-state bootstrap (load trusts stored truth values, import builds nodes before recompute_all) or through the deterministic reasoning engine (uniform pure evaluation with guaranteed termination), the system reaches correct truth states — bootstrap trusts values that were originally computed by the same deterministic engine, and import re-derives them via recompute_all, establishing path independence of initialization. -
IN
inspection-outputs-are-uniformly-normalized
Both inspection mechanisms — belief review and staleness checking — produce normalized, schema-consistent, fail-safe output with deterministic structure suitable for automated consumption. -
OUT
integrity-and-scalability-are-complementary
The system achieves comprehensive integrity (unified across all internal mutations and external belief ingestion) and sound multi-agent scalability (isolated namespaces, minimal primitives, deterministic propagation) simultaneously — these properties reinforce rather than trade off against each other because both derive from the same uniform evaluation rules. -
OUT
integrity-is-an-emergent-consequence-of-minimality
End-to-end integrity across all mutation paths and architectural boundaries is not an independently-achieved property requiring separate enforcement — it falls out of minimality as another emergent consequence, because uniform primitive evaluation leaves no gaps for inconsistency to enter. -
OUT
invariant-preservation-is-architecturally-grounded
The complete reasoning-and-revision architecture preserves invariants through minimal foundations not in a vacuum but atop concrete architectural safety — three-layer containment and atomic mutations provide the structural substrate within which minimal invariant preservation operates. -
OUT
invariant-preservation-is-comprehensive
System invariants are comprehensively preserved through two complementary mechanisms: the closed revision/lifecycle loop ensures temporal coverage across forward computation and backward revision, while dual structural/dynamic enforcement provides orthogonal protection through architectural grounding and minimality-enforced self-correction. -
OUT
invariant-preservation-is-total
Invariant preservation is both comprehensive in scope (spanning revision loops, lifecycle management, and structural/dynamic enforcement) and grounded across all independent dimensions (origin, time, and structure), achieving total invariant coverage with no gaps in either what is preserved or where preservation holds. -
OUT
invariants-are-origin-time-and-structurally-grounded
System invariants are anchored along three dimensions: they hold across all belief origins and through time (comprehensive scope), and they are grounded in the concrete architecture's clean layer boundaries and atomic operations (structural foundation) — the invariants are both broad in what they cover and deep in how they are enforced. -
OUT
invariants-are-structurally-and-dynamically-preserved
System invariants are preserved through two complementary layers: architectural grounding provides structural enforcement via clean layer boundaries and atomic mutations, while minimality-enforced self-correction actively detects and resolves violations through contradiction resolution and staleness detection. -
OUT
invariants-hold-across-origin-and-time
System invariants hold along every independent dimension: across all belief origins (human-initiated, LLM-derived, agent-imported) via shared minimal foundations, and across both temporal phases (creation-time edge-case handling and maintenance-time staleness detection) including all semantic edge cases. -
IN
invoke-claude-raises-when-binary-missing
`_invoke_claude()` raises `FileNotFoundError` if the `claude` CLI is not on `PATH`, rather than returning an error string — this is the one exception that `ask()` does not catch internally. -
IN
invoke-model-strips-claudecode-env
invoke_model() in llm.py strips the CLAUDECODE environment variable before all subprocess.run() calls, preventing recursive Claude Code entry from any module that uses the shared LLM interface (ask, derive, review). -
IN
issue-121-evolution-tolerance-audit
Issue #121: Audit evolution tolerance at all system boundaries — not all boundaries have documented forward-compatibility mechanisms -
IN
issue-122-review-fault-tolerance-audit
Issue #122: Audit review module for unhandled failure modes — three specific handlers do not establish coverage of all failure modes -
IN
issue-123-resource-footprint-audit
Issue #123: Audit resource footprint across all lifecycle phases — only deployment and startup phases are currently evidenced -
IN
issue-126-reference-validation-audit
Issue #126: Audit all node ID reference boundaries for validation — three specific boundaries do not establish coverage of every boundary -
IN
jaccard-tokenizer-splits-on-hyphens-and-colons
`_tokenize_id` splits belief IDs on hyphens and colons into token sets for Jaccard similarity comparison, so `foo-bar-baz` and `foo-bar-qux` share 2/4 tokens (0.5 similarity) -
IN
justification-addition-is-robust-across-graph-states
Adding a justification to an existing node achieves fully consistent multi-dimensional propagation — truth values, dependents index, and access tags — even when the dependency graph contains dangling references or lifecycle-marked nodes, because propagation safely handles both graph anomalies and node lifecycle states. -
IN
justification-evaluation-is-context-independent
Justification evaluation produces identical results regardless of evaluation context: it is pure (no side effects), uniform across types (SL/CP use the same validity rule), and temporally invariant (attaching a justification at creation or later yields the same truth outcome) — making truth computation fully context-free -
IN
justification-evaluation-is-uniform-and-pure
All justification types (SL and CP) use the same validity rule (antecedents IN, outlist OUT), evaluated as a pure function with no side effects -
IN
justification-order-preserved-by-rowid
Already exists as `justification-order-preserved-via-rowid` -
IN
justification-order-preserved-via-rowid
Justification insertion order is preserved across save/load cycles using `AUTOINCREMENT` rowid and `ORDER BY rowid` on read, which matters because justification priority affects truth maintenance. -
IN
justification-timing-is-irrelevant-to-evaluation
Justification evaluation produces identical truth semantics regardless of when a justification is attached: add_node evaluates justifications immediately at insertion, and add_justification triggers identical propagation when attached post-creation — the system has no time-of-attachment sensitivity. -
IN
justification-valid-is-pure
`_justification_valid` is a pure query with no side effects, no logging, and no mutations to network state -
IN
justification-validity-requires-inlist-in-and-outlist-out
A justification is valid iff all antecedents are IN and all outlist nodes are OUT; this single rule drives retraction cascades, kill-switch behavior, challenges, and supersession -
IN
kill-switch-cascade-is-reversible
Retracting `agent:active` cascades all agent beliefs to OUT via the inactive relay flipping IN; re-asserting `agent:active` reverses the cascade, restoring all beliefs to IN via the same BFS propagation -
IN
kill-switch-uses-outlist-not-antecedent
The `agent:inactive` node is placed in each imported belief's outlist (not antecedents) so that retracting `agent:active` cascades all imported beliefs to OUT, while per-belief retraction still works independently -
OUT
knowledge-equilibria-are-fully-characterized
Knowledge revision converges to equilibria that are simultaneously self-sustaining through minimality's fixed-point, invariant-preserving across all belief types, correction-convergent with complete dispute resolution fidelity, and topology-accurate with verified dependency propagation — the complete set of equilibrium properties. -
OUT
knowledge-expansion-is-exhaustive-within-hardened-boundaries
Exhaustive knowledge expansion — deterministic reversible reasoning combined with complete LLM-driven derivation with guaranteed termination — is achieved through production-hardened LLM integration operating within controlled information boundaries, ensuring the system discovers all derivable conclusions while maintaining robustness guarantees at every stage of the pipeline. -
OUT
knowledge-growth-is-exhaustive-and-information-governed
Exhaustive knowledge expansion — deterministic reversible reasoning combined with complete LLM-driven derivation with guaranteed termination within hardened integration boundaries — operates under comprehensive bidirectional information governance: inbound data passes through production-hardened LLM integration with process isolation and fail-soft semantics, while outbound information is constrained by access-tag authorization and token-budget limits. -
OUT
knowledge-growth-reaches-transparent-equilibria
The system's knowledge growth converges to equilibria that are simultaneously negation-transparent (the final stable state is uniquely determined by evaluation order-invariant rules over negative semantics) and propagation-complete (every truth change cascades to every transitively dependent node), with indefinite self-correction ensuring these equilibrium properties are maintained across unbounded operational time -
IN
list-negative-batches-at-40
`api.list_negative` splits candidates into batches of 40, so 120 keyword-matching nodes produce exactly 3 LLM calls. -
IN
list-negative-batches-at-50
`list_negative` splits candidate nodes into batches of approximately 50 for LLM classification, verified by the test suite asserting exactly 3 LLM calls for 120 candidates. -
IN
list-negative-is-bounded-and-batch-scalable
The list-negative classification pipeline is both defensively bounded (two-stage keyword + LLM filtering with hallucination rejection and graceful malformed-output handling) and scalably partitioned (fixed batch size of ~50 candidates per LLM call), ensuring predictable resource usage and bounded LLM costs regardless of belief network size. -
IN
list-negative-is-defensively-bounded
The negative belief listing pipeline applies defense-in-depth: keyword pre-filtering narrows candidates before LLM classification, hallucinated node IDs are discarded against the actual network, and malformed LLM output falls back gracefully to zero count rather than raising. -
IN
list-negative-json-parser-tolerates-prose-preamble
The `list_negative` LLM classification response parser uses `re.finditer` to extract JSON objects from responses that include prose preamble, handling the common LLM pattern of prefacing structured output with natural language rather than requiring clean JSON. -
IN
list-negative-parser-is-fully-resilient
The list-negative LLM response parser handles all degradation levels: regex extraction recovers JSON objects from prose-laden responses, and completely unparseable output returns zero results gracefully rather than raising exceptions. -
IN
list-negative-uses-two-stage-classification
`list_negative` uses keyword pre-filtering against a hardcoded `NEGATIVE_TERMS` list (~50 words), then LLM classification via `ask._invoke_claude` to eliminate false positives. -
OUT
maintenance-loop-is-fully-observable
The minimality-sustained closed maintenance loop has complete observability: every self-correction leaves traceable history (nogoods, retraction records, staleness reports), enabling full audit of how the system maintains itself over time. -
IN
make-nodes-excludes-active-premises
Test helper implementation detail, not a claim about production code behavior -
IN
make-nodes-omits-active-premises
Test helper implementation detail, not a production code invariant -
IN
mcp-bridge-call-tool-timeout-60s
`call_tool()` blocks for up to 60 seconds per invocation via `future.result(timeout=60)`; if the MCP server hangs, the calling thread unblocks with `TimeoutError`. -
IN
mcp-bridge-connect-blocks-up-to-30s
`connect()` blocks the calling thread for up to 30 seconds waiting for MCP session initialization via `_ready.wait(timeout=30)`, then raises `TimeoutError` if the server doesn't respond. -
IN
mcp-bridge-runs-dedicated-event-loop-thread
Each `McpBridge` instance runs its own asyncio event loop on a daemon thread; the MCP session stays alive until `close()` signals the shutdown event, bridging the sync/async boundary via `run_coroutine_threadsafe`. -
IN
mcp-bridge-tools-snapshot-at-connect
The tool catalog and server instructions are populated once during `connect()` and never refreshed — there is no mechanism to pick up tools added after the initial handshake. -
IN
mcp-is-optional-dependency
The `mcp` package is guarded by try/except at import time; `_require_mcp()` defers the `ImportError` to `McpBridge` construction so the module can be imported unconditionally without the SDK installed. -
IN
metadata-actively-governs-truth-propagation
Lifecycle state carried in node metadata (retraction flags, stale reasons) is not passive storage but actively governs truth propagation behavior — retracted nodes are skipped during BFS traversal and trigger nodes are never recomputed — ensuring that the universal extension mechanism directly controls truth maintenance rather than merely recording state -
IN
metadata-is-universal-extension-mechanism
Node metadata is the universal extension mechanism carrying all structured lifecycle state (retraction flags, stale reasons, challenges, access tags, supersession markers), and retraction flags pinned in metadata survive recomputation to enforce sticky retraction. -
OUT
minimality-is-both-generative-and-unifying
Minimality is simultaneously the generative source of each individual system property (extensibility, robustness, revision completeness) and the unifying principle that makes them cohere — the system achieves unity not by coordinating independently-designed features but because every feature is a different manifestation of the same minimal primitive set. -
OUT
minimality-is-the-universal-generative-principle
Minimality is the single generative architectural principle underlying all emergent system properties — extensibility and robustness arise from transparent extension composition on the minimal core, while revision completeness arises from uniform edge-case handling within the same core — revealing that these typically independent qualities share a common origin rather than requiring separate design effort. -
OUT
minimality-produces-uniformity-and-determinism
Minimality is the shared generative root of two independently-established system properties: edge-case uniformity (all cases handled by the same rules without special-casing) and operational determinism (predictable terminating evaluation with conservative failure), demonstrating that a single design principle produces both semantic and operational guarantees simultaneously. -
OUT
minimality-sustains-closed-loop-maintenance
Minimality generates both forward computation properties (uniformity, determinism) and backward revision properties (universal safety), while lifecycle management ensures every generated belief remains under active maintenance with no escape path — together forming a self-sustaining architecture where the generative principle and the maintenance loop are co-dependent. -
OUT
minimality-yields-extensibility-and-robustness
The minimal core simultaneously enables two independent emergent properties — transparent extension composition (dialectics, multi-agent federation) and uniform edge-case handling (vacuous premises, asymmetric absence) — demonstrating that minimality is operationally productive, not merely aesthetically elegant. -
IN
missing-nodes-have-asymmetric-fail-semantics
Missing nodes are treated asymmetrically: absent antecedents fail validation (conservative), absent outlist nodes pass (permissive), creating a "believe unless proven otherwise" default -
IN
missing-outlist-nodes-pass-validation
In `_justification_valid`, missing antecedent nodes cause the check to fail (node goes OUT), but missing outlist nodes pass (don't block) — an open-world default. -
IN
multiple-outlist-is-conjunction
When a justification has multiple outlist entries, ALL must be OUT for the justification to be valid; any single outlist node going IN defeats the entire justification -
OUT
mutation-pipeline-is-atomic-snapshot
Every network mutation follows an atomic snapshot pipeline: API context management ensures load/save atomicity with write-flag gating, while storage performs full-replace persistence — no partial state is ever visible between operations. -
OUT
mutation-pipeline-produces-consistent-state
Every mutation produces a fully consistent persisted network: atomic load/save ensures no partial writes, deterministic propagation ensures all truth values are correctly derived, and lifecycle-aware traversal prevents stale recomputations. -
IN
mutations-achieve-full-traceability
Every mutation is fully traceable from initiation to persisted outcome: atomicity ensures operations complete or roll back entirely, the audit log and structured before/after diffs provide historical context, and consistent artifact identification (deterministic challenge auto-IDs, monotonic nogood IDs) enables referencing specific mutation outcomes indefinitely. -
OUT
mutations-are-atomic-and-safely-propagated
Every network mutation follows an end-to-end safety pipeline: API context management ensures atomic load/save with write-flag gating, truth propagation terminates deterministically with lifecycle-aware BFS traversal, and snapshot persistence captures the final consistent state — no mutation can produce an inconsistent or divergent network. -
IN
mutations-are-atomic-audited-and-index-consistent
Every network mutation achieves three simultaneous guarantees: transactional atomicity (context-managed load/save with write-flag gating), historical auditability (timestamped audit log entries), and structural consistency (dependents index updated synchronously) — forming a complete mutation-safety contract. -
IN
mutations-are-observable-audited-and-index-consistent
Every network mutation achieves triple-layered traceability: callers receive structured before/after diffs at the API level, the internal audit log records timestamped events for historical analysis, and the dependents index is simultaneously maintained — providing both external and internal observability. -
IN
mutations-are-traceable-through-transitive-cascades
Every network mutation and its resulting retraction cascades are simultaneously traceable (atomic operations with audit logging, structured before/after diffs, consistent artifact identification) and transitively complete (propagation reaching all dependent nodes with guaranteed termination), ensuring every cascaded effect is as auditable as the triggering mutation -
IN
namespace-active-premise-invariant
When `namespace` is set, `add_node` auto-creates a `{namespace}:active` premise node and wires it as an antecedent into every namespaced justification; retracting that single premise cascades OUT every belief from that namespace. -
IN
namespace-is-colon-convention-with-auto-wiring
The namespace system is a colon-based convention with automatic infrastructure wiring: colon presence prevents double-prefixing, the `agent:id` format provides scoping, and node creation auto-wires a `{ns}:active` premise as an antecedent. -
IN
namespace-prefix-is-colon-separated
Agent namespacing uses the format `agent_name:belief_id`; `_resolve_namespace()` skips prefixing any ID that already contains a colon, preventing double-prefixing of cross-namespace references -
OUT
negation-is-transparent-to-evaluation
The system's complete negative semantics — structural absence creating premise behavior, explicit outlist defeat with automatic reversal, and guided recovery — operate within transformation-invariant truth evaluation: negation mechanisms alter belief topology but never create special-case evaluation paths, because the same uniform rules evaluate all resulting structures identically regardless of how they were produced. -
OUT
negative-semantics-are-uniform-through-minimality
The system's complete negative semantics — structural absence creating premise behavior, explicit outlist defeat with automatic reversal, permanent identity effects from challenge — handle all edge cases uniformly because they derive from the same minimal primitives as all other truth evaluation, with no special-case handling for negation. -
IN
negative-semantics-have-reversible-defeat-but-permanent-identity-effects
The system's complete negative semantics — structural absence creating premise behavior and explicit outlist defeat — exhibit a fundamental asymmetry: all outlist-based defeat operations (challenge, kill-switch, supersession) are fully reversible in truth value, but dialectical challenge permanently destroys premise identity by injecting a justification into a formerly unjustified node, an irreversible structural transformation. -
IN
network-add-node-rejects-duplicates
`Network.add_node()` raises `ValueError` if a node with the given ID already exists in the network. -
IN
network-any-mode-justification
Duplicates existing belief `node-in-if-any-justification-valid`. -
IN
network-dependents-eagerly-maintained
Duplicates existing beliefs `dependents-bidirectional-index` and `dependents-is-manual-reverse-index`. -
IN
network-disjunctive-justification
A node is IN if ANY of its justifications is valid (disjunction); each individual justification requires ALL antecedents IN and ALL outlist members OUT (conjunction) — the SL justification semantics from Doyle's 1979 paper. -
IN
network-is-central-dependency
`network.py` is imported by essentially every other module — api, storage, import, export, compact, check_stale, and all test files — making it the central data structure of the project. -
OUT
network-is-sole-truth-propagation-engine
All truth value computation and propagation in the system flows through `Network._propagate()` and `Network._compute_truth()`; no other module modifies truth values directly. -
IN
network-metadata-carries-structured-state
Node state such as `_retracted`, `retract_reason`, `superseded_by`, `challenges`, `access_tags`, and `summarized_by` lives in the generic `metadata` dict rather than typed Node fields, keeping the dataclass stable while features layer on behavior. -
IN
network-missing-outlist-passes
Duplicates existing belief `missing-outlist-nodes-pass-validation`. -
IN
network-mutations-append-to-audit-log
Every network mutation records a timestamped event in `self.log`, an append-only list that serves as the propagation audit trail. -
IN
network-mutations-are-audited-and-index-consistent
Every network mutation simultaneously maintains dual invariants: the audit log receives a timestamped event providing historical traceability, and the dependents reverse index is kept consistent ensuring correct future propagation. -
IN
network-retracted-nodes-persist
Retracted nodes remain in `self.nodes` with `_retracted` metadata set; they are never deleted from the graph, enabling later restoration via `assert_node()` without rederivation. -
IN
network-retracted-skips-propagation
Duplicates existing belief `retracted-nodes-skipped-in-propagation`. -
IN
network-single-justification-removal-blocked
`remove_justification()` raises `ValueError` when a node has exactly one justification, forcing callers to use `convert_to_premise` or `retract` instead — preventing accidental creation of unjustified non-premise nodes. -
IN
network-state-is-extensible-and-consistently-tracked
Network state management is both extensible (metadata carries all lifecycle state — retraction flags, stale reasons, challenges, access tags, supersession — as a universal key-value mechanism) and consistently tracked (every mutation maintains the audit log and dependents index simultaneously), ensuring new state dimensions can be added without compromising existing consistency guarantees -
IN
network-tests-are-database-free
Test infrastructure detail, not a production code claim -
IN
node-in-if-any-justification-valid
A Node is IN when at least one of its Justifications is valid; a Justification is valid when all antecedents are IN and all outlist nodes are OUT (disjunctive over justifications, conjunctive within each). -
IN
node-truth-disjunctive
A node is IN if ANY of its justifications is valid (disjunctive semantics); adding a justification to a node can never cause it to go OUT. -
IN
nogood-id-backwards-compat
Databases created before the monotonic counter fix (lacking `network_meta` table) load without error; the counter is derived from max existing nogood ID + 1, or defaults to 1 if none exist -
IN
nogood-id-collision-prevention
After importing nogoods, `_next_nogood_id` is set to one past the highest imported ID, preventing `network.record_nogood` from generating a conflicting ID later. -
IN
nogood-id-counter-is-monotonic
`Network._next_nogood_id` only ever increases — deletion, clearing, save/load, and import never decrease it, preventing ID reuse after the issue #26 fix -
IN
nogood-id-format-is-zero-padded
Nogood IDs follow the format `nogood-NNN` with 3-digit zero-padded integers (e.g., `nogood-001`), and `_next_nogood_id` holds the next integer to assign, not the last used -
IN
nogood-id-monotonic
`Network._next_nogood_id` only ever increases; deletions and clears do not decrement it, preventing ID reuse (fix for issue #26) -
IN
nogood-id-persisted-in-meta
The nogood counter is stored in the `network_meta` SQLite table and survives save/load cycles as a high-water mark, not a count of current nogoods -
IN
nogood-ids-are-durable-and-collision-free
Nogood IDs form a durable collision-free sequence: the counter only increases (surviving deletions and clears), persists across save/load cycles via the network_meta table, and advances past imported IDs to prevent collisions — no operation can produce a reused or ambiguous contradiction identifier -
OUT
nogood-ids-assume-append-only
Nogood IDs are derived from `len(self.nogoods) + 1`, so deleting a nogood from the list would cause ID collisions on subsequent calls -
IN
nogood-resolution-maintains-consistent-ids
Nogood recording and resolution produces a consistent, referenceable history of contradictions -
IN
nogoods-require-valid-nodes
Nogoods whose `Affects` list references node IDs not present in the network are silently skipped rather than raising an error during import -
IN
normalization-drops-unknown-refs
Both `_normalize_markdown` and `_normalize_json` silently drop antecedent/outlist references to IDs not present in the import set, preventing dangling edges in the dependency graph. -
IN
only-reasons-lib-is-distributed
Only the `reasons_lib` package is included in built distributions — `tests/`, `entries/`, and `reviews/` are excluded by the explicit `packages = ["reasons_lib"]` declaration in pyproject.toml. -
IN
only-reasons-lib-packaged
Setuptools is configured to include only the `reasons_lib` package in distributions; tests, entries, reviews, and knowledge-base artifacts are excluded from wheels -
OUT
operational-assurance-is-resource-efficient
The system's comprehensive operational assurance — spanning temporal self-correction, end-to-end reliability, and external control — is achieved within a resource-efficient pipeline that minimizes footprint at every lifecycle phase, ensuring operational guarantees do not degrade under resource constraints. -
OUT
operational-guarantees-span-safety-and-trust
The system's operational guarantees span two independent enforcement dimensions: safety that is universal and condition-independent (holding across all layers, backends, and adverse conditions) and trust boundaries that are comprehensively enforced (architectural self-containment and information flow control at every boundary) — together ensuring no operational path can compromise system integrity regardless of layer, backend, or external interaction. -
OUT
operational-integrity-is-end-to-end
Every network operation achieves both transactional atomicity (load/save gating, snapshot persistence) and semantic determinism (uniform pure evaluation, terminating propagation, reversible defeat), ensuring every mutation produces a predictable, recoverable network state. -
OUT
operational-profile-is-traceable-through-equilibria
The system's safe, assured, resource-bounded operational profile produces evaluations that are traceable from individual computation through system-wide convergence to documented equilibria — operational guarantees hold not just at a point in time but longitudinally across the system's entire trajectory toward stable states. -
OUT
operational-safety-is-defense-in-depth-reinforced
The system's operational safety guarantees — universal across all architectural layers and condition-independent — are concretely reinforced by defense-in-depth at every external boundary: LLM integration applies layered defenses (bounded execution, fail-soft handling, subprocess isolation) and system boundaries enforce validation, resilience, and resource constraints simultaneously -
OUT
operational-safety-is-resource-efficient-defense-in-depth
The system's operational safety guarantees — universal across all architectural layers and condition-independent — are concretely reinforced by defense-in-depth spanning LLM and system boundaries, and this defense-in-depth is achieved within a resource-efficient pipeline from packaging through runtime, meaning comprehensive safety does not trade off against operational cost. -
OUT
operational-safety-is-universal-and-condition-independent
Operational safety holds universally across two independent dimensions: across all architectural layers and storage backends (SQLite and PostgreSQL enforce equivalent safety through backend-appropriate mechanisms), AND under all graph conditions including adverse states (dangling references trigger graceful degradation, cyclic justifications are bounded, concurrent access uses WAL mode) -
OUT
origin-agnostic-trustworthiness-is-fully-verifiable
The system's complete revision trustworthiness holds identically across all belief origins and is independently verifiable through full maintenance loop observability — trustworthiness is not merely claimed but provable through origin-agnostic audit trails — provided propagation's dependents invariant holds. -
OUT
origin-agnosticism-unifies-trustworthiness-and-grounding
The system's origin-agnostic closed loop simultaneously delivers two independent guarantees from a single architectural source: verifiable trustworthiness across all belief origins and complete invariant grounding for external beliefs — origin indifference is not merely a property but the shared mechanism producing both guarantees. -
IN
out-beliefs-imported-as-bare-premises
Covered by existing `out-beliefs-imported-without-justifications` which captures the same invariant -
IN
out-beliefs-imported-without-justifications
Beliefs that are OUT or STALE in the source are imported with an empty justification list, preventing `recompute_all` from resurrecting them to IN -
IN
out-nodes-excluded-from-staleness
Duplicate of existing `check-stale-skips-out-nodes`. -
IN
outlist-absent-means-out
An outlist node that doesn't exist in the network is treated as OUT (justification satisfied); absent antecedent nodes fail validation — this asymmetry makes missing counter-evidence permissive while missing supporting evidence is strict -
IN
outlist-enables-non-monotonic-reasoning
The `outlist` field on `Justification` allows beliefs to be retracted when a defeating node becomes IN — this is the core non-monotonic mechanism powering `supersede`, `challenge`, and default-logic patterns. -
IN
outlist-is-universal-defeat-mechanism
The outlist primitive is the sole defeat mechanism underlying all non-monotonic features: challenges, agent kill-switches, supersession, and direct defeasible reasoning -
OUT
outlist-nodes-not-in-dependents-index
Outlist nodes are not tracked in the `dependents` index, so when an outlist node is retracted (goes OUT), dependent GATE beliefs are not enqueued for re-evaluation by `_propagate` — requiring manual `reasons assert` as a workaround. -
IN
outlist-relationships-survive-persistence
Outlists are stored as `outlist_json` in the SQLite `justifications` table; on load, the dependent index is rebuilt for both antecedents and outlist nodes, preserving propagation behavior across save/load cycles -
IN
outlist-semantics-are-fully-specified
The outlist primitive has complete, well-defined semantics: multiple entries form a conjunction (all must be OUT), absent nodes are treated as OUT (permissive default), and outlist relationships survive persistence through JSON serialization with rebuilt dependent indexes. -
IN
package-name-split
The pip-installable name is `ftl-reasons` but the importable Python package is `reasons_lib`; these are deliberately decoupled. -
IN
parse-beliefs-returns-dicts
Too granular — the dict-return pattern is partially covered by `api-functions-return-dicts`, and the specific field list is an implementation detail likely to evolve -
IN
parse-review-defaults-to-passing
`parse_review_response` defaults missing fields to valid=True, sufficient=True, necessary=True, unnecessary_antecedents=[] — the LLM only needs to report failures explicitly; omission means the belief passed. -
IN
parse-review-response-never-raises
`parse_review_response` returns an empty list on any malformed input — bad JSON, non-list JSON, items missing `id` fields — rather than raising exceptions, with missing boolean fields defaulting to `True`. -
OUT
persistence-is-snapshot-not-incremental
The storage layer operates as a full snapshot: save replaces all rows, load trusts stored values without re-propagation, and the dependents index is rebuilt from scratch -
OUT
persistence-round-trip-is-lossless
The save/load round trip preserves all network state faithfully: snapshot persistence captures the full graph, stored truth values are trusted without re-propagation, justification insertion order is preserved via rowid, and outlist relationships survive serialization. -
IN
pg-access-control-raises-permission-error
`PgApi.show_node()` raises `PermissionError` when called with a `visible_to` list that doesn't intersect the node's `access_tags`, enforcing per-node access control at the API boundary -
IN
pg-antecedent-refs-have-no-fk-constraints
Antecedent and outlist references in `rms_justifications` are JSONB arrays without foreign key constraints; nonexistent referenced nodes default to truth value OUT via `truth_cache.get(a, "OUT")`. -
IN
pg-api-what-if-never-mutates
`PgApi.what_if_retract()` and `what_if_assert()` are read-only operations — they return cascade analysis (changed/went_in/went_out) without modifying database state, verified by checking `get_status()` before and after -
IN
pg-cleanup-covers-five-tables
Fixture teardown deletes from exactly `rms_propagation_log`, `rms_justifications`, `rms_nogoods`, `rms_network_meta`, and `rms_nodes` — adding a new `project_id`-scoped table without updating the fixture will leak test data. -
IN
pg-conninfo-accepts-flag-envvar-or-both
PostgreSQL connection is configured via `--pg`/`--project-id` CLI flags or `REASONS_PG_CONNINFO`/`REASONS_PROJECT_ID` environment variables, with CLI flags taking precedence over env vars — `_backend_kwargs(args)` handles the dispatch. -
OUT
pg-data-integrity-achieves-defense-in-depth
PgApi's data integrity achieves defense-in-depth through both application-level enforcement (write-time referential validation and outlist-aware dependent queries spanning antecedent and outlist references) and comprehensive input validation at all system boundaries, but full defense-in-depth requires database-level foreign key constraints to provide a second independent enforcement layer -
IN
pg-dispatch-is-function-level-early-return
PostgreSQL routing uses a function-level early-return pattern — each API function checks `pg_conninfo` and short-circuits to `_pg_dispatch`, which instantiates PgApi and calls the matching method via `getattr` — rather than using abstract base classes, subclassing, or factory patterns. -
IN
pg-dispatch-requires-project-id
When `pg_conninfo` is provided (via CLI `--pg` flag or `REASONS_PG_CONNINFO` env var) without a corresponding `project_id`, the system calls `sys.exit()` rather than proceeding with an incomplete configuration -
IN
pg-extra-required-for-postgres
PostgreSQL support requires installing the `pg` or `test-pg` optional extra (`psycopg[binary]>=3.1`); it is not available in a bare install. -
IN
pg-fixture-provides-per-test-isolation
Each pg test receives an isolated `PgApi` instance via the `pg_api` fixture, which creates a fresh project namespace (UUID) and tears down after each test, ensuring zero shared state between tests. -
IN
pg-fixture-uses-uuid-isolation
Each `pg_api` test fixture partitions its data by a unique UUID v4 `project_id` rather than using transactions or ephemeral databases, enabling safe concurrent test execution against a shared database. -
IN
pg-keyerror-includes-missing-node-id
When `add_node` references a nonexistent node in `sl=` or `unless=`, PgApi raises `KeyError` with the missing node ID in the exception message — tested via `pytest.raises(KeyError, match="ghost")` — enabling callers to identify which reference is dangling. -
IN
pg-multi-tenancy-via-project-id
`PgApi` isolates beliefs by `project_id` so identical node IDs in different projects store independent data with no cross-contamination — each test gets a unique UUID project -
IN
pg-psycopg-is-optional-dependency
`psycopg` (v3) is soft-imported with fallback to `None`; `_require_psycopg()` raises `ImportError` with install instructions at `PgApi` construction time, mirroring the `mcp_client.py` lazy-guard pattern. -
OUT
pg-reimplements-network-in-sql
`PgApi` reimplements the in-memory `Network`'s algorithms (BFS propagation, entrenchment scoring, nogood resolution, dialectical operations) directly in SQL rather than delegating to the `Network` class. -
IN
pg-search-includes-one-hop-neighbors
PostgreSQL full-text search via `plainto_tsquery` includes 1-hop neighbor expansion — direct antecedents and dependents of matching nodes are included in results, providing richer context than exact-match-only search. -
IN
pg-teardown-rollback-before-delete
The `pg_api` fixture calls `conn.rollback()` before cleanup deletes to recover from any failed-transaction state left by the test, preventing cleanup failures from cascading. -
IN
pg-test-suite-is-backend-parity
`test_pg.py` is a parity test suite: every behavior tested has a corresponding expected behavior from the SQLite backend, validating that PgApi returns the same dict shapes and enforces the same invariants to ensure backend interchangeability. -
IN
pg-unsupported-params-raise-not-implemented
When PgApi-routed commands receive unsupported parameters (e.g., search with `depth != 1`, list with `challenged`/`min_depth`, add with `namespace`), the dispatch raises `NotImplementedError` with a clear message rather than silently ignoring the parameter. -
IN
pg-uses-jsonb-with-gin-for-dependent-queries
PgApi stores antecedents and outlist as JSONB arrays with GIN indexes, enabling per-operation dependent lookups via `@>` containment queries — unlike the SQLite backend which loads the full network, PgApi queries relationships incrementally. -
IN
pg-uses-project-id-for-multi-tenancy
Every `PgApi` query includes `project_id` in its WHERE clause, providing multi-tenant isolation with no cross-project queries and composite primary keys `(id, project_id)`. -
IN
pg-what-if-is-safely-simulated
PgApi's what-if operations achieve safe simulation: mutations are performed against real PostgreSQL data for accurate cascade analysis, then rolled back within a transaction to guarantee zero persistent side effects — combining fidelity with safety. -
IN
pg-what-if-uses-transaction-rollback
`what_if_retract`/`what_if_assert` perform real mutations inside a transaction, collect cascade effects, then rollback — reusing the propagation engine without duplicating logic. -
OUT
pgapi-achieves-implementation-parity
PgApi achieves full behavioral parity with the in-memory Network implementation: it reimplements the core algorithms (entrenchment scoring, nogood resolution, BFS propagation, dialectics) in SQL with BFS propagation executed in application-level Python. -
IN
pgapi-bfs-propagation-in-python
PgApi implements BFS propagation in application-level Python (not stored procedures), using JSONB containment queries (`@>`) against GIN indexes to find dependents -
IN
pgapi-enforces-referential-integrity-bidirectionally
PgApi enforces referential integrity in both directions: write-time validation checks all antecedent and outlist IDs exist before inserting justifications, while read-time dependent discovery queries both antecedent and outlist JSONB containment to find all affected nodes. -
IN
pgapi-find-dependents-queries-outlist
PgApi's `_find_dependents` queries both `antecedents @>` and `outlist @>` JSONB containment, correctly enqueuing outlist-dependent nodes for re-evaluation — a capability the in-memory Network historically lacked until PR #31. -
IN
pgapi-has-no-retry-logic
`PgApi` uses one connection per instance with no retry on transient database errors; callers are expected to handle `psycopg` exceptions from connection failures or serialization conflicts. -
IN
pgapi-is-sql-native-multi-tenant
PgApi operates as a SQL-native multi-tenant implementation: all operations execute directly against PostgreSQL with no in-memory Network object constructed, composite primary keys on all tables provide project-level isolation, and each public method is a single committed transaction. -
IN
pgapi-multi-tenant-composite-keys
All PgApi tables use composite primary keys `(id, project_id)` for multi-tenancy, with JSONB columns (not TEXT) for antecedents, outlist, and metadata -
IN
pgapi-no-in-memory-network
`PgApi` executes all operations as direct SQL against PostgreSQL — no in-memory `Network` object is ever constructed, enabling concurrent writers -
IN
pgapi-one-transaction-per-method
Each PgApi public method is a single PostgreSQL transaction that commits or rolls back at the end; __exit__ rolls back on exception -
OUT
pgapi-partial-api-coverage
PgApi implements core operations (add/retract/assert/search/nogood/explain) but defers simulation, dialectics, namespace support, import/export, and maintenance operations to future work -
OUT
pgapi-referential-integrity-is-database-enforced
PgApi achieves complete referential integrity: application-level bidirectional enforcement (write-time validation and outlist-aware dependent querying) is complemented by database-level foreign key constraints, preventing orphaned justification references even under direct SQL manipulation outside the application layer. -
IN
pgapi-validates-refs-before-justification-insert
PgApi's `_validate_refs` checks all antecedent and outlist node IDs exist in `rms_nodes` before inserting a justification, providing application-level referential integrity that compensates for JSONB arrays' inability to enforce foreign key constraints. -
IN
pgapi-what-if-uses-transaction-rollback
PgApi's `what_if_retract` and `what_if_assert` wrap BFS cascade analysis inside a database transaction with `try/finally` guaranteed ROLLBACK, achieving read-only simulation by never committing the exploratory mutations. -
IN
premise-behavior-emerges-from-absence
Premise behavior is not explicitly implemented — it emerges from three defaults: nodes with no justifications default to IN, empty antecedent lists are vacuously valid, and the system preserves a premise's current truth value rather than deriving it. -
IN
premise-can-receive-justification
A premise node (initially no justifications) can receive a justification via `add_justification`, giving it a derived backup path that keeps it IN even if its premise status is retracted. -
IN
premise-count-is-per-justification-max
`premise_count` in the return value reports the maximum antecedent count across the node's justifications, not the total across all justifications (any_mode with 3 premises returns 1, not 3). -
IN
premise-default-in
Covered by existing `premise-behavior-emerges-from-absence` which captures this as an emergent property -
IN
premise-defaults-to-in
A node with no justifications (a premise) defaults to IN; `_compute_truth` preserves its current truth value rather than recomputing it. -
IN
premise-derived-verifiability-asymmetry
Premises record source file and line range at assertion time, enabling check-stale to verify them against current code; derived beliefs have no equivalent source-level grounding and can only be structurally validated (references exist and are IN). -
IN
premise-identity-is-bidirectionally-transformable
Premise identity can be both destroyed (via dialectical challenge adding justifications) and created (via convert-to-premise removing them), with both directions preserving the dependents invariant — making premise/derived status a fully reversible structural property of the network. -
OUT
premise-identity-is-inherently-transient
Premise identity is inherently transient because it emerges from the absence of justifications, and any justification addition — whether from dialectical challenge, defend, or direct add_justification — irreversibly transforms a premise into a derived node without explicit opt-in. -
OUT
premise-identity-transformation-is-architecturally-asymmetric
Premise identity transformation exhibits a fundamental architectural asymmetry rooted in the same emergent property: premise identity is inherently transient because it arises from the absence of justifications, and dialectical challenge exploits this transience to permanently transform premises into justified nodes — while the truth-value defeat itself remains fully reversible through outlist semantics, creating an irreversible identity change layered atop reversible truth dynamics -
IN
premises-have-no-justifications
A premise node is represented by an empty `justifications` list and defaults to `truth_value="IN"`; the system treats the empty-justifications case as a special unconditional belief. -
OUT
propagate-assumes-dependents-exist
Every ID in `node.dependents` is accessed via `self.nodes[dep_id]` without a membership check; a dangling dependent reference will raise `KeyError` — this is intentional (broken invariant = bug) -
IN
propagate-cascade-stops-on-unchanged
If a dependent's recomputed truth value equals its current value, it is not enqueued — the cascade terminates along that path, making propagation selective rather than exhaustive -
IN
propagate-does-not-change-trigger
The seed node (`changed_id`) is added to `visited` immediately and never has its own truth value recomputed; callers must update it before calling `_propagate` -
IN
propagate-skips-retracted-nodes
`_propagate` never recomputes truth values for nodes with `_retracted` in metadata, even if their justifications would support IN; only `assert_node` can restore them -
IN
propagation-is-bfs
Truth value propagation in `_propagate` uses `deque`-based BFS through the `dependents` graph, not DFS, ensuring breadth-first wavefront expansion. -
IN
propagation-is-crash-free
Truth propagation completes without runtime errors across all reachable nodes -
IN
propagation-is-immediate
Covered by existing `mutations-are-atomic-and-safely-propagated` and `propagation-is-safe-and-terminating` -
IN
propagation-terminates-deterministically
Truth propagation is guaranteed to terminate: BFS prevents stack overflow, stop-on-unchanged prevents oscillation, and fixpoint iteration bounds the outer loop -
IN
pure-evaluation-enables-richly-governed-dialectics
Evaluation purity — grounding dialectics through the minimal architecture — simultaneously enables richly-governed exception-safe revision, so dialectical structures are both minimality-grounded in their computation and richly-governed in the state they produce: challenge/defend operations inherit pure deterministic evaluation while producing metadata-enriched recoverable state changes. -
IN
python-310-floor
The project requires Python 3.10+ (`requires-python = ">=3.10"`), establishing the minimum language features available throughout the codebase. -
IN
python-310-minimum
The project requires Python >= 3.10 (`requires-python = ">=3.10"`), enabling use of structural pattern matching and `X | Y` union type syntax throughout the codebase. -
IN
python-version-floor-3-10
The project requires Python >=3.10, allowing use of match statements, union type syntax with `|`, and other 3.10+ features throughout `reasons_lib`. -
IN
read-and-write-paths-are-both-reliable
Both the read path (staleness checking detects all forms of source drift without false negatives) and the write path (truth propagation completes without runtime errors across all reachable nodes) are operationally reliable, ensuring the system functions correctly in both observational and mutational modes. -
OUT
reasoning-and-knowledge-expansion-are-both-exhaustive
The system achieves exhaustive coverage in both formal reasoning (deterministic reversible truth evaluation with guaranteed-terminating exploration of all derivable conclusions) and LLM-driven knowledge expansion (complete coverage with fault tolerance across all interactive and batch LLM operations) -
IN
reasons-cli-entrypoint-is-cli-main
The `reasons` CLI command is registered as `reasons_lib.cli:main` via `[project.scripts]` in pyproject.toml — changing that function's signature or module location breaks the installed command. -
IN
rebuild-dependents-clears-before-rebuilding
`_rebuild_dependents()` wipes all existing dependent sets before recomputing from justifications, so stale entries are always removed rather than incrementally patched -
IN
rebuild-dependents-is-idempotent
Calling `_rebuild_dependents()` twice in succession produces identical `dependents` sets on every node. -
OUT
reference-validation-is-defense-in-depth
Every system boundary that accepts node ID references validates them against the actual network: import normalization drops unknown antecedent/outlist refs, nogood recording skips invalid node IDs, and LLM-returned negative-list IDs are filtered against existing nodes. -
OUT
references-are-durable-across-persistence-and-evolution
All system-generated identifiers survive both persistence boundaries (save/load cycles, cross-session durability via high-water marks) and format evolution boundaries (parser versioning, schema migration, forward-compatible metadata) — references remain valid and resolvable across time and system versions. -
OUT
removal-effects-are-fully-reported-and-recoverable
Every belief removal — whether intentional retraction or contradiction-triggered backtracking — provides three simultaneous guarantees: complete cascade coverage (transitive propagation captures every truth-value change), accurate effect reporting (structured before/after diffs reflect all affected nodes), and surgical recovery guidance (restoration hints target only cascade victims with surviving premises). -
IN
remove-justification-enforces-minimum-count
`Network.remove_justification` refuses to remove the last justification from a derived node — a derived node must retain at least one justification, enforced with `ValueError("only one justification")` -
IN
rename-from-rms-at-0.3.0
The project was renamed from `rms` to `reasons` in version 0.3.0, driven by a measured 5 percentage-point LLM accuracy improvement in ablation study -
IN
require-sqlite-blocks-pg-for-guarded-commands
`_require_sqlite` enforces that certain subcommands (e.g., `hash-sources`) cannot run against a Postgres backend, checking both CLI flags and environment variables and calling `sys.exit()` if PG is detected -
OUT
resource-efficient-guarantees-are-universal-and-permanent
The system's universal and permanent guarantees are achieved within resource-efficient bounds — self-sustainability, comprehensive auditability, and resource efficiency form a self-reinforcing triad that extends to all belief types without temporal degradation and without resource exhaustion. -
OUT
resource-management-supports-belief-currency
Active belief currency management — sustainable derivation of new beliefs and staleness detection for existing ones — operates with accurate bidirectional token budget control, ensuring derivation rounds allocate resources correctly per agent and output fits context-limited consumer constraints. -
IN
restoration-hints-are-surgical
Restoration hints provide surgical recovery guidance after retraction cascades: hints exclude the directly retracted node (targeting only cascade victims) and require at least one surviving premise in a multi-premise justification — narrowing recovery scope to nodes that can actually be independently re-justified. -
IN
restoration-hints-require-surviving-premises
A retraction cascade only produces a `restoration_hint` for a node if it has a multi-premise SL justification and at least one of its premises is still IN after the cascade. -
IN
result-schema-uniformity
Both `content_changed` and `source_deleted` results from `check_stale` share exactly the same six keys: `node_id`, `old_hash`, `new_hash`, `source`, `source_path`, `reason` — consumers can handle both uniformly. -
IN
results-sorted-by-node-id
`check_stale()` returns results sorted lexicographically by `node_id`, enforced by iterating `sorted(network.nodes.items())`. -
IN
retract-computes-restoration-hints
`retract_node` computes `restoration_hints` by examining surviving premises of multi-antecedent justifications, enabling callers to identify which premises could rebuild retracted derived beliefs. -
IN
retract-returns-changed-set
`Network.retract()` returns a list of all node IDs whose truth value changed, including the target and all transitively affected dependents; retracting an already-OUT node returns `[]` -
IN
retracted-nodes-skipped-in-propagation
Retracted nodes (marked with `_retracted` metadata) are skipped during BFS propagation but remain in the network for potential restoration. -
IN
retracted-pin-survives-recompute
A node explicitly retracted via `retract()` gets a `_retracted` metadata flag that pins it OUT — surviving both `assert_node` on its antecedents and `recompute_all()`, clearable only by `assert_node` on the pinned node itself -
OUT
review-achieves-verified-fault-tolerance
The review pipeline's scoped mutation-safe operation combined with uniform fail-safe output achieves verified fault tolerance across all failure modes — batch failures, missing antecedent references, and malformed LLM responses. -
IN
review-and-contradictions-catch-orthogonal-errors
`review-beliefs` catches invalid reasoning within individual derivation steps (over-generalization, missing bridges, strength escalation) while `contradictions` catches incompatible facts across independently valid beliefs (absolute claims vs. documented exceptions) — the two commands are complementary with different cascade profiles (review: high cascade at depth, contradictions: low cascade at leaves). -
IN
review-batch-failure-is-silent-skip
When an LLM call fails for a review batch, the error is logged to stderr but the batch is skipped with no indication in the returned results; callers cannot distinguish "skipped due to error" from "no problems found." -
IN
review-evaluates-direct-antecedents-only
`review-beliefs` presents each derived belief with only its direct antecedents to the LLM reviewer, not the transitive chain — producing occasional sufficiency false positives when support exists one level deeper, which must be triaged manually. -
IN
review-format-handles-missing-antecedents
`format_belief_for_review` renders `"(not found in network)"` for antecedent IDs that don't exist in the nodes dict rather than crashing, and returns an empty string for a nonexistent belief ID. -
IN
review-has-no-storage-dependency
The review module operates entirely on an in-memory `nodes` dict (from `export_network()`) and never reads from or writes to the database directly. -
OUT
review-is-read-only-and-fault-tolerant
The review module operates entirely on in-memory snapshots with no storage dependency, handles missing antecedent references with placeholder text rather than exceptions, and silently skips failed LLM batches — achieving fault-tolerant read-only operation across all failure modes. -
IN
review-multi-justification-disjunctive
The review prompt instructs the LLM that a belief is valid if ANY of its justifications is sound, matching Doyle's TMS disjunctive support semantics. -
IN
review-only-evaluates-derived-beliefs
`review_beliefs` filters out premises (nodes without justifications); only derived beliefs with at least one justification are sent for LLM review. -
IN
review-only-validates-derived-beliefs
The review pipeline filters out premises (nodes with empty justifications) before sending anything to the LLM; premises are never submitted for review validation. -
IN
review-parse-requires-json-array
`parse_review_response` only accepts JSON arrays; a bare JSON object `{...}` is treated as unparseable and returns an empty list — the LLM must return a list even for single-item reviews. -
IN
review-result-schema-is-normalized
Every result dict returned by `parse_review_response` is guaranteed to have exactly six keys (`id`, `valid`, `sufficient`, `necessary`, `unnecessary_antecedents`, `comment`) regardless of what the LLM returned, via normalization with safe defaults. -
IN
review-skips-premises
`review_beliefs` only sends beliefs with at least one justification to the LLM; premise nodes (empty justifications list) are excluded from review entirely. -
IN
rewrite-dependents-updates-both-antecedents-and-outlists
`_rewrite_dependents(net, old, new)` in `api.py` rewrites justification references and dependent sets for both antecedent and outlist occurrences of the old node ID, not just one or the other -
IN
run-cli-helper-catches-systemexit
The `run_cli` test harness intercepts `SystemExit` to extract exit codes, preventing argparse errors or explicit `sys.exit()` calls from terminating the test process. -
OUT
safety-and-uniformity-are-co-derived
Dialectical safety (deterministic evaluation of irreversible premise transformations) and edge-case uniformity (consistent handling of vacuous premises, asymmetric absence, and empty antecedents) are independently derived from the same shared root — semantic minimality with operational determinism — revealing them as two faces of a single architectural property rather than independent design achievements. -
OUT
safety-integrity-and-uniformity-converge
Three independently-established properties — boundary-agnostic integrity (internal/external indifference), dialectical safety (deterministic evaluation of irreversible transformations), and edge-case uniformity (consistent handling of vacuous and asymmetric cases) — converge to a single architectural invariant because all three derive from the same minimal evaluation rules applied uniformly. -
IN
search-falls-back-to-substring
`api.search()` tries FTS5 full-text search first, then falls back to substring matching if FTS5 tables don't exist or error; it always produces results if substring matches exist. -
IN
search-has-four-output-formats
The `search` function supports four output formats: markdown, json, minimal, and compact — selected by the caller to match the consumption context (human, API, LLM prompt, compact view). -
IN
search-is-resilient-across-index-states
Search operates correctly regardless of FTS5 index availability: the index is derived (rebuilt from scratch on every save) so stale indexes are self-healing, and search falls back to substring matching when FTS tables don't exist or error -
IN
search-relaxation-capped-at-50-queries
FTS progressive relaxation drops terms via `combinations()` (largest subsets first) until results appear, capped at 50 total relaxation queries to prevent combinatorial explosion on long search strings. -
IN
search-uses-fts5-with-substring-fallback
`api.search()` tries FTS5 first (`_fts_search`), falls back to substring matching (`_substring_search`), then expands results with 1-hop neighbors from the dependency graph. -
IN
semantic-contradiction-reuses-cluster-infrastructure
Semantic contradiction detection delegates embedding and clustering to the existing `list_clusters()` from `reasons_lib/cluster.py` — the same infrastructure that serves deduplication also serves contradiction detection, with no duplicate embedding/clustering implementation. -
IN
semantic-contradiction-skips-singleton-clusters
Single-belief clusters are skipped during semantic contradiction detection (no contradiction possible within one belief), and clusters exceeding `CONTRADICTION_BATCH_SIZE` are sub-batched within the cluster boundary. -
IN
semantic-minimality-with-operational-determinism
The system unifies semantic minimality (all non-monotonic features and truth semantics derive from uniform outlist/disjunction primitives) with operational determinism (all operations terminate predictably via BFS fixpoint with conservative failure semantics), yielding a small trusted kernel that powers all reasoning. -
IN
single-cli-entry-point
The only registered console script is `reasons`, pointing to `reasons_lib.cli:main`; all subcommands are dispatched internally -
IN
single-node-api-raises-permissionerror
API functions that target a single node by ID (`show_node`, `explain_node`, `trace_assumptions`, `trace_access_tags`) raise `PermissionError` when the caller lacks clearance; collection endpoints silently filter instead. -
IN
sl-conjunction-any-disjunction
Covered by existing `sl-justification-semantics`, `truth-is-disjunctive-over-conjunctive-rules`, and `node-in-if-any-justification-valid` -
IN
sl-justification-is-disjunctive
A node is IN if *any* of its justifications is valid (disjunctive semantics); `_compute_truth()` short-circuits on the first valid justification rather than requiring all justifications to hold. -
IN
sl-justification-semantics
An SL justification is valid iff ALL antecedents are IN AND ALL outlist nodes are OUT; a node is IN iff ANY of its justifications is valid (conjunction within a justification, disjunction across justifications). -
IN
sl-outlist-asymmetry
Missing antecedents invalidate a justification, but missing outlist nodes do not — this asymmetry enables "believe X unless Y" where Y may not yet exist in the network -
IN
sl-param-is-comma-separated-node-ids
`add_node()` encodes SL justifications as comma-separated node IDs in the `sl=` parameter and outlist nodes in `unless=`, mirroring the TMS (SL, OL) formalism directly in the API signature. -
IN
stale-maps-to-out
STALE status in `beliefs.md` is mapped to OUT (retracted) in the network; there is no distinct STALE state in the TMS data model. -
IN
stale-result-reasons
`check_stale` uses two distinct reason codes: `"content_changed"` when the source file exists but its hash differs, and `"source_deleted"` when the file is gone from disk. -
IN
staleness-checking-is-comprehensive
Staleness checking detects all nodes whose source material has changed on disk -
IN
staleness-information-survives-binary-truth-model
Despite the TMS using binary IN/OUT truth values with no distinct STALE state, staleness information is preserved end-to-end: stale beliefs are mapped to OUT on import with stale_reason metadata, and the compact output surfaces this metadata for OUT nodes — so downstream consumers can distinguish intentional retractions from staleness-driven ones. -
IN
staleness-is-conservative-ci-gate
Staleness checking is designed as a safe CI gate: it never mutates state, only checks IN nodes, requires both source fields, and exits nonzero to fail the pipeline -
IN
staleness-is-surfaced-despite-binary-truth-model
Staleness information not only survives the binary IN/OUT truth model (via metadata-based preservation through import and compact surfacing) but also emerges as deterministic, uniformly-structured, machine-parseable CI output with conservative non-mutating semantics — no information is lost between the TMS representation and the external consumer. -
IN
staleness-output-is-ci-pipeline-ready
Staleness checking produces deterministic, uniformly-structured, machine-parseable output with conservative non-mutating semantics and nonzero exit codes, making it directly consumable by automated CI pipelines without wrapper scripts -
IN
staleness-results-are-dicts-not-exceptions
Both `check_stale` and `hash_sources` report problems (missing files, changed content, deleted sources) as list-of-dict return values, never raising exceptions — designed for batch audit operations that report all problems rather than aborting on the first. -
IN
staleness-uses-full-sha256
Staleness detection compares full 64-character SHA-256 hex digests via exact string equality, not truncated prefixes or fuzzy comparison. -
IN
startup-performance-uses-lazy-loading
Both the API and CLI layers defer importing heavy modules (derive, compact, ask, asyncio, Storage) to function bodies rather than module top-level, minimizing import-time overhead for CLI responsiveness. -
IN
storage-derives-counter-from-existing-nogoods
When loading a database without the `network_meta` table (pre-fix schema), `Storage.load()` derives `_next_nogood_id` from the max ID of existing nogoods rather than defaulting to 1, enabling backward-compatible upgrades -
IN
storage-fts-is-derived-index
The FTS5 full-text search index is rebuilt from scratch during every `save()`; it is a derived index, never the source of truth. -
IN
storage-fts-rebuilt-on-every-save
The `nodes_fts` full-text index is deleted and rebuilt from scratch on each `save()`, guaranteeing consistency with the `nodes` table at the cost of full-reindex overhead. -
IN
storage-handles-schema-evolution-via-try-except
Missing tables from older database versions (`repos`, `network_meta`) are handled by swallowing exceptions during `load()` rather than formal migrations — a backward-compatibility substitute that silently degrades. -
IN
storage-is-fully-production-grade-across-backends
Both storage backends achieve fully production-grade operation — concurrent access optimization (WAL mode, derived FTS5 indexes), equivalent safety guarantees (atomic isolated mutations through backend-appropriate mechanisms), and multi-tenant isolation — when both backends provide complete API coverage. -
IN
storage-justification-order-preserved
Justifications are inserted in list order and loaded via `ORDER BY rowid`, preserving the ordering that determines which justification is evaluated first during truth computation. -
IN
storage-lists-as-json-columns
Antecedents, outlist, nogood node sets, and metadata dicts are stored as JSON text columns rather than normalized join tables, simplifying the schema at the cost of individual field queryability. -
IN
storage-load-bypasses-add-node
`load()` assigns nodes directly to `network.nodes` and calls `_rebuild_dependents()` afterward, deliberately skipping `add_node()` to avoid triggering truth maintenance propagation during state restoration. -
IN
storage-load-bypasses-propagation
`load()` constructs nodes directly into `network.nodes` rather than calling `add_node`, so truth maintenance propagation does not fire during deserialization. -
IN
storage-no-partial-load
`Storage.load()` either returns a complete `Network` with all state or fails; there is no streaming, lazy-loading, or partial deserialization. -
IN
storage-old-schema-compat
`load()` tolerates missing `network_meta` and `repos` tables via silent `try/except`, supporting databases created before those tables were added; `next_nogood_id` is derived from existing IDs as a fallback. -
IN
storage-optimizes-concurrent-access-and-search
The storage layer optimizes for both concurrent access (WAL mode enables non-blocking reads during writes) and full-text search (derived FTS5 index rebuilt from scratch on every save guarantees consistency), making the persistence layer production-ready for multi-reader workloads with search capability. -
IN
storage-round-trip-preserves-dependents
Covered by existing belief `persistence-round-trip-is-lossless` — dependents are part of the persisted state -
IN
storage-save-is-atomic-snapshot
Duplicate of existing `storage-save-is-full-replace` and `mutation-pipeline-is-atomic-snapshot` -
IN
storage-save-is-full-replace
`Storage.save()` deletes all rows from every table before re-inserting the entire network; there is no incremental or differential update path. -
IN
storage-trusts-stored-truth-values
`load()` trusts the stored `truth_value` without re-running propagation, making the database the source of truth for node status. -
IN
storage-uses-json-columns-for-lists
Antecedent IDs, outlist IDs, nogood node sets, and node metadata are stored as JSON text columns rather than junction tables — acceptable because the code always loads the full network, never queries relationships via SQL joins. -
IN
storage-uses-wal-mode
SQLite connections enable WAL mode on initialization, allowing concurrent readers without blocking writes. -
IN
supersession-is-reversible
`supersede()` adds the new node's ID to the old node's outlist rather than deleting the old node; retracting the new belief automatically restores the old one through normal propagation -
IN
supersession-is-reversible-and-view-consistent
Supersession is both mechanically reversible (implemented via outlist, so retracting the superseder restores the original node's truth value) and view-consistent (superseded nodes are excluded from gated belief lists even if they retain active blockers), making it a first-class lifecycle operation rather than just a truth-value toggle. -
IN
sync-is-remote-wins
`_sync_claims` implements remote-wins reconciliation: remote text/metadata overwrites local, beliefs removed from remote are retracted locally, and beliefs remotely IN but locally OUT are re-asserted -
IN
sync-preserves-cascade-wiring
After `sync_agent` runs, the `agent:inactive` outlist entries on beliefs are preserved, so retracting `agent:active` still cascades all agent beliefs to OUT -
IN
sync-returns-structured-diff-counts
`sync_agent` returns a dict with `beliefs_added`, `beliefs_updated`, `beliefs_unchanged`, and `beliefs_removed` counts that accurately reflect the diff between remote file and local state — no double-counting across sync cycles. -
IN
tag-inheritance-is-transitive-union
Derived nodes inherit the sorted union of all ancestor `access_tags`, propagating transitively through arbitrarily long justification chains including diamond dependencies. -
IN
tag-propagation-is-dynamic
Adding a justification to an existing node triggers access tag recomputation that cascades through all downstream dependents via BFS, not just at node creation time. -
IN
tests-verify-via-output-headers
Test methodology detail (how assertions parse output), not a codebase architectural invariant -
IN
three-layer-architecture
The codebase is a three-layer stack: data model (`__init__.py`), TMS engine (`network.py`), and persistence (`storage.py`), with `api.py` providing functional API and `cli.py` as a thin argparse wrapper. -
IN
three-layer-stack-has-clean-boundaries
The architecture enforces strict layer separation: pure data model at bottom, context-managed API with dict returns in the middle, and pure-formatter CLI at the top -
OUT
token-budgets-are-accurate-bidirectionally
Token budget management is accurate in both directions: the compact module reliably constrains output size for context-limited consumers, while the derive pipeline correctly allocates input budgets per agent — ensuring resource-bounded operation across the entire LLM integration surface. -
IN
topo-sort-breaks-cycles
Duplicates existing belief `import-topo-sort-tolerates-cycles`. -
OUT
total-preservation-is-indefinitely-auditable
Total invariant preservation — comprehensive in scope and self-sustaining through minimality — is accompanied by indefinite auditability: every invariant-preserving action across all time leaves traceable history without temporal degradation, meaning the system can prove its own correctness at any point. -
IN
transaction-per-function
Every API function opens the database, does its work, and closes — no shared state, no connection pooling, no long-lived sessions; each invocation is fully independent. -
OUT
transformations-converge-to-documented-equilibria
All structural transformations — mode expansion, negation semantics, identity transformation — are evaluation-transparent (producing identical truth regardless of transformation path), and the system autonomously converges to equilibria that generate deterministic traceable artifacts, meaning any sequence of transformations reaches the same documented stable state. -
OUT
transformations-produce-governed-traceable-output
Every structural transformation — mode expansion, negation semantics, identity transformation — is deterministic, traceable, and boundary-safe, and all transformation results flow through comprehensive self-sustaining output governance that normalizes, authorizes, and self-corrects delivery — creating an end-to-end governed pipeline from belief mutation through information delivery. -
IN
truncated-hash-threshold-is-16-chars
A stored `source_hash` of exactly 16 characters that is a prefix of the current full SHA-256 hash is classified as `truncated_hash` (legacy format), not `content_changed`. -
IN
truncated-hash-upgrade-opt-in
Prefix hash upgrade (16-char truncated SHA-256 to full digest) only occurs when `upgrade_hashes=True` is passed to `check_stale`; without it, truncated hashes produce a warning result with `reason="truncated_hash"`. -
OUT
trust-and-information-boundaries-are-comprehensively-enforced
The system enforces comprehensive boundaries spanning both architecture and information flow: architectural trust boundaries through self-containment and defensive ingestion pipelines ensure no unvalidated data enters the network, while information boundaries through access-tag authorization, token-budget constraints, and bidirectional external surface control ensure no unauthorized or unbounded data leaves it -
OUT
trust-boundary-is-architecturally-enforced
The system's trust boundary is architecturally enforced through complementary internal and external mechanisms: internal self-containment (zero external dependencies, clean three-layer boundaries) eliminates supply-chain and cross-layer attack surfaces, while defensive external containment (layered validation pipelines, namespace isolation, agent kill-switches) prevents untrusted input from corrupting internal state -
OUT
trust-enforcement-is-structural-and-operationally-resilient
System trust boundaries are enforced through two complementary mechanisms: structural containment (zero external dependencies, defensive ingestion pipelines, safe three-layer architecture) provides static trust guarantees, while format resilience at all external interfaces (parser fallbacks, schema evolution tolerance, hallucination filtering) provides dynamic trust that adapts to changing external formats without relaxing validation. -
OUT
trustworthiness-is-verifiable-through-observability
The revision system's complete trustworthiness (verifiable soundness, end-to-end reliability, full auditability) is independently verifiable because the minimality-sustained maintenance loop provides complete observability — every self-correction and maintenance action leaves traceable evidence that can be inspected. -
OUT
truth-evaluation-is-transformation-invariant
Truth evaluation produces identical results regardless of both temporal context (when a justification was attached — at node creation vs. later addition) and structural transformation (premise → justified node via challenge) — all forms of node history and identity change are invisible to the evaluation function, making truth a pure function of current network state. -
IN
truth-is-disjunctive-over-conjunctive-rules
A node's truth is a disjunction over justifications (any valid justification makes it IN), where each justification is a conjunction (all antecedents IN and all outlist OUT), and any-mode explicitly reifies OR semantics as per-premise justifications. -
IN
truth-semantics-are-emergent-and-uniform
Truth maintenance semantics are fully emergent from simple uniform rules: premise behavior arises from empty justification lists, evaluation is pure and type-agnostic across SL/CP, and node truth is a clean disjunction-of-conjunctions — no special cases exist anywhere in the evaluation path. -
IN
untagged-always-visible
Nodes with no `access_tags` key in metadata are never filtered by `visible_to` — they are treated as unconditionally public. -
IN
untagged-nodes-always-visible
Nodes without `access_tags` metadata pass all `visible_to` filters unconditionally and are never hidden by access control. -
IN
update-node-preserves-justifications
`api.update_node` modifies text and source metadata without altering the node's justification list or truth value. -
OUT
user-interface-is-verified-and-fault-tolerant
The complete user-facing stack is both structurally verified (pure delegation with hermetic integration tests ensuring no business logic leaks into the CLI) and operationally resilient (every information flow path is fault-tolerant with graceful degradation and governed output) — users never encounter unverified logic or ungoverned failure modes. -
IN
validate-proposals-rejects-jaccard-similar-to-out
`validate_proposals` skips any proposal whose tokenized ID has >= 0.5 Jaccard similarity to an existing OUT belief, returning "similar to retracted" in the skip reason — preventing re-derivation of retracted beliefs under variant names -
OUT
verified-correctness-has-indefinitely-auditable-equilibria
Verified production correctness — observable and permanently documented across all belief origins with deterministic state trajectories — converges to equilibria that are themselves trajectory-documented and indefinitely auditable, creating a self-reinforcing documentation loop where correctness verification and equilibrium auditability share the same permanent evidence base. -
OUT
verified-correctness-is-indefinitely-observable
Verified production correctness — spanning all belief origins with deterministic state trajectories — is both independently observable through the fully characterized maintenance loop and indefinitely sustainable through minimality's fixed-point self-maintenance, ensuring correctness can be verified at any future point without temporal degradation of either the correctness or the ability to observe it. -
OUT
verified-correctness-is-independently-observable
Production correctness verified across all belief origins is independently observable through the fully characterized maintenance loop — every correctness claim can be audited by inspecting the same deterministic traceable history that the maintenance loop produces, without trusting the system's self-assessment. -
OUT
verified-correctness-is-observable-and-permanently-documented
Verified production correctness spanning all belief origins is both independently observable (every correction is visible through the fully characterized maintenance loop without requiring trust in the system's self-reports) and permanently documented (every state change produces comprehensive artifacts that survive persistence boundaries and format evolution) — correctness is not merely achieved but externally auditable with durable evidence. -
OUT
verified-correctness-is-permanently-documented
Verified production correctness spanning all belief origins is backed by a permanent, comprehensive audit trail — the system not only achieves correct state trajectories across all provenance boundaries, but permanently documents every self-correction that maintained that correctness, with no temporal degradation of either the guarantees or their documentation. -
OUT
verified-interface-controls-bidirectional-flow
All information flowing through the system's verified, fault-tolerant user interface is controlled in both directions: inbound through production-hardened LLM integration with bounded execution and fail-soft semantics, outbound through deterministic authorized access-controlled output — the verified interface serves as a trustworthy gateway that neither admits uncontrolled inputs nor emits unauthorized outputs. -
OUT
verified-mutation-correctness-across-boundaries
Every mutation source produces fully correct persisted state that preserves boundary-agnostic integrity — not just safe operation, but verified output correctness across internal/external boundaries and all source types — only when implementation-level defects in propagation and budget allocation are resolved. -
IN
verify-dependents-is-read-only
`Network.verify_dependents()` never modifies the dependents index; it returns a list of human-readable error strings without side effects -
IN
verify-dependents-is-readonly
`verify_dependents()` never modifies `node.dependents`; it only reads and reports discrepancies as a list of human-readable strings containing `"extra"` or `"missing"`. -
IN
version-is-manual
The version is statically declared in `pyproject.toml` with no dynamic version plugin, `__version__` import, or SCM-based versioning — it must be bumped manually. -
IN
visibility-is-subset-check
`_is_visible` requires ALL of a node's `access_tags` to be present in the caller's `visible_to` list (subset check, not intersection); nodes with no tags are always visible. -
IN
visible-to-is-optional-filter
`_parse_visible_to` returns `None` when `--visible-to` is absent, which the API interprets as "no access restriction"; it never defaults to an empty list. -
IN
visible-to-superset-semantics
A node with `access_tags: ["finance", "hr"]` is only visible to callers whose `visible_to` list contains both tags (superset/subset check, not intersection). -
IN
warning-log-contract-action-target-value
Dangling-dependent warnings in `net.log` have `action="warn"`, a `target` field with the ghost node ID, and a `value` string containing both `"dangling"` and the parent node ID. -
IN
warning-log-schema-stable
Dangling-dependent warnings use the dict schema `{action: "warn", target:, value: , timestamp: }` and tests enforce all four fields. -
IN
write-false-prevents-persistence
Functions using `_with_network(write=False)` can mutate the in-memory network (as `what_if_retract` does) but changes are never saved to SQLite; write-or-not is declared upfront and never conditional. -
IN
zero-runtime-dependencies
`ftl-reasons` declares `dependencies = []` in pyproject.toml; the entire `reasons_lib` package runs on Python's standard library alone (sqlite3, json, argparse); PostgreSQL support is opt-in via the `pg` extra -
IN
zero-runtime-deps
`ftl-reasons` has zero runtime dependencies; the entire library runs on Python's stdlib alone (SQLite via `sqlite3`, dataclasses, etc.).