Commit Graph
1105 Commits
Author SHA1 Message Date
ayushcodes10andClaude Sonnet 5 e2fd1c4f30 Update hash seed tests for the module based re exec and add a regression
The existing probe assertions now expect the module flag form. A new
test drives the probe with a launcher stub path that is not even a
real file in place of argv zero, confirming the fix no longer depends
on it being anything runnable, the exact shape of the Windows uv
launcher regression this closes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
2026-09-23 12:16:41 +05:30
ayushcodes10 f49ec5038c Add regression tests for the shared output graphify root marker fix
One test sets a shared, absolute GRAPHIFY_OUT and confirms the marker
written by an update from a relative path is resolved to an absolute
form. A companion test confirms the ordinary, relative GRAPHIFY_OUT
case still preserves the caller supplied relative path exactly as the
existing 777 test already expects, so this fix only changes the
shared output case.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 181a46dca9)
2026-09-22 17:30:28 +01:00
Ha1baraA11 78b42dfcdc fix(build): fall back from stale graphify root
(cherry picked from commit 1717695609)
2026-09-22 17:30:28 +01:00
Ha1baraA11 1db41f0bef fix(labeling): retry and report partial community labels
(cherry picked from commit 315974e0a5)
2026-09-22 17:30:28 +01:00
ayushcodes10 d8dd477836 Add regression tests for the install CRLF and idempotency guard fix
Cover an unrelated mention of the word graphify no longer suppressing
the real registration block, a stale or hand edited block correctly
getting refreshed on re install instead of being left alone, and both
the CLAUDE.md and GEMINI.md writers now passing newline equals empty
string so an existing file's own line endings are never translated on
write.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit ddf2497f68)
2026-09-22 17:30:28 +01:00
ahm3dwasim a1d2318e50 fix(serve): get_node/get_neighbors accept node_id/id, not just label
The two node-lookup MCP tools read arguments["label"] directly, so a client
that passes the node identifier under "node_id" (or "id") gets a
KeyError('label') instead of a result. Several of the agent frameworks graphify
advertises support for send the argument that way, so the tools are unusable
from those clients while query_graph/god_nodes work fine against the same graph.

Extract a small module-level _node_arg() that accepts label/node_id/id, return
a short guidance string when none is given (instead of raising), and declare
node_id in both inputSchemas so the parameter is discoverable. Add a unit test.

(cherry picked from commit cf84c59735)
2026-09-22 17:30:28 +01:00
oleksii-tumanov 7a508a71bd fix(cargo): resolve inherited workspace dependency identity
(cherry picked from commit 4d8fa2e93c)
2026-09-22 15:00:07 +01:00
ayushcodes10 9b42507014 Add regression tests for cargo subdirectory manifest discovery
Cover a Tauri style layout where the manifest lives one level down and
must still be found, a discovered workspace manifest whose own
members resolve relative to its own directory rather than the outer
scan root, a decoy manifest inside a dependency install folder that
must never be searched into, and a manifest nested past the discovery
depth cap that is correctly left unfound.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 6d2ef5455f)
2026-09-22 14:58:22 +01:00
ayushcodes10 137ead6d56 Add regression tests for cargo extraction with a missing manifest
One test runs extract with the cargo flag against a repo that has no
Cargo.toml at all and confirms it still succeeds and still writes the
graph built from the code already indexed, with a note explaining the
crate edges were skipped. A companion test confirms a repo that does
have a valid manifest still gets its crate node, so the fix only
changes the missing manifest path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit a9b14fef06)
2026-09-22 14:58:22 +01:00
ayushcodes10 890856f873 Add regression tests for the hash seed pinning fix
Since a real re exec replaces the process it runs in, every test that
needs to observe whether it fired runs a small probe in its own
subprocess with a deliberately constructed environment, rather than
mocking inside the pytest process itself, which pytest's own handling
of its current test marker would defeat anyway. Cover each of the
four affected commands re executing with the seed pinned when unset,
an explicit seed never being overridden, unrelated commands never re
executing, the guard against firing while pytest itself is mid test,
a failed re exec being survived rather than raised, and a full real
invocation of update still completing successfully end to end through
the whole re exec.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 2f5f2d705e)
2026-09-22 14:58:13 +01:00
carterko23 081004f60b fix(go): repoint intra-module imports_from edges from go_pkg_ sinks to the package's files
extractors/go.py mints every import target as `go_pkg_<raw import string>` with
no module lookup, so an intra-module import never reaches a file: the sink has
no source_file and nothing links it onward. `affected` and any reachability
walk miss every consumer that arrives through an import, while qualified calls
(`b.F()`) resolve, because the call pass already filters callees by
_go_import_path_for_file. This inverts that mapping once per run and repoints
the intra-module `imports_from` edges onto the imported package's file nodes,
the way the JS/TS resolver repoints module specifiers. Stdlib and external
imports have no file in the corpus and stay sinks by design.

Measured on a 744-file single-go.mod module, same commit, with and without
this change: a 111-file handler package goes from 0/21 direct intra-module
imports resolved to 21/21 (24/24 transitive); the 57 intra-module go_pkg_
nodes disappear; +16,616 imports_from edges; no other relation changes.

Closes #3746

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit fb40961f4a)
2026-09-22 14:58:12 +01:00
oleksii-tumanov f7bc904bda Preserve case-only interface signature owners
(cherry picked from commit 72d5914a10)
2026-09-22 14:58:12 +01:00
oleksii-tumanov bda82f9ded fix(go): preserve interface method signature references
(cherry picked from commit 00fb309983)
2026-09-22 14:58:12 +01:00
safishamsiandClaude Opus 4.8 0e855f3b40 fix: complete the Solidity/method-ghost/Erlang review items (#3716, #3719, #3714 follow-ups)
- #3716 (Solidity): non-callable members (struct/enum/event/error/state-var) had
  no signature and fell back to the LINE NUMBER for their node id, churning ids on
  incremental rebuilds. Use the member name (unique per kind in a contract scope)
  as the discriminator instead.
- #3719 (method-ghost dedup): gate the candidate index on a method-shaped label
  (leading '.' AND '()' suffix) so a dotfile label like '.env' is never treated as
  a method; normalize the index/lookup key via make_id for parity with the alias
  path. Adds a negative test proving two same-named methods in one file are left
  unmerged (the len==1 ambiguity guard).
- #3714 (Erlang): the remote-call resolver reads module/name/arity off node
  metadata, which the incremental context builders stripped for non-Ruby langs,
  so foo:bar() silently failed to resolve on 'graphify update'. Forward the Erlang
  resolution metadata keys through the watch.py/cli.py allow-lists (same fix shape
  as the elixir _elixir_module marker).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-21 19:44:17 +01:00
shobhitagnihotri69 3097e9d2fa fix(build): deduplicate spec-conformant method nodes and aliases (#3705)
(cherry picked from commit fefdb5f074)
2026-09-21 19:19:56 +01:00
Abdullah 89c27c7b80 feat: add Erlang language extractor
(cherry picked from commit 3a92853329)
2026-09-21 19:19:26 +01:00
Abdullah 9f069eafea feat: add Solidity language extractor
(cherry picked from commit 41a1575e5e)
2026-09-21 19:13:40 +01:00
Abdullah d68d6012bd feat: add R language extractor
(cherry picked from commit 250395de31)
2026-09-21 19:10:13 +01:00
Abdullah ebe1f32c81 feat: add VB.NET language extractor
(cherry picked from commit 304c82fe10)
2026-09-21 19:06:50 +01:00
Abdullah ab568c8f42 feat: add COBOL language extractor
(cherry picked from commit 6573ea6306)
2026-09-21 18:04:14 +01:00
shobhitagnihotri69 5da2da3978 fix(build): preserve higher confidence on same-relation edge collision (#3703)
(cherry picked from commit 0b5d0e96c7)
2026-09-21 18:04:04 +01:00
safishamsiandClaude Opus 4.8 20a20d30d8 test(ts): de-flake the normalizer scaling test that reddened v8 CI
The #3359 scaling guard flaked on CI 3.13 (3.2x vs a 3.0x ceiling): at n=1000 the
measurement (~0.03s) is small enough that a GC pause landing in process_time
inflates the ratio. Not a regression — the normalizer path is unchanged; it was
green on 0.9.64 by luck. Harden it: disable GC around the measured section,
measure at 4k/8k where O(n) work dominates and linear converges to a clean ~2x,
and relax the ceiling to 3.5x (still separates linear from quadratic 4x).
Observed locally: 2.10x, stable across repeated runs on 3.10 and 3.13.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-20 18:53:35 +01:00
safishamsiandClaude Opus 4.8 86d8aadd3b fix(export): complete the vis-network crash fix and pin the tooltip XSS boundary (#3699, #3686 follow-ups)
#3699: the Fermat-spiral seed positions referenced `i` inside RAW_NODES.map(n => ...)
which has no index param, so the emitted JS threw 'ReferenceError: i is not defined'
and graph.html failed to render at every graph size. Bind the index: map((n, i) => ...).

#3686: the tooltip `title` sinks were correctly un-escaped (vis-network renders a
string title via Popup.setText -> innerText, verified in the pinned 9.1.6 bundle:
its only non-Element branch is innerText and it has zero innerHTML=<var> sinks).
Documented that invariant at the sink as the #1838 stored-XSS boundary.

Adds two guards to test_export.py: a vis-network version pin assertion (a bump must
force re-verifying the innerText tooltip path) and a spiral-seed indexed-map guard
(catches the free-`i` regression, which the substring-only export tests missed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-20 18:07:23 +01:00
himanshupatro-334 785ee5a426 Fix HTML entity encoding in graph tooltips
(cherry picked from commit 88025d0a93)
2026-09-20 18:01:00 +01:00
safishamsiandClaude Opus 4.8 2ff5db3c37 test(watch): drop two broken #3695 symlink tests that never trigger fail-closed (#3697 follow-up)
The two test_issue_3695_symlink_worker_* tests monkeypatch _reconcile_existing_graph
to inject a deleted_source_identities collision, then assert the 'fail-closed: kept'
message prints — but on v8 that setup never reaches the corpus-sweep excluded-alive
branch, so the message never fires and the tests fail regardless of the fix. The
production fix stays genuinely guarded by the honest tests (fail_closed_preserved /
full_rebuild_preserves_alive / reextracted / shrink_involving), verified to fail
without the watch.py change and pass with it, plus the real-OS-symlink
test_requires_symlinks_rebuild_symlink_worker.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-20 15:07:29 +01:00
himanshupatro-334 c99c3a67bb Strengthen fail-closed regression assertions
(cherry picked from commit e7c15f0512)
2026-09-20 13:59:37 +01:00
himanshupatro-334 87a82770eb Fix fail-closed AST ownership eviction
(cherry picked from commit ae5654a8c2)
2026-09-20 13:59:37 +01:00
ayushcodes10 873b5ebde8 Add regression tests for block scoped let/const shadowing
Covers the issue's three reported shapes (const inside an if block,
a for of binding referenced after the loop, and a closure that
outlives the loop), the control case that must not regress (a
reference still inside its own loop), var's continued function wide
reach in the same shapes, and a genuine reference elsewhere still
resolving. The C style for loop case asserts on source_location since
both calls read as identical source text and a plain edge presence
check cannot tell "only the outside call resolved" from "both wrongly
resolved."

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 9801c0eac1)
2026-09-20 13:59:26 +01:00
rajatnagda45 199d741f67 fix(verilog): link a module instantiation to the local definition, not a phantom
A module instantiation minted its target id with a bare, unscoped
_make_id(name), while a module definition uses _make_id(stem, name). The
two never matched, so instantiating a module defined in the same file:

    module counter(...); endmodule
    module top;
      counter u1(...);
    endmodule

split `counter` into a real definition node and a second, sourced phantom
that carried the `instantiates` edge. `top`'s dependency on the module you
actually defined was invisible, and because the phantom was sourced it also
baked this file's path into the id and blocked the corpus-level rewire that
would otherwise collapse it (the #1402 duplicate shape the other
extractors guard against).

Pre-scan module names so a same-file instantiation (Verilog allows
forward/any-order instantiation) resolves to the definition id. A module
defined in another file becomes a SOURCELESS stub instead, so the
corpus-level rewire collapses it onto the real definition rather than a
per-file duplicate — mirroring the Go extractor's ensure_named_node.

Adds regression coverage for both the local-definition link and the
cross-file sourceless stub.

(cherry picked from commit bca8bbac72)
2026-09-20 13:59:25 +01:00
rajatnagda45 c049ea0a69 fix(java): attach enum body members to the enum, not the file
A Java enum wraps its fields, constructors and methods in an
enum_body_declarations node, nested under enum_body after the constant
list. The generic walker recurses into a class body keeping the enclosing
type as the parent scope, but an unknown wrapper node resets that scope to
None (an unknown wrapper usually IS a scope boundary). That reset orphaned
every enum method, field and constructor onto the file:

    public enum Planet {
        EARTH(5.976e+24), MARS(6.421e+23);
        private final double mass;
        Planet(double mass) { this.mass = mass; }
        public double surfaceGravity() { return 6.673e-11 * mass; }
    }

Planet became a bare list of constants; surfaceGravity was emitted as a
file-level function and the constructor was dropped entirely.

enum_body_declarations is not a scope of its own - its members belong to
the enum - so recurse through it transparently, preserving the enum as the
parent scope (mirrors the companion_object and ERROR handling already in
the walker). Constructors and methods now hang off the enum via method
edges and their bodies are walked as call-graph scopes.

Adds regression coverage: members attach to the enum and don't leak onto
the file, an inter-method call is captured, and the constant case_of edges
are untouched.

(cherry picked from commit 820018ee3c)
2026-09-20 13:59:25 +01:00
rajatnagda45 fc8e9e167f fix(swift): extract method requirements declared in a protocol body
tree-sitter-swift gives a protocol's body-less method requirement its own
node type, protocol_function_declaration, instead of reusing the
function_declaration used inside a class or struct. The Swift config's
function_types only listed function_declaration, so a protocol like

    protocol Drawable {
        func draw()
        func area() -> Double
    }

became an empty node - the method contract every conformer must implement
never entered the graph.

Add protocol_function_declaration to function_types and
function_boundary_types. Requirements now surface as .method() nodes hung
off the protocol, their signature type references (return/param types) are
captured, and they stay distinct from a conformer's implementation of the
same name. Stored property requirements are left alone, matching how
stored properties on a class are already handled.

Adds regression coverage for all three.

(cherry picked from commit 4300054291)
2026-09-20 13:59:25 +01:00
rajatnagda45 a9450a537e fix(go): extract method requirements declared in an interface body
The interface_type branch only handled interface embedding and generic
type-set constraints, so the method requirements that make up an
interface's contract were dropped. An interface such as

    type Shape interface {
        Area() float64
        Perimeter() (float64, error)
    }

became an empty node - Area and Perimeter never entered the graph, and
nothing could resolve against the interface's method set.

Walk the method_elem children too, mirroring the receiver-method path:
each requirement becomes a .Method() node hung off the interface via a
method edge. Embedding (type_elem) keeps emitting embeds/references, so
the two never blur.

Adds regression coverage: requirements surface as method nodes, an
embedded interface stays a heritage edge, and an interface method and a
concrete struct method of the same name remain distinct nodes.

(cherry picked from commit 739940a42b)
2026-09-20 13:59:25 +01:00
safishamsiandClaude Opus 4.8 650f47f526 fix(terraform): redact secret-named attribute values before they hit the graph (#3644 follow-up)
#3644 persists HCL block attributes into node metadata, which is serialized to
graph.json verbatim and surfaced to the model over MCP (query/get_node) with no
sanitize_metadata pass. A hardcoded credential in a .tf file (password,
db_password, aws_secret_access_key, client_secret, a connection string, ...)
would leak. Redact the VALUE of any attribute whose key names a secret while
keeping the key visible, so instance_type/ami queries still work and a user can
still see THAT a secret is set. Recurses into map values so a nested secret in a
tags/connection map is redacted too.

Also notes Terraform block attributes in the _AST_CACHE_SCHEMA 4 comment (they
are newly-cached facts covered by the same bump #3653 introduced).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-18 15:21:04 +01:00
himanshupatro-334 1e09e8bc82 Preserve Terraform attributes during extraction
(cherry picked from commit 6ec5de733c)
2026-09-18 15:17:06 +01:00
oleksii-tumanov 3e87d865d0 fix: resolve generic Rust self calls
(cherry picked from commit d9f00b0f55)
2026-09-18 15:16:09 +01:00
ayushcodes10 f97cf99b87 Add a regression test for the PHP JS id collision edge drop
Covers a same name PHP/JS function pair in one file: exactly one
surviving node, the JS call to the colliding name absent as a calls
edge entirely rather than misattached, and the unrelated JS symbol's
own contains edge from the shared file node still present.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit f59ce06c8c)
2026-09-18 15:15:58 +01:00
ayushcodes10 cc80351872 Add regression tests for the PHP inline script extraction
Covers the issue's exact reproduction (two functions plus the call
edge between them), that line numbers survive the masking pass, that
a script tag with only a src attribute contributes nothing without
crashing, and that a plain PHP file with no script tag is unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 2d408de529)
2026-09-18 15:15:58 +01:00
oleksii-tumanov bdb605fd8a fix: preserve Markdown links on parse failure
(cherry picked from commit 3a9cd7b8dd)
2026-09-18 15:15:57 +01:00
oleksii-tumanov e96235cb1f fix: reconcile Markdown link suffixes
(cherry picked from commit 4da224338c)
2026-09-18 15:15:57 +01:00
safishamsiandClaude Opus 4.8 4e811363a6 test(kotlin): cover companion members + incremental persistence (#1698 follow-up)
#3598 resolves Kotlin object/class-qualified member calls across files. Adds
two regression tests the PR omitted:

- a `companion object` member call (`Service.create()`), pinning the #2565
  enclosing-class attribution the resolver relies on;
- an incremental-rebuild test routed through the real watch._rebuild_code, so
  it actually exercises the context builder's allow-list carrying _callable_class
  + method edges (a hand-fed resolution_context_nodes list would bypass exactly
  the path that silently regressed in sibling PRs).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-17 11:09:52 +01:00
ayushcodes10 4afdfcbaaa Add regression tests for the cross file Kotlin object member call fix
Covers three cases: the new cross file resolution (Config.load() called
from another file resolves to object Config), a same file control to
prove the existing in file resolution is unaffected, and an ambiguous
receiver defined in two packages that must yield no edge under the
exactly one candidate guard.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit f22f17b575)
2026-09-17 11:05:12 +01:00
ayushcodes10 ffa0f529e9 Add a regression test for the god node guard on the fallback
Two different classes that both fail header parsing and happen to
share a class name and a method name must not let a call resolve to
either one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 9e26952d8b)
2026-09-17 11:03:41 +01:00
ayushcodes10 d77141ec85 Add a regression test for the qualified only fallback
The issue's own repro, an Unreal style UCLASS whose method
declaration never survives header parsing, must still get a calls
edge from a site that invokes it through the qualified name.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 3d4dec6b9a)
2026-09-17 11:03:41 +01:00
ayushcodes10 9d747f4da5 Compare partition structure, not just the node id set, for staleness
The node id set comparison missed a whole class of staleness: a merge,
a split, or a single node moving from one community to another can
leave the overall node set exactly unchanged while still describing a
different clustering, so a stale sidecar that happened to still cover
the same nodes kept winning. Compare each side's set of community
blocks instead, which catches a partition change even when the flat
node set stays identical, while still ignoring a pure id renumbering
of the same partition per the existing #1667 guard.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 30a5955931)
2026-09-17 11:01:38 +01:00
ayushcodes10 11d91c8e17 Add negative control test for a sidecar that still matches
Guards against a false positive: an up to date sidecar (the normal
case immediately after extract or the clustering command) must not
trip the new staleness warning or take the reconstruction path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 21342bda37)
2026-09-17 11:01:38 +01:00
ayushcodes10 bafe75b86b Add regression test for stale sidecar cohesion recomputation
Confirms the wiki export path specifically, since it is the one that
prints cohesion into article metadata: a stale, impossibly large
cohesion value in the sidecar must not survive into the export once
staleness is detected, proving score_all actually ran on the fresh
reconstruction rather than the fossil value merely being ignored by
coincidence.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 12c50b975a)
2026-09-17 11:01:38 +01:00
ayushcodes10 18cae252cb Add regression test for the stale sidecar html export path
Corrupts the analysis sidecar community node id set (the exact
staleness signature the issue describes) and confirms export html
still succeeds, still renders graph.html, and prints the new warning
instead of silently exporting against the fossil.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 4518bae439)
2026-09-17 11:01:38 +01:00
ayushcodes10 23d8a8798d Add an end to end regression test for the reporter's exact repro
A file that only imports a package subpath must resolve just like one
that imports the bare package, using the exact package.json plus two
file shape from the issue, instead of coming out fully disconnected
from the framework it depends on.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 5fb7fc34c1)
2026-09-17 11:01:38 +01:00
ayushcodes10 7aebd99372 Add a unit test for scoped package subpath resolution
A scoped package subpath lands on the scope plus package only, never
on a different package that happens to share the same scope.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 02183335e3)
2026-09-17 11:01:38 +01:00
ayushcodes10 3e9d22faf5 Add a unit test for plain package subpath resolution
A plain package subpath now lands on the same resolver target as its
bare import.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit b703d3126e)
2026-09-17 11:01:38 +01:00