docs: Campaign LA — pinned launch-contract schema COMMITTED into plan LA1
The LA3 Opus review process note was right: the contract both sides implement lived only in orchestrator prompts, which is exactly the drift mode the pin exists to prevent (and it produced the paths-key CRITICAL). The schema, field rules, probe-mode discriminator, and status vocabulary are now a binding plan section; amendments change this text first, implementations second. Ledger: LA3 fix round dispatched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
0bcc7ba3a3
commit
db9ad53c1c
38 changed files with 2397 additions and 40 deletions
65
src/AcDream.App/Credentials/AppCredentialSecret.cs
Normal file
65
src/AcDream.App/Credentials/AppCredentialSecret.cs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
using System.Security.Cryptography;
|
||||
|
||||
namespace AcDream.App.Credentials;
|
||||
|
||||
/// <summary>
|
||||
/// Campaign LA slice LA1: retains a resolved <c>--session-config</c>
|
||||
/// credential in erasable memory — the App-side mirror of
|
||||
/// <c>AcDream.Headless.Credentials.HeadlessCredentialSecret</c> (that type is
|
||||
/// internal to the Headless project, so this is a minimal, independent port
|
||||
/// rather than a shared reference). The network boundary still requires one
|
||||
/// short-lived immutable string; callers must not retain that value beyond
|
||||
/// constructing the connect request.
|
||||
/// </summary>
|
||||
internal sealed class AppCredentialSecret : IDisposable
|
||||
{
|
||||
private char[]? _buffer;
|
||||
|
||||
internal AppCredentialSecret(string referenceId, ReadOnlySpan<char> value)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(referenceId);
|
||||
if (value.IsEmpty)
|
||||
{
|
||||
throw new AppCredentialException(
|
||||
$"Credential '{referenceId}' resolved to an empty secret.");
|
||||
}
|
||||
|
||||
ReferenceId = referenceId;
|
||||
_buffer = value.ToArray();
|
||||
}
|
||||
|
||||
internal string ReferenceId { get; }
|
||||
internal bool IsDisposed => _buffer is null;
|
||||
|
||||
internal string Reveal()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_buffer is null, this);
|
||||
return new string(_buffer);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
char[]? buffer = Interlocked.Exchange(ref _buffer, null);
|
||||
if (buffer is null)
|
||||
return;
|
||||
CryptographicOperations.ZeroMemory(
|
||||
System.Runtime.InteropServices.MemoryMarshal.AsBytes(
|
||||
buffer.AsSpan()));
|
||||
}
|
||||
|
||||
public override string ToString() =>
|
||||
$"[redacted:{ReferenceId}]";
|
||||
}
|
||||
|
||||
internal sealed class AppCredentialException : Exception
|
||||
{
|
||||
internal AppCredentialException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
internal AppCredentialException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue