Bug - Fixed "svn update" does not move files in local copy

Some ideas:
1) maybe rebasing updated files should only be peformed for files that are directly in scripts/ - so svn scripts/aaa.ash could be matched to scripts/aaa.ash, scripts/tools/aaa.ash, scripts/scriptname/whatever/aaa.ash; but scripts/name/aaa.ash could only be matched to scripts/name/aaa.ash
- this would at least make sure that rebasing doesn't overwrite any files that wouldn't be overwritten anyway

2) or if you are feeling fancy, match not just the basename, but entire subpath under scripts/ - so svn scrips/a/b/c.ash could be matched to scripts/xxx/a/b/c.ash, but not scripts/a/xxx/b/c.ash

3) or maybe scripts from svn should just each be installed into its own subdirectory of scripts/ (something like svn/x/scripts/name.ash -> scripts/x/name.ash)
- installing/updating would do no rebasing
- clashes would be immediately obvious: the duplicates would be script names (two scripts registered under the same name in script registry)
- does not, on its own, help against multiple scripts using the same basenames
- importing and calling should probably be able to find scripts/x/lib/liblele.ash when script/cli references x/lib/liblele.ash
-- this might be easier for imports; give priority to files belonging to the same script: when scripts/x/main.ash imports lib/liblele.ash, it probably means x/lib/liblele.ash rather than someothery/lib/liblele.ash
- I like the idea of scripts declaring their entry points, which could be exempt (and installed directly under scripts/whatever/the/script/declared.ash)

4) if we want to keep the simple ability to reference scripts by base name (for imports and calling from cli), maybe just refuse to install any script that has duplicate (base) filename with any other script in svn/ ?

5) don't forget about local-only scripts (not installed from svn); how do you publish those to script menu if you require entry points to be declared from svn?
 
The ability to nuke someone else's script has existed since KoLmafia started supporting scripts. If that is a real problem then we solve it for people who fear it by making script updates smarter and more interactive. That was once on the table but it annoyed people at the cost of defending against something that was not happening. The warning that a new file is being installed is a legacy of that.

This is probably an area where the line between "user convenience" and "fixing stupid" gets drawn differently between us :)

If I had to fix the scripts menu I would start by noting that everything in it (and the MRU list) can be explicitly mapped to a fully qualified file on disk. That is easy for scripts selected by filesystem navigation. For other scripts mafia eventually has to access a disk file so a hook to that point would be needed. Key thing is that there was a user selection made that either required no disambiguation or accepted a proposed disambiguation. Remembering what was accepted removes a need for future disambiguation. The list of scripts would probably become a list of fully qualified path names and the the menu display would be derived from the pathname and hopefully unique when reduced to something human friendly.

In almost every development environment where one file can reference another the potential for name collisions exists. I won't bore you with stories but it happens. There are are only two solutions I recall that actually worked. One was to lock down the execution environment in such away that the entity performing the lockdown could guarantee a priori where any reference would be mapped to. The other was for the author to fully qualify the name of any external reference. So there is precedent for requiring script writers to take responsibility for knowing where things will be found while they are writing. Doing so won't solve all of the issues and concerns mentioned but it would certainly change the conversation and shape the feasibility of solutions.
 
While I still prefer nuking this "smart lookup" altogether, I wouldn't mind xKiv's proposal #1 as a compromise. Though my examples involved scripts/, I am more concerned with relay/. Trial and error have taught us to avoid polluting the scripts/ directory. scripts/ is special; don't touch it. When in doubt, create a subdirectory under relay/ and chuck your utility scripts in there. After all, that's where all the CSS and JS files live. That's your script's "safe space".

(Except that there is no safe space, since KoLmafia can overwrite ASH, CSS, JS, and any other files during install.)

If clutter is a concern with Script Menu, how about keeping a list of files that should be always hidden from the script menu? We could store the list in a text file such as "settings/hidden.txt". Or "hidden.json". It seems like a simpler approach than trying to build what is essentially a "Bookmark" feature.
 
When in doubt, create a subdirectory under relay/ and chuck your utility scripts in there. After all, that's where all the CSS and JS files live. That's your script's "safe space".
That may be your belief but it was never intended as such. The intent was that only an ash script that overrode a KoL page (or was launched from the Relay Browser Script menu) was to be in /relay. Supporting scripts were expected to still be in /scripts because that was where KoLmafia expected to look for them. Anything else in /relay was supposed to be under KoLmafia's control entirely.

Your safe space was supposed to be script/philssafespace where you only had to worry about other scripters and not KoLmafia.

Clearly in 2021 with JavaScript supported as a scripting language that needs to be revisited but perhaps the solution is not comingling things KoLmafia needs, and should be able to change unilaterally, with things a script needs and expects to be there.

There is something to be said for a container or sandbox model. Everything a script needs is in one place with a boundary around it and nothing crosses the boundary while the script is running. This makes shared dependencies harder to manage and can lead to duplicate files on disk but it does mean the scripter (who presumably defines the contents of the container) is in total control.

If you write something for me in JavaScript that I want to run locally, how do you package things? What do you give me (in terms of files) and what do I do with them to run you application? What do you do differently if your application does not actually need internet connectivity so I disable my connection and require you to provide me with what I need since you can't fetch it from the internet?

What if KoLmafia introduced /enhancedscripts? A brand new script was installed by creating a subdirectory under it. KoLmafia would traverse the subdirectories in /enhancedscripts. A file beginning with relay_ would be added to the relay browser script dropdown. A catalog or naming convention could be used to indicate what was added to a scripts menu. KolMafia would never try and disambiguate by looking in /enhancedscripts and mafia operations that installed scripts in /engancedscripts would enforce uniqueness on subdirectiry names.
 
@fronobulax Fair enough. I admit that throwing ASH scripts inside relay/ is a hacky way of discouraging users from trying to run or import them directly.

That said, I would still like my HTML, CSS, and JS files to be protected by being placed inside a uniquely named subdirectory. I don't want them to be overwritten just because they have a common name (e.g. styles.css).

I created a PoC project that will destroy all of your scripts just by installing it 🔥 Check out (or checkout) https://github.com/pastelmind/poc-agentsmith
 
I think I narrowed the root causes of the "failed to rename" problem described in the first post.

I say "causes" because it is actually two problems in one:
  1. svn update overwrites existing files.
  2. svn update does not delete non-empty directories.
Here is my testing process:
  1. I created a GitHub repository X with two files:
    Code:
    relay/
      foo/
        a.txt
        b.txt
    In KoLmafia, I installed this repo with svn checkout <URL to X>.

  2. I moved relay/foo/a.txt to relay/bar/a.txt:
    Code:
    relay/
      foo/
        a.txt
      bar/
        b.txt
    I then went back to KoLmafia and ran svn update.

    Result: In the local copy, relay/foo/a.txt was "moved" to relay/bar/a.txt.
    Code:
    Updating all SVN projects...
    Validating repo...
    Repo validated.
    Updating pastelmind-svn-update-repro-branches-partial-move-test...
    C:\Users\Phil\Documents\KoL\svn\pastelmind-svn-update-repro-branches-partial-move-test
    A         https://github.com/pastelmind/svn-update-repro/branches/partial-move-test/relay/bar
    A         https://github.com/pastelmind/svn-update-repro/branches/partial-move-test/relay/bar/a.txt
    D         C:\Users\Phil\Documents\KoL\svn\pastelmind-svn-update-repro-branches-partial-move-test\relay\foo\a.txt
    https://github.com/pastelmind/svn-update-repro/branches/partial-move-test/relay/foo
    https://github.com/pastelmind/svn-update-repro/branches/partial-move-test/relay
    https://github.com/pastelmind/svn-update-repro/branches/partial-move-test
    At revision 25
    Pushing local updates...
    relay\foo\a.txt => DELETED
    a.txt => C:\Users\Phil\Documents\KoL\relay\bar\a.txt
    Done.

  3. I moved relay/foo/b.txt to relay/bar/b.txt:
    Code:
    relay/
      bar/
        a.txt
        b.txt
    I then went back to KoLmafia and ran svn update.

    Result: In the local copy, nothing changed.
    Code:
    Updating all SVN projects...
    Validating repo...
    Repo validated.
    Updating pastelmind-svn-update-repro-branches-partial-move-test...
    C:\Users\Phil\Documents\KoL\svn\pastelmind-svn-update-repro-branches-partial-move-test
    A         https://github.com/pastelmind/svn-update-repro/branches/partial-move-test/relay/bar/b.txt
    https://github.com/pastelmind/svn-update-repro/branches/partial-move-test/relay/bar
    D         C:\Users\Phil\Documents\KoL\svn\pastelmind-svn-update-repro-branches-partial-move-test\relay\foo
    https://github.com/pastelmind/svn-update-repro/branches/partial-move-test/relay
    https://github.com/pastelmind/svn-update-repro/branches/partial-move-test
    At revision 26
    Pushing local updates...
    b.txt => C:\Users\Phil\Documents\KoL\relay\foo\b.txt
    Done.

So what is going on here:
  1. When relay/foo/b.txt is moved to relay/bar/b.txt, KoLmafia processes it as "create relay/bar/b.txt" + "delete directory relay/foo/"
  2. "create relay/bar/b.txt" actually overwrites the original file in relay/foo/b.txt. We already know this.
  3. "delete directory relay/foo/" fails...but why?

The answer can be traced to doDelete() in SVNManager.java...
Java:
	private static void doDelete( File file, String relpath )
	{
		File rebase = getRebase( relpath );

		if ( rebase == null )
			return;

		if ( rebase.exists() )
		{
			String rerebase = FileUtilities.getRelativePath( KoLConstants.ROOT_LOCATION , rebase );
			if ( rebase.delete() )
			{
				RequestLogger.printLine(rerebase + " => DELETED");
				RequestLogger.updateSessionLog(rerebase + " => DELETED");
			}
		}
	}

I stepped through each line and verified that everything works up to rebase.delete(), which fails because the directory is not empty.




My original goal was to move all files from relay/foo/ to relay/bar/ in a clean way.
I don't know how to fix it within KoLmafia.

I can think of a clumsy workaround, though:

  1. Move all files from relay/foo/ to relay/bar/.
    Rename all moved files to prevent KoLmafia from overwriting existing files.
  2. Create a dummy file relay/bar/UNUSED_DIRECTORY_PLEASE_DELETE.txt
  3. Push 1 + 2 as a single commit. This forces KoLmafia to:
    • Create new files properly in relay/bar/
    • Delete old files in relay/foo/, but not delete relay/bar/ itself because UNUSED_DIRECTORY_PLEASE_DELETE.txt is still in there.
  4. Wait until I am sure that everyone has received the updates.
  5. Delete relay/bar/ and push the commit.
    KoLmafia will attempt to delete relay/bar/ (which fails), but users can manually delete it themselves.
 
And then we iterate over the stack in what looks like LIFO order (which is great! stacks are supposed to have problems with that). OTOH, it looks like this sequence needs to be executed in FIFO order. Delete dir foo, add dir qux, add file s-d-s-.ash...

Incidentally, I think I know why we are using LIFO (stack) instead of FIFO (queue). It's a hack.

When scripts/foo/a.txt is moved to scripts/bar/a.txt, SVN interprets it as a "create scripts/bar/a.txt" + "delete scripts/foo/a.txt", in that order.

If we used FIFO, KoLmafia would do this:
  1. Create new file scripts/bar/a.txt. But KoLmafia matches it with scripts/foo/a.txt and overwrites that instead.
  2. Delete scripts/foo/a.txt
...with the catastrophic result of deleting the file entirely.

Hence, we use LIFO to work around this:
  1. Delete scripts/foo/a.txt
  2. Create new file scripts/bar/a.txt. Since scripts/foo/a.txt no longer exists, KoLmafia creates the file in its intended location.
...and what I discovered is an edge case (directory deletion) that cannot be handled by this workaround.
 
Here's a patch that disables the "smart lookup" (i.e. rebasing) for relay/. The reasoning being that relay scripts generally expect a strict directory layout, and users will never move around files in there.
(For example, if your relay script uses relay/my-dir/styles.css, you can't move it to relay/your-dir/styles.css and expect the relay script to keep working.)

The smart lookup still works for scripts/, which allows users to move around files in there. This satisfies @xKiv's use case (organizing the Script Menu) until we find a better solution.
 

Attachments

  • svn update overwrites existing files.
  • svn update does not delete non-empty directories.

SVN is working as designed.

In a pure, no Git, no KoLmafia, SVN environment, the update command is expected to overwrite files of the same name and in the same location. Similarly the update command does not delete an empty directory unless there had been a previous commit that explicitly deleted the directory.

The copy from svn to /scripts (or whatever) added by KoLmafia to the update is documented as overwriting existing files and users who wish to preserve local changes (in /scripts) have been told to edit the file in the svn location and then be aware of the possibility that SVN's attempt to merge changes might not give the desired or hoped for results.

So...

If you create a subdirectory under /scripts and dump all of your files in there, and then write your scripts so they only use files there and from no other location, can you write and maintain the script you want? Are there KoLmafia changes that would help, for example, building the relay scripts menu from other directories in addition to /scripts?

There is a lot of concern about scripts overwriting other scripts. Could that be addressed by enforcing uniqueness of directory names in svn and subdirectories in /scripts that are created by svn? Perhaps a safe checkout and update mode that would not overwrite something and would tell the user?

Also remember that KoLmafia cannot prevent, and perhaps not even detect, changes that a user makes without using KoLmafia.
 
If you create a subdirectory under /scripts and dump all of your files in there, and then write your scripts so they only use files there and from no other location, can you write and maintain the script you want?

Sure. As long as I can continue to import ZLib and other libraries.

Are there KoLmafia changes that would help, for example, building the relay scripts menu from other directories in addition to /scripts?

I am probably underqualified to talk since I rarely use the Script Menu these days. I just type in the name of the script to call it.
But a Bookmark system--similar to what web browsers provide these days--would provide enough flexibility for most cases I can think of, while being reasonably familiar to everyone.

There is a lot of concern about scripts overwriting other scripts. Could that be addressed by enforcing uniqueness of directory names in svn and subdirectories in /scripts that are created by svn? Perhaps a safe checkout and update mode that would not overwrite something and would tell the user?

Fundamentally, I am against enforcing the use of unique file names. Directories help us avoid fighting over file names and they should.

A "safe mode" (or "practical mode") would be great. Suppose file /foo/X from project A is about to overwrite file /bar/X from B. As of today, KoLmafia warns the user, and when the user clicks "No", skips creating a local copy of /foo/X. But the rest of project A is installed anyway in a broken state.

Perhaps we could simply refuse to install A. Perhaps we could create /foo/X where it aught to be. Perhaps we could let the user choose. Any solution would be better for UX than creating a broken install.
 
There is a lot of concern about scripts overwriting other scripts. Could that be addressed by enforcing uniqueness of directory names in svn and subdirectories in /scripts that are created by svn? Perhaps a safe checkout and update mode that would not overwrite something and would tell the user?

Also remember that KoLmafia cannot prevent, and perhaps not even detect, changes that a user makes without using KoLmafia
How would we enforce uniqueness of names? At an install level? What would we do with projB if it attempted to install 99 unique names and utils.ASH, which projB also used? Do we break ProjA or do we leave a non-atomic ProjB, or do we roll back projB?
 
First the unique name enforcement was a possibility for a problem I don't think is especially likely and could be fixed with user education. But other people seemed to be bothered by it so I started thinking out loud. Maybe I misunderstood the real concern? But I was thinking of an association between the svn name (for example svn\kolmafiascripts-mafiarecovery) and any subdirectory of scripts created when the project was checked out or updated. Mafia could only allow the creating project to modify the contents of the created subdirectory. That would effectively make subdirectories of scripts to have unique names across projects. Using fully qualified path names would mean scripts\foo\phil.ash and scripts\bar\phil.ash would be different. At the top level in scripts the file system is already doing a lot of uniqueness enforcement.

But anything mafia does to mange and deconflict filenames across projects is going to be fragile or flat out broken if the user uses something besides mafia to install and update scripts or if the user has scripts that are not installed from a public repository.

I'm not convinced there is a real problem that requires code to change for a solution. I might consider a noSVNClobber preference. If it is set then any KoLmafia SVN operation that tries to create or write a file when the file already exists would generate an Are You Sure? type prompt and abort on No. Implementation could be tricky because the ideal implementation would roll back the SVN operation if any of the collisions generated a No response..
 
I think "find and use a script" might be a separate use case from "update a script".

For me, the principles I want to enforce in code are:
  1. The user should not be able to damage the installation of a package by installing an unrelated package with no common dependencies.
  2. The developer should not be able to damage the installation of their package by using normal, successful SVN operations like renaming a folder.
We fail both those now and don't really tell the user they're happening. Maybe part of it could be clearer if we didn't have a bunch of tags-as-text inside a warning dialog, making it hard to read, but I can't even think of any way to word Warning: Project B is updating utils.js, do you want to continue?" that a user like me, who typed "svn update Project B" would understand would affect Project A.

I have heard "I want to re-arrange my code after install", but I don't feel that I understand why. If it's to resolve the code-finding problem, I want that code re-arranged in the server. If it's to resolve the menu is built from the file structure, I want to fix that (pm+'s bookmarks, which are really "a list of items and targets"). If it's something else, I'd like to know the utility of it. If it's self-modifying code, I have concerns.
 
We have conflated several topics here and it is my opinion that there is no real bug. There are, however, several feature requests that could be considered.

The user should not be able to damage the installation of a package by installing an unrelated package with no common dependencies.

This needs to be fleshed out. Does Kolmafia tell the script author how to name things or does mafia detect collisions upon install? Do we protect against someone who renames files in an installed package and thus causes problems with other packages? How do we deal with files that are created using some other method besides KoLmafia's SVN package? I touched on some of this upthread talking about uniqueness but I am not sure how to implement this without imposing restrictions on users and script packagers.

The developer should not be able to damage the installation of their package by using normal, successful SVN operations like renaming a folder

I believe that the alleged damage was due to operator misunderstanding. Specifically the SVN move and rename commands do not always do what a naïve user expects. The behavior is different based upon whether a directory is empty and previously committed. We may have to distinguish between script authors who use KoLmafia's SVN commands, script writers who use another tool, and users who are only reading a repository. As a user, if all I do is read a repository and update trashes my install the committer may have some responsibility.
 
I believe that the alleged damage was due to operator misunderstanding. Specifically the SVN move and rename commands do not always do what a naïve user expects. The behavior is different based upon whether a directory is empty and previously committed. We may have to distinguish between script authors who use KoLmafia's SVN commands, script writers who use another tool, and users who are only reading a repository. As a user, if all I do is read a repository and update trashes my install the committer may have some responsibility.

I have no control over how GitHub's subversion compatibility layer translates a directory rename operation into a "delete directory" operation. The truth is I've been struggling for a workaround for the last two weeks without luck.

I want to make svn update rename a subdirectory on the local copy, when said subdirectory is renamed for a project hosted on GitHub. If this can be solved without changes on KoLmafia's end, then I'm all ears.

And since we're talking feature requests, I would like to request official recognition and support of GitHub as a distribution platform for KoLmafia script projects.
 
I have no control over how GitHub's subversion compatibility layer translates a directory rename operation into a "delete directory" operation. The truth is I've been struggling for a workaround for the last two weeks without luck.
Not only don't you, you must not have any control over it, since you don't know how far back in the repo the user is updating from. Any rename is a delete + add, per the red-bean book.
Code:
$ svn move -q my-project renamed-project
$ svn commit -m "Rename my-project to renamed-project."
Deleting       my-project
Adding         renamed-project

Committed revision 12.

Note that in the ROOT/svn directory, all this works perfectly. It only gets odd when we add our code to copy it. This is unrelated to SVN. GIT would do the same thing, which is to say perfectly happily copy and update files tied to the repo and then fail to handle the transfer to the scripts directory's semi-flat namespace by finding and acting on a file in the old location instead of honoring the request.


As far as Git is concerned, a copy and delete is the same thing as a move. Git will record both (copy + delete) and (move) the same way. --depp on StackOverflow --emphasis mine

I want to make svn update rename a subdirectory on the local copy, when said subdirectory is renamed for a project hosted on GitHub. If this can be solved without changes on KoLmafia's end, then I'm all ears.

Since it's our issue, I don't expect we can.

I think the option to not change KoLmafia's behavior is basically "tough luck, you can't change your project structure without also renaming your files", which I dislike.

And since we're talking feature requests, I would like to request official recognition and support of GitHub as a distribution platform for KoLmafia script projects.
Integrate JGit and we can do that. For our user's use cases, we just need the supporting pure java toolchain.
 
Last edited:
Thank you for describing the crux in a way that I never could. And yes, the issue was with the local copy, which is managed directly by KoLmafia rather than Subversion. Whenever I moved a directory or renamed a file in the GitHub repo, svn update faithfully reflected these changes in the working copy. The local copy was always the issue.

Also, I'm actually pretty satisfied with how well GitHub's subversion compatibility layer works with KoLmafia. First-class Git support would be awesome, but is not on the top of my wish list.
 
I'm "in a mood". It is not a bug. It is a feature doing exactly what it was designed to do. The fact that the design should be reconsidered because of the way script authors want to deploy JavaScript does not make it a bug. Words are important as anyone who has used "annoy" to describe a KoLmafia behavior should have learned.

Moving the discussion here. I hope we can work things out despite our misunderstandings.

This is not a JavaScript problem. I can build two relay scripts written in ASH that would suffer from the file name collision problem--not just ASH files, but also CSS or font files with the same name.

Similarly the update command does not delete an empty directory unless there had been a previous commit that explicitly deleted the directory.

When a commit deletes an entire (non-empty) directory, svn update (1) deletes the same (non-empty) directory in the working copy, (2) but does not delete the (non-empty) directory in the local copy. This is a problem, because any files left over in there can interfere with future updates due to "smart lookup" that we've been talking about. I hope that can be changed as well.
 
KoLmafia uses SVN commands to check out and update a local repository. KoLmafia then uses KoLmafia code to make changes to the portion of the local file system used by KoLmafia. These changes are driven by what is in the local SVN repository. The original issue could be refined to state that the local file system does not reflect the local SVN repository in cases where a directory is renamed.

Had I realized that was what was going on, I would have responded differently, because this is a clearly a KoLmafia problem and not a SVN problem.

The issue becomes detection. At present KoLmafia knows that /scripts/foo needs to have files placed in it and that it might need to be created. But it does not know that /scripts/bar can/should be deleted. Indeed, by design, KoLmafia is only working on a static snapshot of the repository and is not accessing any of the steps that changed it since the last update. Furthermore, there is no guarantee that /scripts/bar was not used by some other script or is being used by a user doing something that is not derived from any SVN repository.

Under what circumstances is it safe for KoLmafia to delete a file or directory in response to SVN activity?

The conservative answer is never. There is no guarantee that a file or directory was created in response to SVN activity as opposed to a user doing their own thing and accidently or deliberately sharing names with a script.

The SVN Update is useless if it does not overwrite the local copy of the file being updated. If a user or other scripter happens to use the same name, then there is a potential problem that has been previously addressed by "Don't do that".

The name conflict needs some thought. KoLmafia cannot control script writers, so the most feasible solution is to give them a mechanic which, IF THEY CHOOSE TO FOLLOW, their files will neither clobber or be clobbered by any other SVN installed script. We cannot protect a script from a user with a command line and a text editor, nor is it reasonable to expect us to try.

My initial thoughts for the mechanic is that if a script agrees to stick its files in /scripts/mineminemine then KoLmafia will as a minimum let the user know that another script is trying to use the same directory. This may require some other changes in mafia, probably concerning running relay scripts from somewhere besides /relay. Deconflicting two relay scripts that want to override the same page may need to be a KoLmafia service and not an ad hoc convention observed by the script writers who have agreed to follow it.

When SVN was added to KoLmafia there were already user expectations concerning /scripts and /relay. The decision was made to mirror files in local repositories so that expectations and conventions would continue to be met. If someone were motivated enough to make an integrated view of the file system that included /scripts and all of the SVN repositories that could eliminate the copy step. That might even pave the way for local git repositories as well. But first tell me, as a dumb user, how to find the script fil I want to edit for a personal and local change ;-)

And since we're talking feature requests, I would like to request official recognition and support of GitHub as a distribution platform for KoLmafia script projects.

I would say that is already there. Mafia will install and update from SourceForge or GitHub. You need to tell us what you think is missing.
 
When SVN was added to KoLmafia there were already user expectations concerning /scripts and /relay. The decision was made to mirror files in local repositories so that expectations and conventions would continue to be met. If someone were motivated enough to make an integrated view of the file system that included /scripts and all of the SVN repositories that could eliminate the copy step. That might even pave the way for local git repositories as well. But first tell me, as a dumb user, how to find the script fil I want to edit for a personal and local change ;-)
I think this is a great idea. We could add some sort of "svn unpack" command that would copy out your scripts from your svn folder to /scripts and /relay folders. Then at least the user can get the functionality we have now, but they could choose to do so at their own risk. I'd be happy to write this up, sounds like a great move
 
Back
Top