Version. 0.1
Date. 2026-05-29
Status. Investigation / grounding record. Captures the concrete facts about how a future FORAY integration would attach to Loomworks, established by direct reading of the live dunin7-foray SDK, the FORAY protocol/attestation material, and the adapter deep-read. Produced to ground the source-identification (Memory-completion item C) scoping note so it does not re-derive these facts.
Provenance. Claude.ai session 2026-05-29, with Claude Code retrieving verbatim source from the M4 (/Users/dunin7/dunin7-foray, /Users/dunin7/dunin7-workforce) and the record's foray-reference/. Operator: Marvin Percival.
Relationship to existing docs. Distinct from loomworks-foray-ova-integration-strategy-investigation-v0_1 (which concerns the Loom/FORAY/OVA protocol triangle). This note concerns the concrete SDK contract and the data-completeness requirement it implies. Companion to the record's foray-reference/ deep-reads (protocol, adapter, challenge, primer).
This note records what a future FORAY integration actually needs from Loomworks, established by reading the real shipped SDK rather than the protocol spec or demo code. The headline finding: the live dunin7-foray SDK is far thinner than the FORAY v4.1 spec or the Manuka example suggests. Its entire notion of "who did this" is a single free-text string. Everything richer — actor kind, identity, credentials, the held-then-commit chain — has no typed home in the SDK and would travel inside an opaque JSON payload.
The consequence for Loomworks is clarifying, not complicating: Loomworks remains the system of record for the rich actor and provenance model; FORAY anchors a tamper-evident reflection of it. FORAY is a reflection layer, not a system of record — it can only structure and anchor data that already exists. So the design requirement for foundation work is data completeness: every source-and-commit point in Loomworks must natively capture, durably and in Loomworks' own terms, everything a FORAY attestation would need — so that when integration opens, the work is deriving the FORAY call from existing data, not discovering the data was never captured. This note states what that data is, against the real SDK surface.
It also records the FORAY attestation conceptual model (what attestation proves and does not prove), and the adapter finding (the demo adapters are not the integration target; the live in-process SDK is).
The load-bearing principle, from the FORAY adapter material (foray-reference/foray-adapter-deep-read-v0_1, 2026-05-24) and the live integration documentation: FORAY does not create, modify, or insert data. It reads what a source system already holds, structures it, and anchors a tamper-evident reflection. "FORAY can only organize, structure, and anchor data that already exists in your systems."
This sets the entire design posture for Loomworks. Loomworks does not bend its native data model to FORAY's shape. Loomworks perfects its own record — who asserted what, under what authority, when — and a future FORAY integration reflects that record. The work of "preparing for FORAY" is therefore making the Loomworks record FORAY-complete: ensuring the data a reflection needs is present and durable, and marking the points where the reflection will be emitted. It is not building FORAY structures inside Loomworks (a pattern the substrate-hygiene cleanup arc already removed).
There are two distinct things called "adapter" in FORAY material, and conflating them designs the wrong thing:
quickbooks-adapter.js / salesforce-adapter.js are these. They are demo-grade, not deployable (their own disclaimers), and they call an SDK shape (createArrangement, anchorToBlockchain) that was never implemented.dunin7-foray v3.0.0 (ForayClient), used in production by dunin7-workforce. PostgreSQL-backed, append-only, hash-chained event log.
The integration target is the second. The steer (from the adapter deep-read's Loomworks-implications section, and confirmed by reading the live code) is application-layer integration: the Loomworks engine imports ForayClient and calls it inline at lifecycle events, the way dunin7-workforce does. There is no separate adapter process; the application is the adapter. The Kaspathon createArrangement-style code is descriptive illustration, not a deployment template.
dunin7-foray v3.0.0)
Verbatim from /Users/dunin7/dunin7-foray/lib/foray-client.mjs (373 lines), read 2026-05-29.
emit(chainId, eventType, triggeredBy, payload = {})
chainId — string. Per-chain partition key (frame id in Workforce; engagement id in the Loomworks framing). Stored as frame_id.eventType — string, free-form (e.g. 'frame.open', 'attestation'). No enum enforced.triggeredBy — string. The SDK's entire notion of "who." (See §4.)payload — object, optional, stored as JSONB, any shape.{ frame_id, sequence, event_type, triggered_by, payload, prev_hash, timestamp, hash }.
attest(chainId, attestation)
attest() is a thin wrapper over emit(). It does no validation and enforces no schema. It reads attestation.attestor (falling back to 'system') for the actor string, then stores the entire attestation object verbatim as the JSONB payload under event_type = 'attestation'. The documented attestation shape is a JSDoc literal only: { type, attestor, attestor_type, subject_refs, outcome, summary, evidence, metadata }.
CREATE TABLE IF NOT EXISTS foray_events (
id SERIAL PRIMARY KEY,
frame_id TEXT NOT NULL,
sequence INTEGER NOT NULL,
event_type TEXT NOT NULL,
triggered_by TEXT NOT NULL,
payload JSONB NOT NULL DEFAULT '{}',
prev_hash TEXT NOT NULL,
hash TEXT NOT NULL,
hash_timestamp TEXT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (frame_id, sequence)
);
foray_workforce, foray_forge, etc.) — separate database, not separate table.chainId.hash is SHA-256 over the canonical-JSON of {frame_id, sequence, event_type, triggered_by, payload, prev_hash, timestamp}; prev_hash links to the previous event (64 zeroes for the first). Re-verifiable deterministically.triggered_by TEXT field.
getAttestations(chainId, {type, attestor, outcome, limit, offset}), getAttestationsFor(chainId, subjectType, subjectId), getAttestationsByType(chainId, type), getAttestationsByAttestor(chainId, attestor). Retrieval only ever filters on payload->>'type', payload->>'attestor', payload->>'outcome', and payload->'subject_refs' (JSONB containment, where a subject ref is {type, id}). Those four are the de-facto queryable attestation fields; anything else in the payload is stored but not indexed for query.
lib/attestation.mjs exports, for callers to use (the SDK does not check payloads against them):
ATTESTATION_TYPES: certification, inspection, analysis, audit_opinion, verification, oracleATTESTOR_TYPES: human, automated, system, externalOUTCOMES: certified, passed, failed, inconclusive, not_applicable, acknowledged
The SDK does not hash or obfuscate payload identifiers; it stores the payload as-is. The only hashing it does is the chain-integrity hash (tamper-evidence, not party-obfuscation). Party-identifier hashing or amount obfuscation, if needed, is the caller's responsibility, done before emit/attest. The package provides opt-in helpers (hashObject, subjectRef, evidenceRef) the caller may use.
dunin7-workforce)
One ForayClient per product, resolved via foray.for(product). Real call sites use a small free-text actor vocabulary — role labels, not IDs or structured actors:
forayClient.emit(frameId, 'frame.open', 'conductor', { brief_title, budget_ceiling, requirements_count })
foray.workforce.emit(GOV, 'project.created', 'system', { ... })
forayClient.emit(frameId, 'frame.rollback', 'human', { ... })
ctx.foray.emit(ctx.frameId, 'strategist.estimate_complete', 'strategist', { ... })
attest() is not exercised anywhere in dunin7-workforce — production uses emit() exclusively. So the attestation surface, while present in the SDK, has no live production precedent; Loomworks would be an early/first user of it.
The SDK's entire model of "who" is one free-text string (triggeredBy). Loomworks already has a far richer actor model — ActorRef with kind ∈ {contributor, agent, person, companion}, an actor UUID, instruction_version, and (Phase 45) capability_ref/approval_mode. None of that has a typed home in the SDK.
The implication is not a problem to solve in the SDK; it is a division of responsibility to honor:
ActorRef (and its credentials/designation context) lives in Loomworks Memory, durably, queryable in Loomworks' own terms.triggeredBy string from the ActorRef, and (b) a structured actor object embedded in the payload JSONB for anything richer it wants anchored. The SDK won't model it; Loomworks decides what to put in the payload.
This is what "FORAY-complete" means concretely: the Loomworks-native record must be rich enough, at every source-and-commit point, to derive both the triggeredBy string and whatever structured actor/attestation payload a future integration anchors — at any time, including retroactively. If a datum FORAY would want (the model version that produced AI content, the Domain Expert's recognition basis, the commit's WebAuthn attestation) is not captured natively at the moment of the event, the reflection has a permanent hole, because FORAY cannot create data that was never recorded.
From the FORAY attestation material (foray-reference/foray-protocol-deep-read-v0_1, foray-protocol-primer-for-loomworks-substrate-v0_1), grounded in the Manuka Honey example.
The pivotal framing: FORAY does not certify the honey is Manuka — it certifies that the lab analyzed it, that the certifier certified the lab's analysis, and that the regulator approved the certification for export. Attestation records who made a specific claim, at a specific time, under specific credentials, and that it has not been altered since. It is a Type 2 transaction — claims about reality made by identified parties.
| Attestation proves | Attestation does NOT prove | |---|---| | A specific party made a specific claim at a specific time | The claim is true | | The claim has not been altered since anchoring | The attestor is competent | | The attestor's credentials are recorded | Physical reality matches the digital record |
Two structural facts that matter for Loomworks:
subject_refs/dependencies). This is structurally how Loomworks' held-then-commit ceremony maps: an AI/agent's contribution is one attestation-shaped event; the Operator's commit is a second that references the first. The commit does not replace the contribution's provenance — it chains onto it, preserving the "AI contributed, human committed" trajectory in an anchorable form.certification, inspection, analysis, audit_opinion, verification, oracle) and attestor types (human, automated, system, external) align with Loomworks' source-kinds and acts: an AI/agent contribution is oracle/automated-shaped; a Domain Expert recognition is certification-shaped; an Operator commit is verification/approval-shaped. This is why "AI cannot commit" is load-bearing: an AI can produce an analysis/oracle contribution but can never be the attestor of the approval at commit.Source identification plus FORAY-completeness gives accountability and tamper-evidence — who asserted what, under what authority, unaltered, with a chained trajectory through to commit. It does not give correctness (that the claim is true) or competence (that the source was qualified). That boundary should be stated wherever the foundation's value is described, so it is not overclaimed.
ForayClient.emit/attest), called application-layer inside the engine — not the demo adapters, not blockchain anchoring (which dunin7-foray v3.0.0 does not yet implement).triggeredBy + a structured payload at any time, retroactively included.dunin7-foray is taken in item C (option (a)): item C ensures data completeness and places reserved-location seams; the actual ForayClient wiring is a later, separately-scoped FORAY-integration phase.ForayClient in a Loomworks service layer or imports it directly (deferred to that phase).chainId granularity for Loomworks (per-engagement is the natural mapping, but per-Operator/per-tenancy variants exist in the workforce pattern — deferred).
v0.1 (2026-05-29). Initial grounding record. Captures the live dunin7-foray v3.0.0 SDK contract verbatim, the reflection-layer principle, the adapter finding (demo vs. live target), the actor-string-vs-ActorRef gap, the attestation conceptual model from the Manuka example, and the seven items this grounds for source-identification scoping.
DUNIN7 — Done In Seven LLC — Miami, Florida Loomworks FORAY Integration Grounding — Investigation v0.1 — 2026-05-29