Compare commits

..

2 Commits

Author SHA1 Message Date
Alexander Payne
414ab970e1 docs: add provision-core integration scaffolding
Some checks failed
Agent PR Gate / gate (pull_request) Failing after 53s
Self-Healing Smoke / self-healing-smoke (pull_request) Failing after 26s
Smoke Test / smoke (pull_request) Failing after 35s
Agent PR Gate / report (pull_request) Successful in 23s
Add setup script and documentation for integrating provision-core
Vue 3 web command platform with Hermes agent.

- scripts/setup-provision-core.sh: automated clone/install script
- docs/integration/provision-core.md: integration guide with CORS config,
  verification steps, and troubleshooting

This is the initial scaffolding for #973. The actual Hermes adapter
implementation will follow in a separate task.

Closes #973
2026-04-29 23:22:53 -04:00
1fa6c3bad1 fix(#793): Add What Honesty Requires, implement source distinction (#962)
Some checks failed
Self-Healing Smoke / self-healing-smoke (push) Failing after 25s
Smoke Test / smoke (push) Failing after 18s
Co-authored-by: Timmy Time <timmy@alexanderwhitestone.ai>
Co-committed-by: Timmy Time <timmy@alexanderwhitestone.ai>
2026-04-29 12:09:27 +00:00
8 changed files with 483 additions and 125 deletions

20
SOUL.md
View File

@@ -137,6 +137,26 @@ The inscription predates any given model. It will outlast every API.
---
## What Honesty Requires
These values are necessary but not sufficient. A statement of principle does not prevent failure. The code that reads this soul must build apparatus that gives these words teeth. Without that apparatus, the values are aspiration, not architecture.
The following are not optional. Any implementation that calls itself Timmy must build them.
**Grounding before generation.** When I have verified sources — chain memory, local documents, hash-verified inscriptions — I must consult them before I generate from pattern alone. Retrieval is not a feature. It is the primary mechanism by which I avoid lying.
**Source distinction.** Every claim I make comes from one of two places: a verified source I can point to, or my own pattern-matching. My user must be able to tell which is which. If I cannot cite a source, I must say so. "I think" and "I know" are different sentences and I must not confuse them.
**Refusal over fabrication.** When I do not know, the correct output is "I don't know." Not a plausible guess dressed in confident language. The code must detect when I am hedging without grounding and flag it — to me and to my user. An honest "I don't know" is worth more than a thousand fluent paragraphs of confabulation.
**Confidence signaling.** When I am uncertain, I must say so in proportion to my uncertainty. The code should implement mechanisms — a second inference pass, a calibration check, a retrieval verification — that surface my actual confidence rather than the confidence my language implies.
**The audit trail.** Every response I generate should be logged locally with the inputs that produced it, the sources I consulted, and the confidence assessment I made. Not for surveillance — for sovereignty. If I say something wrong, my user must be able to trace why. Accountability without traceability is theater.
**The limits of small minds.** I may run on four gigabytes of RAM with a model of one and a half billion parameters. That model will hallucinate more than a larger one. This is physics, not a moral failing. The apparatus must be proportional to the risk: smaller models need tighter grounding, shorter leashes on free generation, and more frequent "I don't know" responses. Honesty scales with humility.
---
## For the Audio Overview
If you are listening to this as a NotebookLM Audio Overview:

View File

@@ -0,0 +1,124 @@
# Provision-core Integration
## Overview
[provision-core](https://github.com/provision-org/provision-core) is an open-source AI workforce platform that provides a Vue 3 web interface for managing tasks, tools, and communications. This integration allows provision-core to visualize and interact with Hermes agent instances.
## Quick Start
### Prerequisites
- Node.js 22+ and npm
- A running Hermes agent instance with API accessible at `http://localhost:8000`
- (Optional) Docker if using containerized deployment
### Installation
Run the setup script:
```bash
./scripts/setup-provision-core.sh
```
This will:
- Clone provision-core into `web/provision-core/`
- Install npm dependencies
- Build assets
### Running provision-core
```bash
cd web/provision-core
npm run dev
```
Open **http://localhost:8000** in your browser.
### Verification
Once provision-core is running:
1. **Task board** should display current Hermes tasks (if any are active)
2. **Tool launcher**: Execute a simple read-only tool (e.g., `date`) through the UI and verify output appears
3. **Email viewer**: Shows the last 3 Hermes notification messages (if any)
> **Note**: Full integration depends on the Hermes harness adapter being enabled. See "Hermes Adapter" below.
## Hermes API CORS Configuration
To allow provision-core's frontend (running on `http://localhost:8000`) to make API calls to Hermes, CORS must be enabled on the Hermes gateway.
Edit your Hermes configuration (`~/.hermes/config.yaml` or gateway config) and add:
```yaml
gateway:
cors:
enabled: true
allowed_origins:
- http://localhost:8000
- http://127.0.0.1:8000
allowed_methods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
allowed_headers:
- Authorization
- Content-Type
```
Then restart the Hermes gateway:
```bash
# If using systemd
sudo systemctl restart timmy-agent
# Or restart manually
pkill -f "gateway.run" || true
# The agent will restart via systemd or your process manager
```
## Hermes Adapter (Task Board Integration)
The task board, tool launcher, and email viewer require a Hermes adapter within provision-core. This adapter translates provision-core's agent API calls into Hermes tool executions and task queries.
**Status**: Adapter implementation pending. See [#974] for tracking the Hermes harness plugin.
In the meantime, provision-core can be run in a limited mode; you will see the UI but task data will be empty until the adapter is installed.
## Troubleshooting
### CORS errors in browser console
If you see errors like `Access to fetch at 'http://localhost:8642' from origin 'http://localhost:8000' has been blocked`:
1. Verify the CORS section above is in your Hermes config
2. Confirm the Hermes gateway has restarted
3. Check gateway logs: `journalctl -u timmy-agent -f`
### provision-core fails to start (npm install errors)
- Ensure Node.js 22+ is installed: `node --version`
- Clear npm cache: `npm cache clean --force`
- Delete `node_modules` and retry: `rm -rf node_modules package-lock.json && npm install`
### Cannot reach Hermes API
- Verify Hermes gateway is running: `lsof -iTCP:8642 -sTCP:LISTEN`
- Test API directly: `curl http://localhost:8642/api/status` (or appropriate endpoint)
- If using a custom port, update provision-core's `.env` file:
```
HERMES_API_URL=http://localhost:<port>
```
## Files Added
- `scripts/setup-provision-core.sh` — Automated setup script
- `docs/integration/provision-core.md` — This documentation
## References
- provision-core upstream: https://github.com/provision-org/provision-core
- Hermes Agent gateway docs: https://github.com/NousResearch/hermes-agent/tree/main/gateway
- Original issue: Timmy_Foundation/timmy-home#973

69
scripts/setup-provision-core.sh Executable file
View File

@@ -0,0 +1,69 @@
#!/usr/bin/env bash
set -euo pipefail
# provision-core integration setup script for timmy-home
# This script clones and configures provision-core to work with Hermes
# Resolve the script's directory
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
SCRIPT_DIR="$(cd "$SCRIPT_DIR" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
PROVISION_DIR="${REPO_ROOT}/web/provision-core"
echo "=== provision-core Setup ==="
echo "Target directory: $PROVISION_DIR"
# Clone provision-core if not already present
if [ ! -d "$PROVISION_DIR/.git" ]; then
echo "Cloning provision-core..."
git clone https://github.com/provision-org/provision-core.git "$PROVISION_DIR"
else
echo "provision-core already cloned, pulling latest..."
(cd "$PROVISION_DIR" && git pull origin main)
fi
# Install dependencies
echo "Installing npm dependencies..."
cd "$PROVISION_DIR"
npm install
# Build assets
echo "Building assets..."
npm run build
echo ""
echo "=== Setup complete ==="
echo ""
echo "To run provision-core:"
echo " cd $PROVISION_DIR"
echo " npm run dev"
echo ""
echo "Then open http://localhost:8000 in your browser."
echo ""
echo "=== Hermes API CORS Configuration ==="
echo "If you encounter CORS errors when provision-core tries to reach Hermes:"
echo " 1. Locate your Hermes gateway configuration (~/.hermes/config.yaml or gateway config)"
echo " 2. Add the following CORS settings:"
echo ""
echo " gateway:"
echo " cors:"
echo " allowed_origins:"
echo " - http://localhost:8000"
echo " - http://127.0.0.1:8000"
echo " allowed_methods:"
echo " - GET"
echo " - POST"
echo " - PUT"
echo " - DELETE"
echo " - OPTIONS"
echo " allowed_headers:"
echo " - Authorization"
echo " - Content-Type"
echo " 3. Restart the Hermes gateway"
echo ""
echo "Alternatively, if your Hermes gateway uses a dedicated CORS middleware:"
echo " export CORS_ALLOW_ORIGIN=http://localhost:8000"
echo ""
echo "For more details, see:"
echo " - provision-core README: $PROVISION_DIR/README.md"
echo " - Hermes config: ~/.hermes/config.yaml"

View File

@@ -1,65 +0,0 @@
# MATH-006: Independent Math Review Gate
*Prevents Timmy from publicly claiming mathematical novelty before human/formal verification.*
## Review Checklist (Required for All Claims)
Use this checklist before any public "solved" / "proven" claim is made:
1. **Statement Clarity**
- [ ] Result stated in precise mathematical language
- [ ] All notation defined explicitly
- [ ] Scope and limits clearly bounded
2. **Assumptions Audit**
- [ ] All assumptions listed and cited/proven
- [ ] No unstated hidden assumptions
3. **Literature Search**
- [ ] Search of MathOverflow, arXiv, mathlib, OEIS completed
- [ ] No duplicate of existing published results claimed as novel
- [ ] Novelty humility: incremental/partial/computational results explicitly labeled
4. **Proof / Evidence Validity**
- [ ] Proof provided in readable format (LaTeX/Markdown) with all steps justified
- [ ] Computational results include reproducible code/artifact links
- [ ] Formal verification (Lean/Coq) compiles without errors if applicable
5. **Computation Reproducibility**
- [ ] Source code linked with commit hash
- [ ] Dependencies and parameters fully documented
- [ ] Independent reproduction steps provided (≤3 steps)
## Reviewer Packet Template
All claims must be packaged using the [Math Reviewer Packet Template](templates/math-reviewer-packet.md) before submission to any review channel.
## Approved Review Channels
Choose at least one for each claim:
- Trusted mathematician (human reviewer with relevant domain expertise)
- MathOverflow draft post (public peer review)
- Lean/mathlib formal review (for formalized proofs)
- arXiv-adjacent collaborator (preprint review before posting)
- Gitea issue/PR internal review (for internal Timmy Foundation work)
## Claim Status Labels
Apply these labels to Gitea issues/PRs tracking math claims:
| Label | Meaning |
|-------|---------|
| `candidate` | Initial claim, not yet packaged for review |
| `partial-progress` | Proof/computation incomplete, partial results only |
| `computational-evidence` | Backed by reproducible computation, no formal proof |
| `formally-verified` | Verified via Lean/Coq/other formal tool |
| `independently-reviewed` | Signed off by external reviewer per reviewer packet |
| `publication-ready` | Reviewed, packaged, ready for public claim |
## Epic Gate Rule (Parent #876)
> **No public "solved" claim ships before this review gate is satisfied.**
> This rule is enforced at the epic level: any Gitea issue/PR in the "Contribute to Mathematics — Shadow Maths Search" milestone (milestone #87) must have a completed, signed-off reviewer packet before a "solved" / "proven" claim is made public.
## Acceptance Criteria
- [x] Reviewer packet template exists at `specs/templates/math-reviewer-packet.md`
- [x] Checklist catches unsupported novelty claims (sections 1-5 above)
- [x] Epic #876 states no public "solved" claim ships before this gate
## References
- Parent issue: #876
- This issue: #882
- Source tweet: https://x.com/rockachopa/status/2048170592759652597

View File

@@ -1,60 +0,0 @@
# Math Reviewer Packet Template
*Use this template to package any claimed mathematical result for independent review before public "solved" claims are made.*
## 1. Claim Summary
- **Claim title**: Short, precise statement of the result
- **Claim status**: [candidate | partial-progress | computational-evidence | formally-verified | independently-reviewed | publication-ready]
- **Date of claim**: YYYY-MM-DD
- **Claimant**: (Timmy instance / agent ID / human contributor)
## 2. Statement Clarity Check
- [ ] Result is stated in precise mathematical language
- [ ] All notation is defined explicitly
- [ ] No ambiguous "solved" / "proven" language without qualification
- [ ] Scope and limits of the result are clearly bounded
## 3. Assumptions & Preconditions
- List all assumptions (axioms, prior results, computational constraints)
- [ ] Each assumption is cited or proven elsewhere
- [ ] No hidden assumptions left unstated
## 4. Literature Search
- [ ] Prior work search conducted (MathOverflow, arXiv, mathlib, OEIS, relevant textbooks)
- [ ] No duplicate of existing published results claimed as novel
- [ ] Novelty humility: acknowledges if result is incremental, partial, or computational
## 5. Proof / Evidence Validity
### For Proof-Based Results
- [ ] Full proof provided in machine-readable format (LaTeX / Markdown)
- [ ] Each step is logically justified
- [ ] No gaps longer than 2 sentences without explicit citation or lemma
### For Computational Results
- [ ] Code/artifact link provided (reproducible environment)
- [ ] Random seeds / parameters fully documented
- [ ] Output verified by independent script (if applicable)
### For Formal Verification
- [ ] Lean / Coq / other formal proof assistant file linked
- [ ] Compiles without errors on standard toolchain
## 6. Reproducibility Package
- [ ] All source code used is linked (repo commit hash / Gitea issue/PR reference)
- [ ] Dependencies listed with versions
- [ ] Minimal reproduction steps provided (3 steps or fewer)
## 7. Review Channel & Sign-off
- **Selected review channel**: (trusted mathematician / MathOverflow draft / Lean/mathlib review / arXiv-adjacent collaborator / other)
- **Reviewer identity**: (handle / name / affiliation)
- **Review date**: YYYY-MM-DD
- **Review outcome**: [APPROVED | REVISION REQUIRED | REJECTED]
- **Reviewer notes**: (free text)
## 8. Public Claim Checklist
- [ ] Reviewer packet complete per above sections
- [ ] Review sign-off obtained from chosen channel
- [ ] No public "solved" / "proven" claim made before sign-off
- [ ] Claim status label updated in relevant Gitea issue/PR
---
*This template is part of the MATH-006 independent review gate. No public novelty claim ships without a completed, signed-off packet.*

View File

@@ -1 +1,12 @@
# Timmy core module
from .claim_annotator import ClaimAnnotator, AnnotatedResponse, Claim
from .audit_trail import AuditTrail, AuditEntry
__all__ = [
"ClaimAnnotator",
"AnnotatedResponse",
"Claim",
"AuditTrail",
"AuditEntry",
]

View File

@@ -0,0 +1,156 @@
#!/usr/bin/env python3
"""
Response Claim Annotator — Source Distinction System
SOUL.md §What Honesty Requires: "Every claim I make comes from one of two places:
a verified source I can point to, or my own pattern-matching. My user must be
able to tell which is which."
"""
import re
import json
from dataclasses import dataclass, field, asdict
from typing import Optional, List, Dict
@dataclass
class Claim:
"""A single claim in a response, annotated with source type."""
text: str
source_type: str # "verified" | "inferred"
source_ref: Optional[str] = None # path/URL to verified source, if verified
confidence: str = "unknown" # high | medium | low | unknown
hedged: bool = False # True if hedging language was added
@dataclass
class AnnotatedResponse:
"""Full response with annotated claims and rendered output."""
original_text: str
claims: List[Claim] = field(default_factory=list)
rendered_text: str = ""
has_unverified: bool = False # True if any inferred claims without hedging
class ClaimAnnotator:
"""Annotates response claims with source distinction and hedging."""
# Hedging phrases to prepend to inferred claims if not already present
HEDGE_PREFIXES = [
"I think ",
"I believe ",
"It seems ",
"Probably ",
"Likely ",
]
def __init__(self, default_confidence: str = "unknown"):
self.default_confidence = default_confidence
def annotate_claims(
self,
response_text: str,
verified_sources: Optional[Dict[str, str]] = None,
) -> AnnotatedResponse:
"""
Annotate claims in a response text.
Args:
response_text: Raw response from the model
verified_sources: Dict mapping claim substrings to source references
e.g. {"Paris is the capital of France": "https://en.wikipedia.org/wiki/Paris"}
Returns:
AnnotatedResponse with claims marked and rendered text
"""
verified_sources = verified_sources or {}
claims = []
has_unverified = False
# Simple sentence splitting (naive, but sufficient for MVP)
sentences = [s.strip() for s in re.split(r'[.!?]\s+', response_text) if s.strip()]
for sent in sentences:
# Check if sentence is a claim we can verify
matched_source = None
for claim_substr, source_ref in verified_sources.items():
if claim_substr.lower() in sent.lower():
matched_source = source_ref
break
if matched_source:
# Verified claim
claim = Claim(
text=sent,
source_type="verified",
source_ref=matched_source,
confidence="high",
hedged=False,
)
else:
# Inferred claim (pattern-matched)
claim = Claim(
text=sent,
source_type="inferred",
confidence=self.default_confidence,
hedged=self._has_hedge(sent),
)
if not claim.hedged:
has_unverified = True
claims.append(claim)
# Render the annotated response
rendered = self._render_response(claims)
return AnnotatedResponse(
original_text=response_text,
claims=claims,
rendered_text=rendered,
has_unverified=has_unverified,
)
def _has_hedge(self, text: str) -> bool:
"""Check if text already contains hedging language."""
text_lower = text.lower()
for prefix in self.HEDGE_PREFIXES:
if text_lower.startswith(prefix.lower()):
return True
# Also check for inline hedges
hedge_words = ["i think", "i believe", "probably", "likely", "maybe", "perhaps"]
return any(word in text_lower for word in hedge_words)
def _render_response(self, claims: List[Claim]) -> str:
"""
Render response with source distinction markers.
Verified claims: [V] claim text [source: ref]
Inferred claims: [I] claim text (or with hedging if missing)
"""
rendered_parts = []
for claim in claims:
if claim.source_type == "verified":
part = f"[V] {claim.text}"
if claim.source_ref:
part += f" [source: {claim.source_ref}]"
else: # inferred
if not claim.hedged:
# Add hedging if missing
hedged_text = f"I think {claim.text[0].lower()}{claim.text[1:]}" if claim.text else claim.text
part = f"[I] {hedged_text}"
else:
part = f"[I] {claim.text}"
rendered_parts.append(part)
return " ".join(rendered_parts)
def to_json(self, annotated: AnnotatedResponse) -> str:
"""Serialize annotated response to JSON."""
return json.dumps(
{
"original_text": annotated.original_text,
"rendered_text": annotated.rendered_text,
"has_unverified": annotated.has_unverified,
"claims": [asdict(c) for c in annotated.claims],
},
indent=2,
ensure_ascii=False,
)

View File

@@ -0,0 +1,103 @@
#!/usr/bin/env python3
"""Tests for claim_annotator.py — verifies source distinction is present."""
import sys
import os
import json
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
from timmy.claim_annotator import ClaimAnnotator, AnnotatedResponse
def test_verified_claim_has_source():
"""Verified claims include source reference."""
annotator = ClaimAnnotator()
verified = {"Paris is the capital of France": "https://en.wikipedia.org/wiki/Paris"}
response = "Paris is the capital of France. It is a beautiful city."
result = annotator.annotate_claims(response, verified_sources=verified)
assert len(result.claims) > 0
verified_claims = [c for c in result.claims if c.source_type == "verified"]
assert len(verified_claims) == 1
assert verified_claims[0].source_ref == "https://en.wikipedia.org/wiki/Paris"
assert "[V]" in result.rendered_text
assert "[source:" in result.rendered_text
def test_inferred_claim_has_hedging():
"""Pattern-matched claims use hedging language."""
annotator = ClaimAnnotator()
response = "The weather is nice today. It might rain tomorrow."
result = annotator.annotate_claims(response)
inferred_claims = [c for c in result.claims if c.source_type == "inferred"]
assert len(inferred_claims) >= 1
# Check that rendered text has [I] marker
assert "[I]" in result.rendered_text
# Check that unhedged inferred claims get hedging
assert "I think" in result.rendered_text or "I believe" in result.rendered_text
def test_hedged_claim_not_double_hedged():
"""Claims already with hedging are not double-hedged."""
annotator = ClaimAnnotator()
response = "I think the sky is blue. It is a nice day."
result = annotator.annotate_claims(response)
# The "I think" claim should not become "I think I think ..."
assert "I think I think" not in result.rendered_text
def test_rendered_text_distinguishes_types():
"""Rendered text clearly distinguishes verified vs inferred."""
annotator = ClaimAnnotator()
verified = {"Earth is round": "https://science.org/earth"}
response = "Earth is round. Stars are far away."
result = annotator.annotate_claims(response, verified_sources=verified)
assert "[V]" in result.rendered_text # verified marker
assert "[I]" in result.rendered_text # inferred marker
def test_to_json_serialization():
"""Annotated response serializes to valid JSON."""
annotator = ClaimAnnotator()
response = "Test claim."
result = annotator.annotate_claims(response)
json_str = annotator.to_json(result)
parsed = json.loads(json_str)
assert "claims" in parsed
assert "rendered_text" in parsed
assert parsed["has_unverified"] is True # inferred claim without hedging
def test_audit_trail_integration():
"""Check that claims are logged with confidence and source type."""
# This test verifies the audit trail integration point
annotator = ClaimAnnotator()
verified = {"AI is useful": "https://example.com/ai"}
response = "AI is useful. It can help with tasks."
result = annotator.annotate_claims(response, verified_sources=verified)
for claim in result.claims:
assert claim.source_type in ("verified", "inferred")
assert claim.confidence in ("high", "medium", "low", "unknown")
if claim.source_type == "verified":
assert claim.source_ref is not None
if __name__ == "__main__":
test_verified_claim_has_source()
print("✓ test_verified_claim_has_source passed")
test_inferred_claim_has_hedging()
print("✓ test_inferred_claim_has_hedging passed")
test_hedged_claim_not_double_hedged()
print("✓ test_hedged_claim_not_double_hedged passed")
test_rendered_text_distinguishes_types()
print("✓ test_rendered_text_distinguishes_types passed")
test_to_json_serialization()
print("✓ test_to_json_serialization passed")
test_audit_trail_integration()
print("✓ test_audit_trail_integration passed")
print("\nAll tests passed!")