Skip to content

feat(repl): repository commands %repo, %projects, %load and %publish over the SysML v2 API - #1005

Merged
HuiJun merged 9 commits into
developfrom
feat/repl-repository-commands
Oct 9, 2026
Merged

HuiJun merged 9 commits into
developfrom
feat/repl-repository-commands

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

What and why

The sysml REPL and the Jupyter kernel gain the four repository commands of the OMG pilot's Jupyter kernel, with its grammar:

%repo [<BASE PATH>]
%projects
%load [--id=<PROJECT ID>] [--name=<NAME>] [--branch=<BRANCH NAME or ID>] [<NAME>]
%publish [-d] [--project=<PROJECT NAME>] [--branch=<BRANCH NAME>] <NAME>

They work against any server speaking the standard SysML v2 API (/projects, /branches, /commits with change payloads, /elements, /roots) — the pilot's SysML-v2-API-Services, which wants no token — and against Flexo MMS, whose extras (bearer token, organization, Layer 1 Turtle, ETag pushes) stay optional.

  • Transport. The standard API client is now internal/translate/interop/sysmlapi (projects, branches, commits, elements, roots, optional bearer token, typed StatusError/UnreachableError); internal/translate/interop/flexo wraps it and keeps Layer 1, orgs and ETag semantics. Paging follows a server's Link: rel="next" to the end even when a page is short, which the pilot server does. sysml -sync is unchanged and its tests pass as they were.
  • Shared sync. internal/translate/interop/modelsync holds what sysml -sync and the REPL share: baseline/apply over reposync.Diff/reposync.Apply, provenance scope (ProjectRef), the rooted cut of a model, notation writing, and the derived-property filter. There is no second sync implementation.
  • REPL. %repo shows or sets the session's base URL (seeded from FLEXO_SYSMLV2_URL, plain-HTTP guard honoured, token read only from FLEXO_INTEROP_TOKEN and never printed or persisted). %projects lists <name> (<id>) over every page. %load resolves the project by name/--id/--name and the branch by name or id (default branch when absent), reads the head's elements, writes them as notation and submits them as a document, recording project/branch/commit so a later %publish without --branch commits on that branch; a name two projects share is refused naming both ids. A lone argument that is a path on disk still loads files. %publish cuts the elements rooted in a session-resolved qualified name, creates the project when none has that name, otherwise commits the diff against the branch head, reporting the commit id and created/updated/deleted counts; -d sends every derived property the exporter computes, without it the ones the reader recomputes are left out. The REPL reaches the transport through a replext.Repository extension and does not import cmd/sysml.
  • Errors. Syntax and argument problems are *repl.UsageError, which the kernel now answers as UsageError for every meta command; server failures carry the HTTP status and the server's message.
  • Docs. "Working with a repository" in the Jupyter guide (environment for the pilot server and for Flexo), the kernel reference, the REPL command reference, %help, and a changelog fragment.

How it was verified

  • go build ./..., go vet ./..., gofmt -l . (empty).
  • go test ./internal/translate/interop/... ./internal/frontend/... ./cmd/sysml/... — pass. New tests: an httptest fake of the standard API (projects, branches, commits with change, elements, roots, Link paging, 404, 409) driving all four commands through Session.RunMeta, including a %publish → %load round trip into a fresh session that compares the models, a %load --branch=<non-default> followed by a %publish that must commit on that branch, usage errors, completion of flags and project names, %help; a Jupyter engine test over %repo/%projects/UsageError/CommandError; modelsync tests pinning the derived-property set against every example model.
  • make test-short — pass apart from TestFormattingDiffIsCheaperThanFormatting (an LSP timing test unrelated to this change) under the full-suite load; it passes with -count=3 on its own.
  • make man-check, make docs-check, python3 scripts/changelog.py check — pass. tests/hygiene layering passes with the new packages.
  • Live Flexo MMS stack (Fuseki + Layer 1 + SysML v2 API from the flexo-mms-sysmlv2 compose files), driven through the built bin/sysml: %repo show/set, the plain-HTTP guard, %projects empty and populated, %publish creating a project (9 created) then committing an edit (2 created, 1 updated, no second project), --project/--branch to another project and branch, -d confirmed by API readback (owningClassifier present only after a derived publish, a repeat is a no-op), %load by name, --id, --name and --branch into fresh sessions with the notation preserved, publish after load committing on the loaded branch (default and non-default), clean errors for a missing project, missing branch, unresolved root and unreachable server, %help and Tab completion of flags and live project names; sysml -sync diff/apply then no-op against the same stack. No token appeared in any transcript.
  • The opt-in TestFlexoInterop expectation gate was run against that stack and fails three comparisons (74 → 76 subjects, 240 → 268 identity triples, sysx:sourceLine/sourceColumn/sourceEndLine/sourceEndColumn now among the uncarried properties); it fails identically on origin/develop, so the drift predates this branch and the expectation is left for its own change.
  • Not verified against the OMG pilot SysML-v2-API-Services server.

Checklist

  • make test and make lint pass locally
  • Tests added or updated for the change
  • Documentation extended where it already covers the surface (see CONTRIBUTING.md)
  • Changelog entry added as changes/unreleased/<slug>.<section>.md, not as an edit to CHANGELOG.md
  • baselines regenerated and make docs-counts run if a gate count moved (compliance rows need nothing: the census is counted at docs build)
  • No internal work-item labels (waves, slices, F4, K5) in the body, docs, or changelog

Link to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/5c4ff33730224c458b08de0632279da1
Open in Devin Desktop: https://nasa-jpl-demo.devinenterprise.com/desktop/session/5c4ff33730224c458b08de0632279da1?variant=devin
Requested by: @HuiJun

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration
devin-ai-integration Bot marked this pull request as ready for review October 8, 2026 16:19
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@devin-ai-integration
devin-ai-integration Bot force-pushed the feat/repl-repository-commands branch from cbbf0b1 to cecbfc6 Compare October 9, 2026 01:32
@devin-ai-integration
devin-ai-integration Bot changed the base branch from develop to feat/load-notebook October 9, 2026 01:32
@devin-ai-integration
devin-ai-integration Bot added this pull request to stack #1014 October 9, 2026 01:32
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Rebased onto feat/load-notebook (#1004) so the two can merge in sequence. Besides the textual merges in meta.go's command table and docs/reference/repl-commands.md, the rebase had to reconcile the two %load extensions, which both parse -- options:

  • loadsRepository now routes a %load to the repository only on a repository option (--id, --name, --branch) or a lone name that names no path. Before, any -- option was taken as a project load, so with the repository linked (as the sysml binary has it) %load --cells 1,3 nb.ipynb would have been refused as "--cells is not an option of %load".
  • namesPath counts an .ipynb extension as a path, like .sysml/.kerml, so a notebook that is missing is reported as an unreadable file rather than a project that doesn't exist.
  • An unknown option of the file form is now a *UsageError (as the repository form's already was), whose lines carry usageLoadPath, the repository usage when one is linked, and the "unknown option" message; usageLoadPath is the notebook-aware usage line, and meta.go prints it from the constant.
  • The prefixed helper added here duplicated the one complete.go gained from feat(repl): draw named elements on demand with the pilot kernel's %viz #1000; the copy here is dropped.

TestLoadWithCellsReadsANotebookNotAProject covers the first two points with the repository linked; TestLoadRefusesWhatItCannotRead reads every line of a usage error for its "unknown option" case.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@HuiJun
HuiJun force-pushed the feat/repl-repository-commands branch 3 times, most recently from 2d9bace to 2d57f48 Compare October 9, 2026 03:53
Base automatically changed from feat/load-notebook to develop October 9, 2026 10:53
devin-ai-integration Bot and others added 9 commits October 9, 2026 03:53
…over the SysML v2 API

Split the standard SysML v2 API client (projects, branches, commits with
change payloads, elements, roots, Link-paged collections, optional bearer
token) into internal/translate/interop/sysmlapi; flexo keeps Layer 1
Turtle, organizations and ETag pushes over it. modelsync holds what
sysml -sync and the REPL share: rooted cuts, provenance scope, notation,
derived-property filtering and diff/apply over reposync.

The REPL and the Jupyter kernel gain the OMG pilot kernel's four
repository commands through a replext repository extension, with usage
errors as *repl.UsageError, completion of flags and project names, and
an httptest fake of the standard API exercising all four and a
publish/load round trip.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
… absent

A session that loaded a project at a branch other than the default
published its edits to the default branch; the loaded branch is now the
one a publish without --branch addresses.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ge links, refuse repeated options and plaintext redirects

%publish deletes only under the root it publishes, only on a branch the
session loaded or published from the same server, and reports what it
left in place; a repository state carries the server it came from. A
failed first commit names the project it created. Elements paging
follows a Link rel=next as given instead of rebuilding it as a cursor.
A %load or %publish option given twice is a usage error. A redirect
with a bearer token is held to the plaintext rule of the first request.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
… refuse a repeated -d and redirects off the server's host

confine looks the root up among the branch's subjects by effective id,
so a branch that qualifies its ids still has removed descendants
deleted. -d given twice is a usage error like any repeated option. With
a bearer token, a redirect to another host is refused as well as one to
plaintext.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…port

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…k included

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…s --cells beside the repository options

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…to %load

Co-Authored-By: jason.han <hanhuijun@gmail.com>
@HuiJun
HuiJun force-pushed the feat/repl-repository-commands branch from 2d57f48 to 8427187 Compare October 9, 2026 10:53
@HuiJun
HuiJun merged commit 5bf4555 into develop Oct 9, 2026
1 check passed
@HuiJun
HuiJun deleted the feat/repl-repository-commands branch October 9, 2026 10:54
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