Skip to content
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
41ac195
Add a "debug string" callback to LinkSyncAdapter interface
lucksus Jun 16, 2025
976ec6a
Implement debug string callback in p-diff-sync
lucksus Jun 16, 2025
3d3ac55
Add storing of debug strings in runtime controller and wire up callbacks
lucksus Jun 16, 2025
bb2b3d5
Add runtimeDebugStrings gql query
lucksus Jun 16, 2025
4c087bb
Unit tests for runtime service debug string functions
lucksus Jun 16, 2025
bcb1d95
Integration test for debug strings
lucksus Jun 17, 2025
b4c1951
Merge branch 'dev' into link-language-debugging
lucksus Jun 17, 2025
24ef2e7
Fix broken merge
lucksus Jun 18, 2025
79e624f
emit some more debug strings
lucksus Jun 18, 2025
0d45e1c
p-diff-sync: route new debug_string HC signals to linksAdapter
lucksus Jun 18, 2025
fc4b7dd
Fix debug_string integration tests to look at both alice's and bob's …
lucksus Jun 18, 2025
7e35093
fmt
lucksus Jun 18, 2025
fca322f
Fix Languages tab in launcher
lucksus Jun 18, 2025
83bfb69
UI in launcher for showing language debug strings
lucksus Jun 18, 2025
568b3a6
update bootstrap seed with new p-diff-sync build
lucksus Jun 18, 2025
0af4c1f
Merge branch 'dev' into link-language-debugging
lucksus Jun 19, 2025
425436b
Make PerspectiveState enum use camel-case also over graphql
lucksus Jun 23, 2025
ef2fb49
Use new test version of ad4m core in p-diff-sync with change Perspect…
lucksus Jun 23, 2025
36688b1
Print more info about gossip loop error in p-diff-sync
lucksus Jun 23, 2025
520893d
deno.lock
lucksus Jun 23, 2025
add91fc
bootstrap seed with latest p-diff-sync build
lucksus Jun 23, 2025
d4368aa
Handle j-modal's close button correctly
lucksus Jun 24, 2025
f2eb1f9
Fix build, use new turbo field-name "tasks" instead of "pipeline"
lucksus Jun 24, 2025
84d548e
Don't use hooks inside a map/loop
lucksus Jun 24, 2025
87dd0b7
comment-out new test to try fix CI
lucksus Jun 26, 2025
f909aac
Merge branch 'dev' into link-language-debugging
lucksus Jul 14, 2025
aff68d5
Upgrade ts-node and use 'transpile-only' for pulishTestLangs step
lucksus Jul 14, 2025
2515474
lock files
lucksus Jul 14, 2025
da3dc3a
workspace:* instead of link:../../core
lucksus Jul 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bootstrap-languages/p-diff-sync/hc-dna/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ name = "perspective_diff_sync"
[dependencies]
derive_more = "0"
serde = "1"
serde_json = "1"
lazy_static = "*"
chrono = { version = "0.4.38", default-features = false, features = ["clock", "std", "oldtime", "serde"] }
thiserror = "1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ pub fn pull<Retriever: PerspectiveDiffRetreiver>(
let mut workspace = Workspace::new();

if current.is_none() {
emit_signal(serde_json::json!({
"type": "debug_string",
"operation": "pull-info",
"debug_string": "No current revision, collecting from latest",
}))?;
workspace.collect_only_from_latest::<Retriever>(theirs.clone())?;
let diff = workspace.squashed_diff::<Retriever>()?;
update_current_revision::<Retriever>(theirs, get_now()?)?;
Expand All @@ -93,6 +98,9 @@ pub fn pull<Retriever: PerspectiveDiffRetreiver>(

workspace.build_diffs::<Retriever>(theirs.clone(), current.hash.clone())?;

// Generate and emit debug graph for visualization
workspace.emit_debug_graph("pull")?;

// First check if we are actually ahead of them -> we don't have to do anything
// they will have to merge with / or fast-forward to our current
if workspace.all_ancestors(&current.hash)?.contains(&theirs) {
Expand Down Expand Up @@ -152,6 +160,11 @@ pub fn pull<Retriever: PerspectiveDiffRetreiver>(

let (diffs, current_revision) = if fast_forward_possible {
debug!("===PerspectiveDiffSync.pull(): There are paths between current and latest, lets fast forward the changes we have missed!");
emit_signal(serde_json::json!({
"type": "debug_string",
"operation": "pull-info",
"debug_string": format!("Fast-forwarding from {} to {}", current.hash, theirs),
}))?;
let mut out = PerspectiveDiff {
additions: vec![],
removals: vec![],
Expand All @@ -169,6 +182,11 @@ pub fn pull<Retriever: PerspectiveDiffRetreiver>(
(out, theirs)
} else if is_scribe {
debug!("===PerspectiveDiffSync.pull():There are no paths between current and latest, we must merge current and latest");
emit_signal(serde_json::json!({
"type": "debug_string",
"operation": "pull-info",
"debug_string": format!("Merging their {} into my {}", theirs, current.hash,),
}))?;
//Get the entries we missed from unseen diff
let mut out = PerspectiveDiff {
additions: vec![],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,29 @@ impl Workspace {
}
}

pub fn generate_debug_graph(&self) -> String {
format!(
"{:?}",
Dot::with_config(
&self.graph.map(
|_node_index, node| { crate::retriever::hash_to_node_id(node.to_owned()) },
|_edge_index, _edge| {}
),
&[Config::EdgeNoLabel]
)
)
}

pub fn emit_debug_graph(&self, operation: &str) -> SocialContextResult<()> {
emit_signal(serde_json::json!({
"type": "debug_string",
"operation": operation,
"debug_string": self.generate_debug_graph(),
}))?;

Ok(())
}

pub fn all_ancestors(&self, child: &Hash) -> SocialContextResult<Vec<Hash>> {
//debug!("===Workspace.all_ancestors(): Function start");
//let fn_start = get_now()?.time();
Expand Down
2 changes: 1 addition & 1 deletion bootstrap-languages/p-diff-sync/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default async function create(context: LanguageContext): Promise<Language
}],
async (signal) => {
//@ts-ignore
if (signal.payload.reference || (signal.payload.additions && signal.payload.removals)) {
if (signal.payload.reference || (signal.payload.additions && signal.payload.removals) || signal.payload.type === "debug_string") {
await linksAdapter.handleHolochainSignal(signal)
} else {
for (const callback of telepresenceAdapter.signalCallbacks) {
Expand Down
24 changes: 20 additions & 4 deletions bootstrap-languages/p-diff-sync/linksAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { LinkSyncAdapter, PerspectiveDiffObserver, HolochainLanguageDelegate, LanguageContext, PerspectiveDiff,
LinkExpression, DID, Perspective, PerspectiveState } from "https://esm.sh/@perspect3vism/ad4m@0.5.0";
import type { SyncStateChangeObserver } from "https://esm.sh/@perspect3vism/ad4m@0.5.0";
LinkExpression, DID, Perspective, PerspectiveState, DebugGraphObserver, DebugStringObserver } from "https://esm.sh/@coasys/ad4m@0.10.1-release-candidate-4-debug-strings-2";
import type { SyncStateChangeObserver } from "https://esm.sh/@coasys/ad4m@0.10.1-release-candidate-4-debug-strings-2";
import { Mutex, withTimeout } from "https://esm.sh/[email protected]";
import { DNA_ROLE, ZOME_NAME } from "./build/happ.js";
import { encodeBase64 } from "https://deno.land/[email protected]/encoding/base64.ts";
Expand All @@ -16,6 +16,7 @@ export class LinkAdapter implements LinkSyncAdapter {
hcDna: HolochainLanguageDelegate;
linkCallback?: PerspectiveDiffObserver
syncStateChangeCallback?: SyncStateChangeObserver
debugStringCallback?: DebugStringObserver
peers: Map<DID, PeerInfo> = new Map();
generalMutex: Mutex = withTimeout(new Mutex(), 10000, new Error('PerspectiveDiffSync: generalMutex timeout'));
me: DID
Expand Down Expand Up @@ -182,7 +183,7 @@ export class LinkAdapter implements LinkSyncAdapter {
this.gossipLogCount = 0;
}
} catch (e) {
console.error("PerspectiveDiffSync.gossip(); got error", e);
console.error("PerspectiveDiffSync.gossip(); got error", e, e.stack, JSON.stringify(e));
} finally {
release();
}
Expand Down Expand Up @@ -250,10 +251,25 @@ export class LinkAdapter implements LinkSyncAdapter {
return 1;
}

addDebugStringCallback(callback: DebugStringObserver): number {
this.debugStringCallback = callback;
return 1;
}

async handleHolochainSignal(signal: any): Promise<void> {
// Check if this is a debug signal
if (signal.payload && signal.payload.type === "debug_string") {
console.log("PerspectiveDiffSync.handleHolochainSignal: signal.payload.type === 'debug_string'");
if (this.debugStringCallback) {
console.log("PerspectiveDiffSync.handleHolochainSignal: calling debugStringCallback");
this.debugStringCallback(signal.payload.debug_string, signal.payload.operation);
}
return;
}

const { reference_hash, reference, broadcast_author } = signal.payload;
if (reference_hash && reference && broadcast_author) {
//Check if this signal came from another agent & contains a reference and reference_hash
if (reference && reference_hash && broadcast_author) {
// console.log(`PerspectiveDiffSync.handleHolochainSignal:
// diff: ${JSON.stringify(diff)}
// reference_hash: ${reference_hash.toString('base64')}
Expand Down
2 changes: 1 addition & 1 deletion cli/mainnet_seed.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"did:key:z6MkvPpWxwXAnLtMcoc9sX7GEoJ96oNnQ3VcQJRLspNJfpE7"
],
"knownLinkLanguages": [
"QmzSYwdiuaBqw812TNcudpKwvU5U3HM9tdxWqjhkp26XEc228xe"
"QmzSYwdmF7y3Gj7v2Lu5Gm6dmukcbeCLoV3bFkxaujCEe1RtSzg"
],
"directMessageLanguage": "QmzSYwdob1TwkrGs5SzpS6UF2NpNBBzd3XSy2HpmaDnRPivNcE9",
"agentLanguage": "QmzSYwdZDdgxiyE8crozqbxoBP52h6ocMdDq2S2mg4ScjzVLWKQ",
Expand Down
8 changes: 6 additions & 2 deletions core/src/language/Language.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,9 @@ export interface GetAllAdapter {
getAll(filter: any, count: number, page: number): Promise<Expression[] | null>;
}

export type PerspectiveDiffObserver = (diff: PerspectiveDiff)=>void;
export type SyncStateChangeObserver = (state: PerspectiveState)=>void;
export type PerspectiveDiffObserver = (diff: PerspectiveDiff) => void;
export type SyncStateChangeObserver = (state: PerspectiveState) => void;
export type DebugStringObserver = (debugString: string, operation: string) => void;

/** Interface for "Link Languages" that facilitate the synchronization
* between agents' local Perspectives inside a Neighbourhood.
Expand Down Expand Up @@ -187,6 +188,9 @@ export interface LinkSyncAdapter {

/** Add a sync state callback method */
addSyncStateChangeCallback(callback: SyncStateChangeObserver);

/** Add a debug string callback method for capturing internal debug information */
addDebugStringCallback?(callback: DebugStringObserver);
}

export type MessageCallback = (message: PerspectiveExpression) => void;
Expand Down
12 changes: 6 additions & 6 deletions core/src/perspectives/PerspectiveHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { Field, ObjectType } from "type-graphql";
import { NeighbourhoodExpression } from "../neighbourhood/Neighbourhood";

export enum PerspectiveState {
Private = "PRIVATE",
NeighboudhoodCreationInitiated = "NEIGHBOURHOOD_CREATION_INITIATED",
NeighbourhoodJoinInitiated = "NEIGHBOURHOOD_JOIN_INITIATED",
LinkLanguageFailedToInstall = "LINK_LANGUAGE_FAILED_TO_INSTALL",
LinkLanguageInstalledButNotSynced = "LINK_LANGUAGE_INSTALLED_BUT_NOT_SYNCED",
Synced = "SYNCED",
Private = "Private",
NeighboudhoodCreationInitiated = "NeighboudhoodCreationInitiated",
NeighbourhoodJoinInitiated = "NeighbourhoodJoinInitiated",
LinkLanguageFailedToInstall = "LinkLanguageFailedToInstall",
LinkLanguageInstalledButNotSynced = "LinkLanguageInstalledButNotSynced",
Synced = "Synced",
}
// This type is used in the GraphQL interface to reference a mutable
// prespective that is implemented locally by the Ad4m runtime.
Expand Down
15 changes: 15 additions & 0 deletions core/src/runtime/RuntimeClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,21 @@ export class RuntimeClient {
return runtimeGetNetworkMetrics
}

async debugStrings(languageAddress?: string): Promise<any[]> {
const { runtimeDebugStrings } = unwrapApolloResult(await this.#apolloClient.query({
query: gql`query runtimeDebugStrings($languageAddress: String) {
runtimeDebugStrings(languageAddress: $languageAddress) {
languageAddress
operation
debugString
timestamp
}
}`,
variables: { languageAddress }
}))
return runtimeDebugStrings
}

async restartHolochain(): Promise<boolean> {
const { runtimeRestartHolochain } = unwrapApolloResult(await this.#apolloClient.mutate({
mutation: gql`mutation runtimeRestartHolochain {
Expand Down
26 changes: 26 additions & 0 deletions core/src/runtime/RuntimeResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,21 @@ export class TriggeredNotification {
triggerMatch: string;
}

@ObjectType()
export class DebugStringEntry {
@Field()
languageAddress: string;

@Field()
debugString: string;

@Field()
operation: string;

@Field()
timestamp: string;
}

@ObjectType()
export class ImportStats {
@Field()
Expand Down Expand Up @@ -239,6 +254,17 @@ export default class RuntimeResolver {
return ["Qm12345abcdef"]
}

@Query(returns => [DebugStringEntry])
runtimeDebugStrings(@Arg("languageAddress", type => String, {nullable: true}) languageAddress?: string): DebugStringEntry[] {
// Mock data for testing - in real implementation this would come from stored debug strings
return [{
languageAddress: "Qm123example",
debugString: "Debug string content",
operation: "merge",
timestamp: new Date().toISOString()
}]
}

@Mutation(returns => [String])
runtimeAddKnownLinkLanguageTemplates(@Arg("addresses", type => [String]) addresses: string[]): string[] {
return addresses
Expand Down
29 changes: 29 additions & 0 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions executor/src/core/LanguageController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,14 @@ export default class LanguageController {
this.callSyncStateChangeObservers(state, {address: hash, name: language.name} as LanguageRef);
})
}

if (language.linksAdapter.addDebugStringCallback) {
language.linksAdapter.addDebugStringCallback((debugString: string, operation: string) => {
// Store debug string via runtime service using the language hash
//@ts-ignore
RUNTIME_SERVICE.addDebugString(hash, debugString, operation);
})
}
}

if(language.telepresenceAdapter) {
Expand Down
9 changes: 9 additions & 0 deletions executor/src/runtime_service_extension.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ declare global {
friends(): Promise<string[]>;
addMessageOutbox(did: string, message: object, wasSent: boolean): Promise<void>;
getTrustedAgents(): Promise<string[]>;
addDebugString(languageAddress: string, debugString: string, operation: string): Promise<void>;
getDebugStrings(languageAddress?: string): Promise<DebugStringEntry[]>;
}

interface DebugStringEntry {
language_address: string;
debug_string: string;
operation: string;
timestamp: string;
}

const RUNTIME_SERVICE: RuntimeService;
Expand Down
Loading