Skip to content

CLI agent-surface truth + the manifest rail - #1571

Open
aaj3f wants to merge 8 commits into
mainfrom
feat/cli-ai-surface
Open

CLI agent-surface truth + the manifest rail#1571
aaj3f wants to merge 8 commits into
mainfrom
feat/cli-ai-surface

Conversation

@aaj3f

@aaj3f aaj3f commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Why

Fluree AI (fluree/solo) already hands its users' AI assistants off to this CLI — a generated build prompt, LLM-targeted served docs — and the embedded docs/ corpus is what agents consume as version-exact truth via fluree docs and the MCP docs toolset. This PR fixes what that corpus gets wrong, closes the CLI-side credential leak, and builds the rail that lets every downstream teacher of CLI grammar (fluree/solo's UI and docs, the upcoming fluree-cli Claude Code plugin) validate what it teaches against what actually ships.

What

Doc truth in the embedded corpus (2dc6ffb, part of 4dfc…docs/ai below)

  • All five policy examples that put the ?$this correlation clause under a nonexistent $where key — silently dropped by the parser, so every documented "restriction" policy allowed everything — rewritten to the single correlated where-array form the passing tests use, with a callout on the silent-allow failure mode and --track-policy verification.
  • --memory-budget-mb auto is documented as 80% of RAM (code truth; docs said 60%) with an explicit co-residency OOM warning.

Credential hygiene (c497bbe)

  • fluree config list redacts credentials as [redacted] by default (it dumps .fluree/config.toml, which carries live access and refresh tokens after auth login) — same redact_json_secrets policy and convention the graph-source info path already uses. --reveal is the documented raw escape hatch; config get <key> stays raw; auth token remains the scripting path. refresh_token joins SECRET_CONFIG_KEYS.

The manifest rail (438bd51, 071e0ed)

  • Hidden fluree manifest emits a machine-readable JSON of the CLI surface — command paths, flags, value enums, positionals, compiled features — generated by walking the clap tree so it cannot drift from the binary. Structure only, deliberately no help text. cargo-dist extra-artifacts publishes it as fluree-cli-manifest.json on each release for consumer CI (fluree/solo's cli-snippets test, the plugin's contract check).
  • It was born __manifest and renamed the same day: the completions_bash integration test caught that clap_complete's bash generator uses __ as its command-path separator and panics on a command with that literal name. The name is what consumers script against forever, so the collision-free name lands before anything consumes it.
  • fluree remote add reads the optional cli.min_version from /.well-known/fluree.json (served by the paired fluree/solo PR) and warns when this binary is older than what the stack's docs teach; lenient semver compare, malformed advertisements never warn. The discovery-failure fallback that silently guessed <url>/fluree now says so and names the fix.

Docs-coverage gate (221d01a)

  • tests/docs_coverage.rs walks Cli::command() and fails when a command lacks a docs/cli page, a page outlives its command, a nested action is never mentioned in its parent page, the README index skips a command, or SUMMARY.md orphans a page (orphans are still embedded — served to agents while invisible on the website). Adding a subcommand previously required zero doc changes to pass CI. The gate immediately caught real gaps on main, all fixed here: the missing fluree branch revert section, 7 of 43 commands unlinked from the README index, and load.md + server-integration.md orphaned from the book.

Agent guide (last commit)

  • docs/ai/README.md un-staled (the CLI MCP bullet omitted the entire docs toolset; the vector claim now states the HNSW index is behind the non-default vector feature while inline similarity functions are in every build).
  • New docs/ai/claude-code.md: the versioned operating guide for agents driving this CLI — probe-first doctrine, machine-output map, remote/device-flow choreography, credential hygiene, destructive-op confirmation rules, shared-machine budgets, policy correlation. It ships in the embedded corpus precisely so agent-side packaging defers to it instead of copying it.

Cross-repo pairing

  • fluree/solo#893 (feat/cli-ai-surface) is the other half: serves cli.min_version in .well-known/fluree.json, fixes the platform-side handoff defects, centralizes its UI's CLI grammar into ui/lib/cli-snippets.ts, and carries an env-gated test that validates those snippets against this PR's manifest once a release ships the asset. Either PR can land first; the handshake and the manifest check both degrade silently until both sides exist. (Post-review: the manifest now emits a root path: [] entry with --version exposed — the producer-side resolution both reviews preferred — so solo’s declared root entry validates as-is.)
  • fluree/claude-plugins: a fluree-cli Claude Code plugin (PR to follow) whose contract file validates against the same release-asset manifest and whose skill defers to this PR's docs/ai/claude-code.md.

Validation

cargo fmt clean; clippy -p fluree-db-cli --all-targets --no-deps 0 warnings; full crate suite green (165 lib + 120 integration + 5 docs-coverage); fluree-db-api redaction tests green. Honest caveat: the dist extra-artifacts hook is config-only here and gets its first real exercise on the next release CI run.

Follow-ups (deliberately not here)

  • --json for list, info, and show (the last human-only outputs an agent needs).
  • Convert plain-string --format flags to clap value enums so their choices surface in --help and the manifest.
  • A repository_dispatch from release CI to fluree/solo + fluree/claude-plugins so a breaking CLI change surfaces in consumer CI the day it ships.
  • An integration test pinning the exact cookbook policy strings (the corrected form is test-proven; the doc-string pin is polish).

aaj3f added 6 commits July 30, 2026 13:51
…t 60%)

Both live in the corpus embedded into the binary (fluree docs + the MCP
docs toolset), so agents consume them as version-exact truth:

- policy cookbook + policy-in-transactions: all five f:query examples put
  the ?$this correlation clause under a '$where' key that no parser
  reads — unknown keys are silently dropped, so every documented
  'restriction' policy matched any identity and allowed everything.
  Rewritten to the single where-array form the passing tests use
  (correlate via a shared plain variable), intermediates renamed from
  ?$user/?$mgr/?$subject to plain variables (the ?$ prefix belongs to
  the reserved/injected bindings), plus a callout on the silent-allow
  failure mode and --track-policy verification.

- --memory-budget-mb docs claimed auto = 60% of RAM; import.rs:288 is
  80%, sized for a dedicated machine. Corrected, with an explicit
  co-residency warning (live incident: an 8.6GB import at auto budget
  OOM'd a machine running normal desktop workloads).
.fluree/config.toml carries live access AND refresh tokens after auth
login, and 'fluree config list' dumped them raw — the one command
docs/cli/auth.md claimed never printed refresh tokens. list now routes
the parsed tree through the same redact_json_secrets policy graph-source
info already uses ([redacted] convention, fail-closed), on both the TOML
and JSON-LD config paths, with a stderr note pointing at --reveal.
'config get <key>' keeps raw output (explicit single-key request), and
'auth token' remains the sanctioned scripting path.

SECRET_CONFIG_KEYS gains refresh_token (the graph-source redactor never
met one, but the CLI config stores one per remote). auth token help and
docs/cli/{auth,config}.md updated to match; integration test covers
masked-by-default + --reveal.
fluree __manifest [-o FILE] emits a machine-readable manifest of this
binary's CLI surface — command paths, flags, value enums, positionals,
compiled features — generated by walking the clap tree, so it cannot
drift from what ships. Structure only, deliberately no help text (prose
belongs to --help and the embedded docs; including it would churn the
manifest on every wording edit and tempt consumers to copy reference
content instead of deferring to the binary).

cargo-dist extra-artifacts publishes it as fluree-cli-manifest.json on
each release. Consumers: fluree/solo CI (validates the centralized
ui/lib/cli-snippets.ts against it) and the fluree-cli Claude Code plugin
CI (validates its contract file). Hidden commands are excluded from the
manifest — they are not teachable surface.

Note: dist plan not exercised locally (dist not installed); the first
release CI run exercises the extra-artifacts hook. Manifest observation:
several --format flags are plain strings, so their value sets are
invisible to both --help and the manifest — converting them to clap
value enums is a follow-up.
New tests/docs_coverage.rs walks Cli::command() and fails when a
non-hidden command lacks a docs/cli page, a page outlives its command,
a nested action is never mentioned in its parent page, the README index
skips a command, or SUMMARY.md orphans a page from the published book
(the embedded corpus ships the whole folder, so an orphan is served to
agents while invisible on the website — the worst kind of split).
Previously adding a subcommand required zero doc changes to pass CI.

Fixes everything the gate caught on main: branch.md gains the missing
'fluree branch revert' section (written from the clap definition);
README.md indexes branch, multi-query, iceberg, publish, cache,
cluster, context (7 of 42 commands were unlinked); SUMMARY.md gains
cli/load.md and cli/server-integration.md.
…ifest rename

- remote add now reads the optional cli.min_version block from
  /.well-known/fluree.json (the field the paired fluree/solo PR serves)
  and warns when the running binary is older than what the stack's docs
  and UI teach. Lenient semver compare (pre-release ignored, malformed
  advertisements never warn); pure helper + unit tests.

- The discovery-failure fallback that silently guessed '<url>/fluree' as
  the API base now says so and tells the user the fix, instead of
  failing later with 404s on every request. Inputs already ending in
  /fluree are still used as-is (that path was verified correct — the
  suffixed form solo teaches is robust on both discovery paths).

- __manifest renamed to (hidden) manifest: clap_complete's bash
  generator uses '__' as its command-path separator in completion
  function names and panics on a command literally named __manifest
  (caught by the completions_bash integration test). The name is what
  consumer CI scripts against, so the collision-free name lands before
  anything consumes it; dist extra-artifacts + tests updated.
- ai/README.md: the CLI MCP bullet finally mentions the docs toolset
  (docs_search/get/examples/tree — it listed only memory tools, hiding
  the version-exact docs surface from the very audience it serves); the
  vector-search claim now states plainly that the HNSW index is behind
  the non-default 'vector' feature while the inline similarity functions
  are in every build (same doc-lie class as the $where fix: standard
  binaries cannot serve an HNSW claim).

- New ai/claude-code.md: the versioned operating guide for agents
  driving the CLI — probe-first doctrine (docs/--help/hidden manifest,
  feature-gated command disappearance), machine-output map, remote +
  device-flow choreography (the human approves /activate), credential
  hygiene (auth token vs config list --reveal, .fluree/ gitignore),
  destructive-op confirmation rules, shared-machine resource budgets,
  policy correlation + --track-policy --direct, and query hygiene
  (--at WHERE-substring rule, auto-routing vs --direct). Ships in the
  embedded corpus so agent-side packaging (the fluree-cli Claude Code
  plugin, Solo's generated build prompt) defers to it instead of
  copying it.
@aaj3f

aaj3f commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

First live use of the manifest rail, during plugin-contract authoring: the checker caught that fluree list has no --json (only --remote) — a fact I had wrong from a mis-scoped grep. So the machine-output follow-up here should read: --json for list, info, and show. The contract + skill in the plugin PR ship corrected.

…eth, release-day hardening

Response to independent review of #1571; every finding verified before
fixing (all four confirmed real).

- docs/ai/claude-code.md machine-output bullet was wrong three ways:
  'list --json' and 'branch merge --preview' don't exist, and --envelope
  is query-only (not multi-query). Rewritten from the manifest: --json
  lives on exactly graph list, branch diff, branch revert, and the four
  docs subcommands; list/info/show are human-only.

- manifest gains a root entry (path: []) with the clap-builtin --version
  exposed (requires cmd.build() — builtins only materialize then), so
  'fluree --version' is validatable and fluree/solo#893's declared root
  entry passes instead of red-lighting the day the release asset ships.
  Also: possible_values now emitted only when the arg takes values
  (SetTrue booleans no longer report a spurious true/false enum, so the
  two real value enums aren't buried under ~60 fake ones), and the
  module doc states what the manifest deliberately omits (defaults,
  num_args, relational constraints — 'required' is not the whole truth).

- docs_coverage nested-action check was mutation-toothless (bare
  substring; a prose 'revert' satisfied it). Now matches the full
  invocation path ('branch revert'); re-ran the reviewer's mutation and
  the gate fails on it, restored tree passes. Module doc no longer
  overclaims feature-portability.

- dist extra-artifacts drops --release: it runs in the global-artifacts
  job on the default 4-core/16GB runner, and this workspace's release
  profile is fat-LTO + codegen-units=1 — the config the same file
  records as OOMing at link time. Debug is fine; the manifest is pure
  introspection. Comment also pins the no-features invariant.

- auth login now runs the same cli.min_version check as remote add
  (the recurring touchpoint — a stack that raises its floor after add
  was previously never heard from again), making the solo-side CliCompat
  doc comment accurate.

- fluree-db-api gains the test tying RemoteAuth's credential fields to
  SECRET_CONFIG_KEYS across the crate boundary (refresh_token previously
  had no coverage in the crate that owns the list); features test now
  pins cluster<->server alongside validate<->shacl, plus a root-entry
  test and a no-spurious-enum test.
aaj3f added a commit to fluree/claude-plugins that referenced this pull request Jul 30, 2026
Response to independent review of #1; every finding verified before
fixing. The three text blockers were all real against v4.1.4, the
latest installable release:

- SKILL.md credential rail was INVERTED: it said config list redacts on
  'current versions' when redaction ships only in the first release
  after fluree/db#1571 — an agent on 4.1.4 would have blessed a command
  that dumps auth.token AND refresh_token in plaintext. Rewritten to be
  safe on binaries that exist: treat config list output as secret
  through v4.1.4; verify --reveal exists before assuming redaction.
- workflows.md taught 'branch diff main dev', which does not parse
  (one positional + --target, defaulting to the source's parent).
- The 'start here' pointer to docs get ai/claude-code is now hedged the
  same way the manifest pointer already was (page ships after #1571),
  and 'supersedes' narrowed to command-surface authority.

Floor moves to 4.1.4 in lockstep with the paired solo review (auth
token does not exist at 4.1.3) — SKILL, setup, README, cli-facts.

CI honesty (the job could report green while running nothing):
- 404 is the only skip; every other curl outcome is now ::error + fail.
  The skip is a ::warning with an owner and expiry (issue #2 filed).
- Marketplace root now validated (the per-plugin loop never saw it);
  claude-code npm install pinned (2.1.220); .mcp.json syntax-checked in
  CI (validate --strict provably ignores it) and its nonstandard
  description key dropped.

Checker soundness:
- exit 2 (not a stack trace masquerading as 'contract violated') for
  unreadable/malformed inputs; commands-array guard; version fallback.
- min_cli_version is now load-bearing: the checker fails when the
  manifest is older than the contract's floor. manifest_source is now
  read by CI (single source of truth for the URL).
- hidden entries assert ABSENCE from the manifest (catches a
  hidden->visible promotion of ); checked-count counts
  checked commands, not list length.

Contract covers the prose it missed: query --at/--policy-class, --json
on the four docs subcommands, bare model, branch diff --target/--json,
branch revert --json.

New: the inbound-MCP section WS-4 asked for (claude mcp add --transport
http against a Space's /v1/mcp?space=<id> — the Data Admin tab documents
Claude Desktop only); README states the two intentional doctrine-mirror
relationships and what wins where; honest failed-server description for
a missing binary; .DS_Store untracked + .gitignore added; homepage in
plugin.json; skill trigger scoped to terminal contexts + SDK-only
check in the body.

Declined with reason (recorded in the response doc): flag-VALUE
validation — mcp init's --ide/--toolsets and query --format expose no
possible_values in the manifest today (plain strings), so the mechanism
would be vacuous; riding the 'convert --format flags to value enums'
follow-up in fluree/db instead.
aaj3f added a commit to fluree/claude-plugins that referenced this pull request Jul 30, 2026
Response to independent review of #1; every finding verified before
fixing. The three text blockers were all real against v4.1.4, the
latest installable release:

- SKILL.md credential rail was INVERTED: it said config list redacts on
  'current versions' when redaction ships only in the first release
  after fluree/db#1571 — an agent on 4.1.4 would have blessed a command
  that dumps auth.token AND refresh_token in plaintext. Rewritten to be
  safe on binaries that exist: treat config list output as secret
  through v4.1.4; verify --reveal exists before assuming redaction.
- workflows.md taught 'branch diff main dev', which does not parse
  (one positional + --target, defaulting to the source's parent).
- The 'start here' pointer to docs get ai/claude-code is now hedged the
  same way the manifest pointer already was (page ships after #1571),
  and 'supersedes' narrowed to command-surface authority.

Floor moves to 4.1.4 in lockstep with the paired solo review (auth
token does not exist at 4.1.3) — SKILL, setup, README, cli-facts.

CI honesty (the job could report green while running nothing):
- 404 is the only skip; every other curl outcome is now ::error + fail.
  The skip is a ::warning with an owner and expiry (issue #2 filed).
- Marketplace root now validated (the per-plugin loop never saw it);
  claude-code npm install pinned (2.1.220); .mcp.json syntax-checked in
  CI (validate --strict provably ignores it) and its nonstandard
  description key dropped.

Checker soundness:
- exit 2 (not a stack trace masquerading as 'contract violated') for
  unreadable/malformed inputs; commands-array guard; version fallback.
- min_cli_version is now load-bearing: the checker fails when the
  manifest is older than the contract's floor. manifest_source is now
  read by CI (single source of truth for the URL).
- hidden entries assert ABSENCE from the manifest (catches a
  hidden->visible promotion of 'fluree manifest'); checked-count counts
  checked commands, not list length.

Contract covers the prose it missed: query --at/--policy-class, --json
on the four docs subcommands, bare model, branch diff --target/--json,
branch revert --json.

New: the inbound-MCP section WS-4 asked for (claude mcp add --transport
http against a Space's /v1/mcp?space=<id> — the Data Admin tab documents
Claude Desktop only); README states the two intentional doctrine-mirror
relationships and what wins where; honest failed-server description for
a missing binary; .DS_Store untracked + .gitignore added; homepage in
plugin.json; skill trigger scoped to terminal contexts + SDK-only
check in the body.

Declined with reason (recorded in the response doc): flag-VALUE
validation — mcp init's --ide/--toolsets and query --format expose no
possible_values in the manifest today (plain strings), so the mechanism
would be vacuous; riding the 'convert --format flags to value enums'
follow-up in fluree/db instead.
@aaj3f
aaj3f requested a review from bplatz July 30, 2026 21:43
aaj3f added a commit to fluree/claude-plugins that referenced this pull request Jul 30, 2026
…ned hedges (1.0.1)

Response to a fresh-session live test of v1.0.0 driving the plugin as a
real consumer; every finding re-verified against a binary before fixing.

- workflows.md insert recipe had two independent bugs: prefixed-name
  Turtle fails insert's data sniffer (the positional becomes a LEDGER
  NAME and the CLI waits on stdin — a silent hang in agent sessions),
  and 'ex:' was never declared (Turtle parse error even routed right).
  Recipe now uses @Prefix + explicit -e, with a rail note naming both
  hazards. --explain gets its required --format json; the export recipe
  gains the required 'fluree index' step.

- SKILL Rule 3's --envelope claim corrected: it pairs ONLY with
  --format ndjson (verified: 'error: --envelope only applies to
  --format ndjson'). Same bug is being fixed in fluree/db#1571's
  ai/claude-code.md.

- policy.md now carries the crucial caveat the tester proved live: the
  cookbook EMBEDDED in binaries <= 4.1.4 teaches the broken $where form
  (their A/B: cookbook policy 2/2 allowed vs corrected 1/2), and this
  page previously sent readers there without warning. Corrected
  cookbook ships in releases after fluree/db#1571.

- troubleshooting.md's unhedged 'fluree manifest' and ai/claude-code
  references now carry the same after-#1571 hedge as SKILL.md; the
  vague 'recent releases only' wording tightened; the docs-search
  fallback changed to "AI agents" (bare 'agent' ranks HTTP User-Agent
  docs first).

- setup step 2: on a floor failure, check 'which -a fluree' for a
  shadowed newer install before recommending a reinstall — the tester's
  own machine had 4.1.4 shadowed by 4.1.2 on PATH.

Version 1.0.1 in plugin.json + marketplace so installed copies see the
update.
A fresh-session live test of the fluree-cli plugin caught the agent
guide implying 'query --format json --envelope' works; the binary says
'error: --envelope only applies to --format ndjson'. Corrected here and
in the plugin's SKILL in the same pass — the second machine-output
claim in this page that only running the binary caught.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant